THREE.js-用曲线(X / Z轴)射击球

时间:2018-09-02 18:23:03

标签: javascript three.js 3d physics

我是THREE.js的新手,对物理学的了解很差,但是我仍然想制作足球经理的游戏(从顶视图看),我需要知道球的踢脚尽可能真实

当球碰到边界时,我能够使球朝正确的方向移动和旋转,同时改变运动的位置。

现在我需要处理球的弯曲问题,以及如何使球弯曲,从而使球根据脚的角度弧形移动到顶部和侧面(X / Y)击球

我只想说,我需要知道如何处理两种情况:

  • 1)从球的底轴附近开始踢球
  • 2)从球的右轴开始踢球

您的帮助非常合适。谢谢!

** -我添加了代码以显示到目前为止的内容 -我添加了一张说明我的目标(或此人得分目标)的图像

enter image description here

            /*
            *
            * SET UP MOTION PARAMS
            * 
            */

            var boundries = [40, 24] //indicate where the ball needs to move in mirror position
            var completeFieldDistance = boundries[0] * 2;
            var fullPower = 1.8; //the power needed to move the ball the enitre field in one kick
            var power = null; //will be set when the kick set in depending on the distance

            var isKickStop = false; //indicate the renderer weather to stop the kick
            var velocityX = null;
            var velocityY = null;

            //*** this is where i need help! *** 
            //how can I make the ball move in the Z axis with a nice curv up depending on a given angle
            var curv = 15;
            var peak = curv;
            var velocityZ = 0;

            var friction = 0.98;
            var gravity = 0.5;
            var bounciness = 0.8;
            var minVelocity = 0.035; //for when it need to stop the kick rendering
            var ballRadius = 3;
            var ballCircumference = Math.PI * ballRadius * 2;
            var ballVelocity = new THREE.Vector3();
            var ballRotationAxis = new THREE.Vector3(0, 1, 0);

            //world meshes
            var ball = {};
            var field = {};

            /*
            *
            * THE KICK HANDLERS
            * 
            */

            function onKick(angleDeg, distance) {
                isKickStop = true;
                peak = curv; 
                power = (distance / completeFieldDistance) * fullPower;
                velocityX = Math.cos(angleDeg) * power;
                velocityY = Math.sin(angleDeg) * power;
                velocityZ = peak / (distance / 2);

                requestAnimationFrame(function (params) {
                    isKickStop = false;
                    animateKick();
                })
            }

            //** THIS IS WHERE I NEED HELP - how do I make the ball move 
            // render the movements of the ball
            var animateKick = function (params) {
                if (isKickStop) { return; }

                ball.position.x += velocityX;
                ball.position.z += velocityZ;
                ball.position.y += velocityY;

                if (Math.abs(velocityX) < minVelocity && Math.abs(velocityY) < minVelocity) {
                    ball.position.z = ball.bottom;
                    isKickStop = true;
                    console.log("DONE!");
                    return;
                }

                if (ball.position.z >= peak) {
                    ball.position.z = peak;
                    velocityZ *= -1;
                }

                if (ball.position.z < ball.bottom) {
                    peak *= gravity;
                    velocityZ *= -1;
                    ball.position.z = ball.bottom;
                }

                // Figure out the rotation based on the velocity and radius of the ball...
                ballVelocity.set(velocityX, velocityY, 0);
                ballRotationAxis.set(0, 0, 1).cross(ballVelocity).normalize();
                var velocityMag = ballVelocity.length();
                var rotationAmount = velocityMag * (Math.PI * 2) / ballCircumference;
                ball.rotateOnWorldAxis(ballRotationAxis, rotationAmount);

                //reduce velocity due to friction
                velocityX *= friction;
                velocityY *= friction;

                //making sure ball is not outside of its boundries
                if (Math.abs(ball.position.x) > boundries[0]) {
                    velocityX *= -1;
                    ball.position.x = (ball.position.x < 0) ? boundries[0] * -1 : boundries[0];
                }

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

            window.onload = (function (params) {
                /*
                *
                * SET UP THE WORLD
                * 
                */                                     
                //set up the ratio
                var gWidth = window.innerWidth;
                var gHeight = window.innerHeight;
                var ratio = gWidth / gHeight;

                //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.castShadow = true;         
                light.position.set(0, 0, 35);
                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(ballRadius, 8, 8);
                //make a checkerboard texture for the ball...
                var canv = document.createElement('canvas')
                canv.width = canv.height = 256;
                var ctx = canv.getContext('2d')
                ctx.fillStyle = 'white';
                ctx.fillRect(0, 0, 256, 256);
                ctx.fillStyle = 'black';

                for (var y = 0; y < 16; y++)
                    for (var x = 0; x < 16; x++)
                        if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
                var ballTex = new THREE.Texture(canv);
                ballTex.needsUpdate = true;

                var material = new THREE.MeshLambertMaterial({
                    map: ballTex
                });
                ball = new THREE.Mesh(geometry, material);

                ball.castShadow = true;
                ball.receiveShadow = false;
                ball.bottom = ballRadius / 2;
                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);
                field = new THREE.Mesh(geometry, material);
                field.receiveShadow = true;
                field.position.z = -1;
                scene.add(field);
                /*
                *
                * HANDLING EVENTS
                * 
                */
                var domEvents = new THREEx.DomEvents(camera, renderer.domElement);
                domEvents.addEventListener(field, 'click', function (e) {
                    //set points 1 and 2
                    var p1 = { x: e.intersect.point.x, y: e.intersect.point.y };
                    var p2 = { x: ball.position.x, y: ball.position.y };
                    var angleDeg = Math.atan2(p1.y - p2.y, p1.x - p2.x);
                    var a = p1.x - p2.x;
                    var b = p1.y - p2.y;
                    var distance = Math.sqrt(a * a + b * b);
                    window.onKick(angleDeg, distance);
                }, false);  
                /*
                *
                * ANIMATION STEP
                * 
                */

                var render = function (params) {
                    //render kick if it is on the go 
                    if(!isKickStop){
                        animateKick();
                    }

                    //render the page
                    renderer.render(scene, camera);
                    requestAnimationFrame(render);
                }
                render();
            })()
body {
    padding: 0;
    margin: 0;
}
<html>
<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
    <script src="https://www.klika.co.il/scripts/three.events.js"></script>
</head>
<body>
</body>
</html>

1 个答案:

答案 0 :(得分:1)

我建立了一个模型对此进行模拟,该模型接受几个参数,即初始速度和角速度,在球上有三个力,重力,空气阻力和马格努斯力。

v0_x = 0; //initial velocity
v0_y = 4;
v0_z = 1;
w_x = 0 * Math.PI; // initial angular velocity
w_y = 2 * Math.PI;
w_z = 0 * Math.PI;
m = 2; //weight
rho = 1.2; // air density
g = 9.8; // gravity 
f = 10; //frequency of the rotation of the ball
cl = 1.23; //horizontal tension coefficient
cd = 0.5; //air resistance coefficient
D = 0.22; // diameter of the ball
A = Math.PI * Math.pow((0.5 * D), 2); //cross-sectional area of the ball
t_step = 1 / 60;
b = (1 / 2) * cd * rho * A; //for convenience
c = cl * rho * Math.pow(D, 3) * f; // for convenience
vt_x = v0_x
vt_y = v0_y
vt_z = v0_z

animateKick = function() {
  if (ball.position.y < 0) {
    return;
  }
  tmp_1 = c * Math.pow(Math.pow(vt_x, 2) + Math.pow(vt_z, 2) + Math.pow(vt_y, 2), 2)

  tmp_2 = (Math.sqrt(Math.pow(w_z * vt_y - w_y * vt_z, 2) + Math.pow(w_y * vt_x - w_x * vt_y, 2) + Math.pow(w_x * vt_z - w_z * vt_x, 2)))

  tmp = tmp_1 / tmp_2

  Fl_x = tmp * (w_z * vt_y - w_y * vt_z)

  Fl_z = tmp * (w_y * vt_x - w_x * vt_y)

  Fl_y = tmp * (w_x * vt_z - w_z * vt_y)

  //Motion differential equation
  a_x = -(b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_x + (Fl_x / m)
  a_z = -(b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_z + (Fl_z / m)
  a_y = -g - (b / m) * Math.sqrt((Math.pow(vt_z, 2) + Math.pow(vt_y, 2) + Math.pow(vt_x, 2))) * vt_y + (Fl_y / m)

  //use formula : s_t = s_0 + v_0 * t to update the position
  ball.position.x = ball.position.x + vt_x * t_step
  ball.position.z = ball.position.z + vt_z * t_step
  ball.position.y = ball.position.y + vt_y * t_step

  //use formula : v_t = a * t to update the velocity 
  vt_x = vt_x + a_x * t_step
  vt_z = vt_z + a_z * t_step
  vt_y = vt_y + a_y * t_step

}

window.onload = (function() {
  gWidth = window.innerWidth;
  gHeight = window.innerHeight;
  ratio = gWidth / gHeight;

  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xeaeaea);
  camera = new THREE.PerspectiveCamera(35, ratio, 0.1, 1000);
  camera.position.z = -15;
  light = new THREE.SpotLight(0xffffff, 1);
  light.castShadow = true;
  light.position.set(0, 5, -10);
  scene.add(light);

  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);

  geometry = new THREE.SphereGeometry(D, 8, 8);
  //make a checkerboard texture for the ball...
  canv = document.createElement('canvas')
  canv.width = canv.height = 256;
  ctx = canv.getContext('2d')
  ctx.fillStyle = 'white';
  ctx.fillRect(0, 0, 256, 256);
  ctx.fillStyle = 'black';

  for (y = 0; y < 16; y++)
    for (x = 0; x < 16; x++)
      if ((x & 1) != (y & 1)) ctx.fillRect(x * 16, y * 16, 16, 16);
  ballTex = new THREE.Texture(canv);
  ballTex.needsUpdate = true;

  material = new THREE.MeshLambertMaterial({
    map: ballTex
  });
  ball = new THREE.Mesh(geometry, material);

  ball.castShadow = true;
  ball.receiveShadow = false;

  ball.bottom = D / 2;
  scene.add(ball);
  camera.lookAt(ball.position);

  plane_geometry = new THREE.PlaneGeometry(20, 100, 32);
  plane_material = new THREE.MeshBasicMaterial({
    color: 'green',
    side: THREE.DoubleSide
  });
  ground_plane = new THREE.Mesh(plane_geometry, plane_material);
  ground_plane.rotation.x = 0.5 * Math.PI
  ground_plane.position.y = -1
  ground_plane.position.z = 20
  scene.add(ground_plane);

  render = function(params) {
    animateKick();
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  };
  render();
})
body {
  padding: 0;
  margin: 0;
}
<html>

  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/96/three.min.js"></script>
    <script src="https://www.klika.co.il/scripts/three.events.js"></script>
  </head>

  <body>
  </body>

</html>