Calculating ball trajectory in full 3d world

###Hello

We have quite common problem but we couldn’t figure out any working solution any that fits (all of those bits of code we found on unity forums doesn’t tell us much because we are not programmers). If that’s possible we prefer code in javascript.

##Our situation:

  1. Players character is grenadier in full 3d world (he can jump and walk on different levels of high)
  2. Players sees that character as third person from isometric-like point of view (similar to most crpg games like Diablo)
  3. In any time player can throw grenade to any place within range by clicking mouse button on the ground
  4. We know how to find coordinates of player and that click on the ground

#Our problem:

  1. We don’t know how to calculate the trajectory of thrown grenade in 3d world. Grenade should start from characters position and ends on the clicked ground (right now its misses badly).
  2. We would like to have control over speed, angle and distance in which player can throw grenade.
    This doesn’t have to be 100% precise , we prefer something that works and works really fast.

###Image for better understanding our problem:

841-trajectory.jpg

Those two clips can show you what we searching for:

http://www.vimeo.com/8900520 – this one have something we are looking for

http://www.youtube.com/watch?v=kNabvLyUXzI#t=0m32s - here is “feel” of throw we are looking for

##Thanks SO much!

Actually there’s a very simple way using basic physics formulas, with no errors whatsoever regardless of the elevation:

private Vector3 calculateBestThrowSpeed(Vector3 origin, Vector3 target, float timeToTarget) {
	// calculate vectors
	Vector3 toTarget = target - origin;
	Vector3 toTargetXZ = toTarget;
	toTargetXZ.y = 0;
	
	// calculate xz and y
	float y = toTarget.y;
	float xz = toTargetXZ.magnitude;
	
	// calculate starting speeds for xz and y. Physics forumulase deltaX = v0 * t + 1/2 * a * t * t
	// where a is "-gravity" but only on the y plane, and a is 0 in xz plane.
	// so xz = v0xz * t => v0xz = xz / t
	// and y = v0y * t - 1/2 * gravity * t * t => v0y * t = y + 1/2 * gravity * t * t => v0y = y / t + 1/2 * gravity * t
	float t = timeToTarget;
	float v0y = y / t + 0.5f * Physics.gravity.magnitude * t;
	float v0xz = xz / t;
	
	// create result vector for calculated starting speeds
	Vector3 result = toTargetXZ.normalized;		// get direction of xz but with magnitude 1
	result *= v0xz;								// set magnitude of xz to v0xz (starting speed in xz plane)
	result.y = v0y;								// set y to v0y (starting speed of y plane)
	
	return result;
}

Simply call the function with the origin position, target position and how long you want the throw to take (which will affect the angle).
Then apply the result on the object you want to throw with rigidbody.AddForce(throwSpeed, ForceMode.VelocityChange);

The function BallisticVel below calculates the necessary velocity to reach a target given the launching angle and the target transform. It uses a ballistic trajectory that originally only worked fine for objects at the same height, but I added code to compensate to some extent for different heights - maybe this correction is precise enough in your case.

The function BallisticVel returns a vector velocity ready to be assigned to the grenade’s rigidbody.velocity: the rigidbody goes in a ballistic trajectory under physics control and lands on the target point. This script must be added to the grenade launcher (weapon, arm, whatever) because it uses the owner object position to calculate the velocity. The grenade must also not touch any collider when instantiated, or the reaction to the collision will deviate its trajectory:

function BallisticVel(target: Transform, angle: float): Vector3 {
	var dir = target.position - transform.position;  // get target direction
	var h = dir.y;  // get height difference
	dir.y = 0;  // retain only the horizontal direction
	var dist = dir.magnitude ;  // get horizontal distance
	var a = angle * Mathf.Deg2Rad;  // convert angle to radians
	dir.y = dist * Mathf.Tan(a);  // set dir to the elevation angle
	dist += h / Mathf.Tan(a);  // correct for small height differences
	// calculate the velocity magnitude
	var vel = Mathf.Sqrt(dist * Physics.gravity.magnitude / Mathf.Sin(2 * a));
	return vel * dir.normalized;
}

var target: Transform;
var grenadePrefab: Transform;

function Update(){
  if (Input.MouseButtonDown(0)){
    var grenade: Transform = Instantiate(grenadePrefab,...,...);
    grenade.rigidbody.velocity = BallisticVel(target, 45); // pass the angle and the target transform
  }
}

The higher the angle, the lesser the error when the heights are different (but the velocity is also lower, what may be a good thing for a grenade). The function doesn’t check for invalid parameters, thus it may throw exceptions if the angle is too high (near to 90) or too low (near to 0).

EDITED: If you want to pass the click point instead of a target transform, just modify the first lines of BallisticVel (the target transform is used only to get the target position anyway):

function BallisticVel(targetPos: Vector3, angle: float): Vector3 {
    var dir = targetPos - transform.position;  // get target direction
    ...

Probably you know all this already, but still…you can calculate a parabola given 3 points. You already have two of them: the player’s position and the target point. The third would be (roughly) the apex, take half of the distance between the player and the target and the height you want (maybe with player control over the height…how long he presses the mouse button, for example, so he can lob the grenade over walls or even through windows with some exercise).

It would be a good idea to reduce the equation to 2 dimensions. You eliminate the direction and only use the distance. The first point would be (0, chracter.height), the second would be (distance, 0), the last one (distance/2, throw.heigth).

To calculate the parabola you have Ax² + Bx + C = y. Put your three coordinates in there, and you can calculate A, B and C.

You now have the trajectory your grenade should follow, minus the direction, but it should be easy to rotate it around the player once it is calculated. You probably have to control the grenade completely to follow the trajectory. The physics might not give you the desired results. You could try to calculate the starting velocity you need from the height and the distance, but I don’t know how difficult that will be and how reliable.

One thing that could be a problem, is to mirror the trajectory at obstacles…don’t have a spontaneous idea for this one.

check there: Trajectories

Then, you will have to compute when they are on different levels, y0 becomes the y of the grenadier. Check “where will it land” at the bottom of the page for this issue.

Also, add the z position to your calculation in the same fashion as x.

Here’s a solution

Hi. I you want slightly more control and include drag/wind, you could also use this: http://forum.unity3d.com/threads/script-library-demo-for-planning-ballistic-projectiles-with-air-resitance-free.314236/. It’s in c# though. in short: it’s internally still using a predefined type of curve which is slightly more advanced than a parabola, but with more physical accuracy and influences.

Where is the test of the question and answers?
Is that a bug in the website or something ?

why comments are all invisible?