Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
178 views
in Technique[技术] by (71.8m points)

javascript - Ammo.js Three.js - Calculate force vector needed to make a basketball shot

I’m building a basketball game with three.js and ammo.js. (Enable3d)

The hoop/rim and ball’s positions are constantly changing (AR) so the shots will have to be dynamic and relative.

I need to calculate the force vector to apply to the ball, for a successful shot. In the game play the user will swipe to shoot and this will effect the “perfect shot” force.

I’ve seen many examples of code and equations for calculations of trajectories, ballistics, etc, and I’ve been converting them to JavaScript from C# as a lot of the scripts I’m finding are on the Unity forums.

I need a function that calculates the initial force vector3 To apply to the ball using the position of the ball, position of the hoop, the ball’s mass, and gravity. The initial angle or max height (terminal velocity y) will also have to be passed to this function, as I’ve noticed in all the equations and calculators I’ve seen.

EDIT:

So I converted a script I found on the Unity forums (Below), to Javascript/Three.js:

function getBallVelocity( ballPos, rimPos, angleDegrees, gravity ) {
  
  const Vector3 = {
    forward: new THREE.Vector3( 0, 0, 1 ),
    up: new THREE.Vector3( 0, 1, 0 )
  };
  
  // Get angle in radians, from angleDegrees argument
  const angle = THREE.Math.degToRad( angleDegrees );
  
  gravity = gravity || 9.81;

  // Positions of this object and the target on the same plane
  const planarRimPos  = new THREE.Vector3( rimPos.x, 0, rimPos.z ),
        planarBallPos = new THREE.Vector3( ballPos.x, 0, ballPos.z );

  // Planar distance between objects
  const distance = planarRimPos.distanceTo( planarBallPos );
  
  // Distance along the y axis between objects
  const yOffset = rimPos.y - ballPos.y;
  
  // Calculate velocity 
  const initialVelocity = ( 1 / Math.cos( angle ) ) * Math.sqrt( ( 0.5 * gravity * Math.pow( distance, 2 ) ) / ( distance * Math.tan( angle ) + yOffset ) ),
        velocity = new THREE.Quaternion( 0, initialVelocity * Math.sin( angle ), initialVelocity * Math.cos( angle ) );

  // Rotate our velocity to match the direction between the two objects
  const planarPosDifferenceBetweenObjects = planarRimPos.sub( planarBallPos ),
        angleBetweenObjects = Vector3.forward.angleTo( planarPosDifferenceBetweenObjects ),
        angleUpRotated = new THREE.Quaternion().setFromAxisAngle( Vector3.up, angleBetweenObjects ),
        finalVelocity = angleUpRotated.multiply( velocity );
  
  return finalVelocity;
}

I'm calling the shot like this:

const velocity = getBallVelocity( ball.position, rim.position, 45 );
ball.body.applyForce( velocity.x, velocity.y, velocity.z )

It's shoots the wrong direction and very weak. I assume I'm not doing the rotation correctly at the end of the function, and the weakness could be due to not having mass multiplied. The ball's mass it 2, so I assume I should be multiplying some Y values by 2?? Have no idea :(

Here is the C# script I attempted a conversion of:

using UnityEngine;
using System.Collections;
 
public class ProjectileFire : MonoBehaviour {
 
    [SerializeField]
    Transform target;
 
    [SerializeField]
    float initialAngle;
 
    void Start () {
        var rigid = GetComponent<Rigidbody>();
 
        Vector3 p = target.position;
 
        float gravity = Physics.gravity.magnitude;
        // Selected angle in radians
        float angle = initialAngle * Mathf.Deg2Rad;
 
        // Positions of this object and the target on the same plane
        Vector3 planarTarget = new Vector3(p.x, 0, p.z);
        Vector3 planarPostion = new Vector3(transform.position.x, 0, transform.position.z);
 
        // Planar distance between objects
        float distance = Vector3.Distance(planarTarget, planarPostion);
        // Distance along the y axis between objects
        float yOffset = transform.position.y - p.y;
 
        float initialVelocity = (1 / Mathf.Cos(angle)) * Mathf.Sqrt((0.5f * gravity * Mathf.Pow(distance, 2)) / (distance * Mathf.Tan(angle) + yOffset));
 
        Vector3 velocity = new Vector3(0, initialVelocity * Mathf.Sin(angle), initialVelocity * Mathf.Cos(angle));
 
        // Rotate our velocity to match the direction between the two objects
        float angleBetweenObjects = Vector3.Angle(Vector3.forward, planarTarget - planarPostion);
        Vector3 finalVelocity = Quaternion.AngleAxis(angleBetweenObjects, Vector3.up) * velocity;
 
        // Fire!
        rigid.velocity = finalVelocity;
 
        // Alternative way:
        // rigid.AddForce(finalVelocity * rigid.mass, ForceMode.Impulse);
    }
}

Thanks!

question from:https://stackoverflow.com/questions/65713249/ammo-js-three-js-calculate-force-vector-needed-to-make-a-basketball-shot

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

OK, three things:

  1. The rotation of the velocity wasn't working. Used atan2 instead.
  2. I needed to switch the subtracting Y axes points getting the yOffset
  3. I needed to use .setVelocity(x, y, z) instead of .applyForce(x, y, z)

Here is the final script, hope it helps someone!

static getBallVelocity( ballPos, rimPos, angleDegrees, gravity ){
 
  // Get angle in radians, from angleDegrees argument
  const angle = THREE.Math.degToRad( angleDegrees );
 
  gravity = gravity || 9.81;
 
  // Positions of this object and the target on the same plane
  const planarRimPos  = new THREE.Vector3( rimPos.x, 0, rimPos.z ),
        planarBallPos = new THREE.Vector3( ballPos.x, 0, ballPos.z );
 
  // Planar distance between objects
  const distance = planarRimPos.distanceTo( planarBallPos );
 
  // Distance along the y axis between objects
  const yOffset = ballPos.y - rimPos.y;
 
  // Calculate velocity
  const initialVelocity = ( 1 / Math.cos( angle ) ) * Math.sqrt( ( 0.5 * gravity * Math.pow( distance, 2 ) ) / ( distance * Math.tan( angle ) + yOffset ) ),
        velocity = new THREE.Vector3( 0, initialVelocity * Math.sin( angle ), initialVelocity * Math.cos( angle ) );
   
  // Rotate our velocity to match the direction between the two objects
  const dy = planarRimPos.x - planarBallPos.x,
        dx = planarRimPos.z - planarBallPos.z,
        theta = Math.atan2( dy, dx ),
        finalVelocity = velocity.applyAxisAngle( PopAShotAR.Vector3.up, theta )
 
  return finalVelocity;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...