Animating a spherical object in 3D space using THREE.js

As a newcomer to THREE.js with limited physics knowledge, I am in the process of developing a top-view football game engine. However, I am currently facing challenges related to the ball's movement.

When attempting to move the ball from side to side, its rotation always faces one direction, and I am struggling to make it rotate in the direction it is moving towards.

I have included a simple code snippet showcasing this issue. Any assistance you can provide would be greatly appreciated.

     /*
            *
            * SET UP MOTION PARAMS
            * 
            */

            var degrees = 10;
            var power = 1;
            var angleRad = degrees * Math.PI / 120;

            var velocityX = Math.cos(angleRad) * power;
            var velocityY = Math.sin(angleRad) * power;
            var velocityZ = 1;

            var friction = 1;
            var gravity = 0.2;
            var bounciness = 0.9;



            window.onload = function (params) {

                /*
                *
                * SET UP THE WORLD
                * 
                */
                
                

                //set up the ratio
                var gWidth = window.innerWidth;
                var gHeight = window.innerHeight;
                var ratio = gWidth / gHeight;
                var borders = [40, 24] //indicate where the ball needs to move in mirror position


                //set the scene
                scene = new THREE.Scene();
                scene.background = new THREE.Color(0xeaeaea);

                //set the camera
                var camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
                camera.position.z = 120;   

                //set the light
                var light = new THREE.SpotLight(0xffffff, 1);
                light.position.set(100, 1, 0); 
                light.castShadow = true;         
                light.position.set(0, 0, 100);
                scene.add(light);

                //  set the renderer 
                var renderer = new THREE.WebGLRenderer();

                //properties for casting shadow
                renderer.shadowMap.enabled = true;
                renderer.shadowMap.type = THREE.PCFSoftShadowMap; 

                renderer.setSize(gWidth, gHeight);
                document.body.appendChild(renderer.domElement);




                
                /*
                *
                * ADD MESH TO SCENE
                * 
                */


                // create and add the ball
                var geometry = new THREE.SphereGeometry(5, 5, 5);
                var material = new THREE.MeshLambertMaterial({ color: 'gray' });
                var ball = new THREE.Mesh(geometry, material);

                ball.castShadow = true;
                ball.receiveShadow = false;
                scene.add(ball);


                
                // create and add the field
                var margin = 20;
                var fieldRatio = 105 / 68;

                var width = 90;
                var height = width / fieldRatio;

                var material = new THREE.MeshLambertMaterial({ color: 'green' });
                var geometry = new THREE.BoxGeometry(width, height, 1);
                var field = new THREE.Mesh(geometry, material);

                field.receiveShadow = true;
                field.position.z = -1;
                scene.add(field);





                /*
                * setting up rotation axis 
                */


                var rotation_matrix = null;

                var setQuaternions = function () {
                    setMatrix();
                    ball.rotation.set(Math.PI / 2, Math.PI / 4, Math.PI / 4); // Set initial rotation
                    ball.matrix.makeRotationFromEuler(ball.rotation); // Apply rotation to the object's matrix
                }

                var setMatrix = function () {
                    rotation_matrix = new THREE.Matrix4().makeRotationZ(angleRad); // Animated rotation will be in .01 radians along object's X axis
                }

                setQuaternions();


                /*
                *
                * ANIMATION STEP
                * 
                */

                var render = function (params) {

                    // add velocity to ball
                    ball.position.x += velocityX;
                    ball.position.z += velocityZ;
                    ball.position.y += velocityY;


                    //validate if ball is stop moving
                    if (Math.abs(velocityX) < 0.02 && Math.abs(velocityY) < 0.02) {
                        console.log("DONE!");
                        return;
                    }



                    // handle boucing effect
                    if (ball.position.z < 1) {
                        velocityZ *= -bounciness;
                        ball.position.z = 1
                    }


                    // Update the object's rotation & apply it
                    ball.matrix.multiply(rotation_matrix);
                    ball.rotation.setFromRotationMatrix(ball.matrix);


                    //reducing speed by friction
                    angleRad *= friction;
                    velocityX *= friction;
                    velocityY *= friction;
                    velocityZ *= friction;


                    //set up the matrix 
                    setMatrix();
                    


                    //validate ball is withing its borders otherwise go in the mirror direction
                    if (Math.abs(ball.position.x) > borders[0]) {
                        velocityX *= -1;
                        ball.position.x = (ball.position.x < 0) ? borders[0] * -1 : borders[0];
                    }

                    if (Math.abs(ball.position.y) > borders[1]) {
                        velocityY *= -1;
                        ball.position.y = (ball.position.y < 0) ? borders[1] * -1 : borders[1];
                    }


                    // reduce ball height with gravity
                    velocityZ -= gravity;

                    

                    //render the page
                    renderer.render(scene, camera);

                    requestAnimationFrame(render);
                }

                render();

            }
body {
    padding: 0;
    margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>

<head>

</head>

<body>
</body>

</html>

Answer №1

Delving into the realm of advanced physics, achieving a realistic simulation with friction and inertia is quite challenging. However, there are shortcuts you can take to create a visually appealing rolling effect...

To obtain a perpendicular vector in the movement direction of the ball, simply perform a .cross product operation between the movement vector and the world up vector.

This resulting vector represents the axis around which a ball would rotate if it experienced full friction with the ground. By utilizing .rotateOnWorldAxis (axis: Vector3, angle: Float) with the object, you can implement this rotation...

The next step involves determining the amount of rotation based on factors such as the ball's radius and the distance traveled. Calculate this by multiplying the length (referred to as magnitude in the code below) of the movement vector by (PI*2) / the circumference of the ball.

Feel free to reach out if you find this explanation helpful...

p.s - A small correction was made to your "angleRad" computation; it had been dividing by 120 instead of 180, which has now been rectified.

Answer №2

To address your current scenario, consider implementing a rotation to the ball that revolves around its "right axis" within its local space.

For simplifying the mathematical calculations involved, THREE.js offers several handy methods, such as the `makeRotationAxis()` method found in the `THREE.Matrix4` class.

In essence, fine-tuning your ball.rotation calculations slightly should help you achieve the desired outcome. You can refer to the code snippet below for an example of how this can be achieved (or view it in action on this jsFiddle demo):

   /*
            *
            * SET UP MOTION PARAMS
            * 
            */
            
            var rotationAngle = 0;

            var degrees = 10;
            var power = 1;
            var angleRad = degrees * Math.PI / 120;

            var velocityX = Math.cos(angleRad) * power;
            var velocityY = Math.sin(angleRad) * power;
            var velocityZ = 1;

            var friction = 1;
            var gravity = 0.2;
            var bounciness = 0.9;



            window.onload = function (params) {

                // Additional setup for the environment

                // Code continues...
            }
body {
    padding: 0;
    margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
<html>

<head>

</head>

<body>
</body>

</html>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to customize the hover effect for react-bootstrap navbar link

I'm looking to update the hover effect color from white to green, but I'm having trouble finding the correct CSS code for it. <Navbar fixed='top' collapseOnSelect expand="md" variant="dark" className="animate ...

Looking for Sha1 encryption functionality in jQuery or JavaScript?

I am currently using sha1 encryption coding in PHP <?php echo hash('sha1', 'testing'.'abcdb'); ?> However, I need this encryption to work on a page named xyz.html, causing the current block of code to not function prop ...

Navigate through the JSON object and generate all possible combinations of nested elements

Here is a JSON object that I need to iterate through in order to generate all possible nested group names: { "groups": [ { "group": "group1", "childrens": [ { "group": "group1_1", ...

Encountered issues while installing a package with npm instead of yarn

I have established a Git repository that will serve as an NPM package in another project. Let's refer to this sharable repository as genesis-service-broker. Within one of my services (specifically the activation service), I am utilizing this shareabl ...

Display symbols based on zoom level using the ArcGIS JavaScript API

I am currently working on creating a map using the ArcGIS JS API. I have added numerous symbols to the graphic layer, but they appear to be too dense and are overlapping with each other. Is there a way for me to display fewer points on the map when zoomed ...

Displaying React components using Bootstrap in the navigation bar

I'm feeling a little stuck here. I'm currently working with React Bootstrap and the Navigation component. One of the routes in my application is the dashboard, which is only accessible after logging in. When a user navigates to the dashboard, I ...

Using the power of jQuery to create straightforward CSS animations

On my simple webpage, I have a main content area called content-wrapper and a sidebar on the right side named #sidebar-right I am attempting to create a margin on the right side of the content to prevent it from overlapping the sidebar, and have it expand ...

Tips for synchronizing the indexes of two arrays using a common value in JavaScript

Is there a way to sort one array to match the order of another array with identical content, based on a shared property? For instance: let firstArray = [{id: 123, ...}, {id: 456, ...}, {id: 789, ...}] // always in this order let secondArray = [{id: 456, . ...

I'm having difficulty saving a key-value pair in a log file

const FileHasher = require("./FileHasher"); if (process.argv.length < 3) { console.log("Please provide a program file and one argument."); process.exit(1); } const filename = process.argv[2]; const fs = require('fs'); const file = fs.rea ...

React - How to Close Material-UI Drawer from a Nested Menu

Currently, I am utilizing a fantastic example (here) to create a dynamic nested menu for my application. Taking it a step further, I am trying to integrate it into a Material UI appbar/temporary drawer. My goal is to close the drawer when the user clicks o ...

Opacity control with Dat.GUI

When attempting to adjust the transparency model stl using dat.gui, I am invoking the following command: gui.add(opacity_model, 'model').onChange(function(){ if (opacity_model, 'model' == true) { m ...

The charts created using chartjs-vue display no data

After following some examples to create a test chart, I am facing an issue where the chart renders blank with dummy data. https://i.sstatic.net/RD77S.png My initial suspicion is that maybe the options are not being passed for the lines, but it seems like ...

Having trouble creating an angularjs table using ng-repeat in the controller?

I have a sample snippet that I would like to discuss. My goal is to display a JSON object in an angular table using ng-repeat, which is being generated from my angular controller script. I have attempted the code below, but for some reason, the table is no ...

Exploring the world of web development with a mix of

var articles = [ {% for article in article_list %} {% if not forloop.first %},{% endif %} { title: "{{ article.title }}", slug: "{{ article.slug }}", content: "{{ article.content }}", auth ...

What is the best way to manage horizontal scrolling using buttons?

I was hoping that when the button is clicked, the scroll would move in the direction of the click while holding down the button. Initially, it worked flawlessly, but suddenly it stopped functioning. export default function initCarousel() { const carous ...

Using Javascript, Google Charts is able to interpret JSON data

I am currently working on integrating Google Charts and populating it with data from an external JSON file that I have generated using PHP's json_encode() function. After some effort, I managed to get Google Charts to display data successfully, but f ...

Selecting multiple categories using jQuery

HTML: <ul id="selector"> <li><a href="#" class="group1" rel="group1">group 1</a></li> <li><a href="#" class="group2" rel="group2">group 2</a></li> <li><a href="#" class="group3" re ...

Exploring Highcharts Pie Chart with AJAX for Real-time Data Updates

Looking for some guidance with implementing AJAX in my code to make my chart dynamic based on data from the database. Although the code is error-free, the chart is not refreshing automatically. Any help, comments, or suggestions would be greatly appreciate ...

The operation is unable to be executed in an external document

Working on a WordPress page, I am utilizing the Google Maps API. The functions in my file are as follows: function custom_map_style() { // Enqueue Google Maps API wp_enqueue_script('maps', 'https://maps.googleapis.com/maps/api/js? ...

Choosing comparable choices from the drop-down menu

I am working on a dropdown menu that contains different options. I need to use jQuery to automatically select the option that corresponds to the branch of the currently logged in user. However, when some option text is very similar, it causes an issue. // ...