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!
OK, three things:
The rotation of the velocity wasn't working. Used atan2 instead.
I needed to switch the subtracting Y axes points getting the yOffset
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;
}
I've setup a scene with a camera which remains in a fixed point in the center of the scene. The user can pan the camera around by clicking (and holding) and dragging the left mouse button. When the user releases the left mouse button, the motion of the camera stops. My scene is based on one of the Three.js demos as see here
There are a bunch of event handlers which are used to create a target position for the camera to lookAt:
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
}
}
and in my update loop I have:
// lat and long have been calculated in the event handlers from the cursor click location
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = (500 * Math.sin( phi ) * Math.cos( theta ));
camera.target.y = (500 * Math.cos( phi ));
camera.target.z = (500 * Math.sin( phi ) * Math.sin( theta ));
camera.lookAt( camera.target );
Which works as expected. When the user clicks and drags the mouse, the camera rotates to follow. Now I am attempting to add some inertia to the movement so when the user releases the left mouse button the camera continues to rotate in the direction of rotation for a small amount of time.
I have tried tracking the change in position of the mouse position in my drag start and drag end events and try to calculate the direction of movement based on that, but it seems like a convoluted way of doing it
Any suggestions on how I could add inertia to the camera controls?
I've been struggling for a while with the concepts of quaternions and I think that it may have something to do with this particular challenge.
If you know three.js, you may be familiar with the equirectangular panorama video example. What I've been trying to do is to simply extract the camera's rotation at any given moment in a format that I understand (radians, degrees...) for each axis. In theory, shouldn't I be able to simply tap into the camera's rotation.x/y/z parameters to find those out? I'm getting strange values, though.
Check out this example:
http://www.spotted-triforce.com/other_files/three/test.html
I'm outputting the camera's xyz rotation values at the upper left corner and instead of expected values, the numbers bounce around between positive and negative values.
Now, this example doesn't use one of the control scripts that are available. Rather, it creates a vector to to calculate a camera target to look at. Here's what the camera code looks like:
function init() {
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 1100 );
camera.target = new THREE.Vector3( 0, 0, 0 );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
isUserInteracting = true;
onPointerDownPointerX = event.clientX;
onPointerDownPointerY = event.clientY;
onPointerDownLon = lon;
onPointerDownLat = lat;
}
function onDocumentMouseMove( event ) {
if ( isUserInteracting === true ) {
lon = ( onPointerDownPointerX - event.clientX ) * 0.1 + onPointerDownLon;
lat = ( event.clientY - onPointerDownPointerY ) * 0.1 + onPointerDownLat;
}
}
function onDocumentMouseUp( event ) {
isUserInteracting = false;
}
function update() {
lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );
camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );
camera.lookAt( camera.target );
}
Does anybody know what these strange values are that I'm getting and how I can extract proper rotation values that I can then use on another object to mirror the motion?
If you set
camera.rotation.order = "YXZ"
( the default is "XYZ" ) the Euler angles will make a lot more sense to you:
rotation.y will be the camera heading in radians
rotation.x will be the camera pitch in radians
rotation.z will be the camera roll in radians
The rotations will be applied in that order.
three.js r.70
I am trying to rotate earth about it's tilted axis in three js. I found this solution, and I am trying to use it on my code, but it doesn't do anything.
When I execute this code the planet just sits there and doesn't rotate at all. I don't really have a firm grasp of what a quaternion is or how it works, so I am not sure what is going wrong.
function rotateAroundAxis(object, axis, radians) {
var vector = axis.normalize();
var quaternion = new THREE.Quaternion().setFromAxisAngle(vector, radians);
object.rotation = new THREE.Euler().setFromQuaternion( quaternion );
}
earthAxis = new THREE.Vector3(Math.cos(23.4*Math.PI/180), Math.sin(23.4*Math.PI/180), 0);
function render() {
stats.update();
step += 0.02;
rotateAroundAxis(earth, earthAxis, step);
}
First, you need to tilt your sphere's geometry by 23.4 degrees by applying a transformation to it.
var radians = 23.4 * Math.PI / 180; // tilt in radians
mesh.geometry.applyMatrix( new THREE.Matrix4().makeRotationZ( - radians ) );
Then, to rotate your earth on its axis in object space, first normalize the axis you are rotating around.
earthAxis = new THREE.Vector3( Math.sin( radians ), Math.cos( radians ), 0 ).normalize();
Then in your render function, do this:
earth.rotateOnAxis( earthAxis, 0.01 ); // axis must be normalized
three.js r.69
I'm trying to reproduce a solar system like in Three.js, but I don't manage to make planet rotate in an inclined way around the star :
Here is a fiddle sample but with the wrong rotation :
http://goo.gl/MzcwI
I tried this solution :
How to rotate a 3D object on axis three.js? without success.
If someone have a clue to help me,
Thanks
I had to add an element at the root position of the pivot, it creates a sort of new plan that you can rotate.
Solution here.
jsfiddle
Have you tried?
function animate() {
pivot.rotation.y += 0.01;
pivot.rotation.x += 0.01;
requestAnimationFrame(animate);
render();
}
If you want to rotate just using the position vectors of the mesh you can simply do something like
theta = 0;/* Global variable */
function render(){
orbiter = /* Get the planet you want to rotate*/;
orbiter.mesh.position.x = Math.cos( theta ) *100;
orbiter.mesh.position.z = Math.sin( theta ) *25;
orbiter.mesh.position.y = Math.cos( theta ) * -60;
theta +=.02;
renderer.render( scene, camera );
}
function animate(){ animation_id = requestAnimationFrame( animate ); render(); }
animate();
Your going to have to play around with the values to get the desired rotation angles - especially for the position.y value. Increasing theta should increase the speed.