- Home /
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:
Players character is grenadier in full 3d world (he can jump and walk on different levels of high)
Players sees that character as third person from isometric-like point of view (similar to most crpg games like Diablo)
In any time player can throw grenade to any place within range by clicking mouse button on the ground
We know how to find coordinates of player and that click on the ground
Our problem:
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).
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:
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!
Answer by aldonaletto · May 11, 2012 at 12:47 AM
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 ...
What if we need to check lower ground targets or target up above our heads (90degrees)?
You could calculate the elevation angle of the target with a function like this:
function ElevationAngle(target: Transform): float {
// find the cannon->target vector:
var dir = target.position - transform.position;
// create a horizontal version of it:
var dirH = Vector3(dir.x, 0, dir.y);
// measure the unsigned angle between them:
var ang = Vector3.Angle(dir, dirH);
// add the signal (negative is below the cannon):
if (dir.y < 0) ang = -ang;
return ang;
}
The target angle may be used to avoid errors, and also to calculate the shooting angle - like this:
if (Input.$$anonymous$$ouseButtonDown(0)){
var ang = ElevationAngle(target);
var shootAng = ang + 15; // shoot 15 degree higher
// limit the shoot angle to a convenient range:
shootAng = $$anonymous$$athf.Clamp(shootAng, 15, 85)
// and shoot:
var grenade: Transform = Instantiate(grenadePrefab,...,...);
grenade.rigidbody.velocity = BallisticVel(target, shootAng);
}
$$anonymous$$y problem is slightly different if you have spare time can check our here :D http://answers.unity3d.com/questions/320449/rigidbody-jump-on-platform.html
Answer by Tomer-Barkan · May 14, 2013 at 12:36 PM
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);
WOW, the function tbkn posted is amazing for me! Thank you very much for posting that. $$anonymous$$an there's some smart people in this world haha.
Just so other people know, the "calculateBestThrowSpeed()" function tbkn posted works great. $$anonymous$$y projectile reaches the static target point AND I can control how quickly. Thank you so much tbkn.
I had to change 0.5f
to 1f
because my projectiles were only reaching half-way, but 1f
does the trick for me! Thanks
I didn't have to change anything, tbkn's script and instructions worked perfectly. Velo222, my feelings exactly. I've been working on this all night and this just solved everything. Nice work tbkn.
Answer by Piflik · May 10, 2012 at 09:16 PM
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.
Answer by fafase · May 06, 2012 at 07:04 AM
check there: http://hyperphysics.phy-astr.gsu.edu/hbase/traj.html#tra7
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.
Answer by Ankit Priyarup · Mar 15, 2014 at 11:39 AM
http://www.youtube.com/watch?v=ZCDiTxBT1nE Here's a solution
Your answer
![](https://koobas.hobune.stream/wayback/20220613071134im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Kinect character throwing ball using Physics 1 Answer
Player doesn't shoot ball object forward like I tell him 2 Answers
Android Build help needed regarding ball throw 1 Answer
How can I show trajectory of a bouncing ball OnCollisionEnter/OnCollisionExit? 0 Answers
Show trajectory of a bouncing ball 0 Answers