Trajectory - issue with velocity and precision
Hi,
I'm trying to launch a projectile according to an angle and velocity at launch. According to the maths, if I launch a projectile with an angle of 50.59 degrees, at a velocity launch of 10m/s, the projectile should land 10 m further.
This is "basic" physics and maths, and it's confirmed here (http://hyperphysics.phy-astr.gsu.edu/hbase/traj.html)
Now my problem is that when I launch a GameObject in Unity with those information, it reaches 9.9m, but not 10m.... On my screenshot: - in blue the right trajectory - in white the trajectory of the Gameobject
Here is my code, which is very simple:
Vector3 shootDir = Quaternion.Euler (-50.59f, 0, 0) * Vector3.forward; //Direction of the shoot
Vector3 force = shootDir * 10;
GoToLaunch.transform.GetComponent<Rigidbody>().velocity = force;
GoToLaunch.transform.GetComponent<Rigidbody> ().useGravity = true;
The GameObject has an angularDrag of 0, so there is no friction.
It's been driving me crazy for days now... any idea?
This could very well be wrong, but worth a shot.
1) Angular Drag if I remember correctly is only Rotational Friction, sense you are using force as well, make sure drag is set to 0 until it hits the ground then reset it back to where you like or it will fly somewhere else lol. But if I remember correctly using Velocity, friction doesn't react to it regardless, so friction couldn't be an issue.
2) - Did you take into account the strength of Gravity could perhaps be wrong? I think Unity just gives it a approximation, and I highly doubt it's correct, I always end up making my own gravity system by applying my own forces as I find Unity Gravity way to weak. Or just change the Gravity for Unity, you can do it inside (Edit > Project Settings > Physics and adjust the Y value to your liking).
But I'd say your culprit is gravity if you look at how the Arc is swinging, as soon as it hits the Apex of the first part it starts to gradually drop. But this is purely my observation. As it's been known Unitys physics isn't exactly 100% accurate as I'm sure no game engine is purely accurate but close.
Sorry if this wasn't helpful lol. Not trying to push something I've made, but you can check out Physics Inspector on the Asset Store and it might be something of use for you, only mentioning it because this is clearly a physics issue.
Hope I could have been of some help buddy.
Answer by Nicolas-Liatti · Apr 25, 2016 at 06:01 AM
Yes I tried to modify the gravity, but it does not change anything. The thing is that I use Physics.gravity as a variable for both the estimation and my shoot.
For the blue estimation, I use this formula: force*t + Physics.gravity*t*t*0.5f
and for the white I just throw the object using the same force and gravity. I used this formula in some previous projects and it was working fine, so I really don't get why it's not the case anymore... I am using 5.3.4 if that makes any difference.
If anybody could try this small script I put above and let me know if the object falls at (10,0,0) exactly, or 9.xx like me...
This gets weirder...
I made the following test: On one sphere I put this code:
Vector3 shootDir = Quaternion.Euler (-50.59f, 0, 0) * Vector3.forward;
force = shootDir * power;
GoToLaunch.transform.GetComponent<Rigidbody> ().velocity = force;
And on another one:
void Update(){
float t = Time.time - startTime; //startTime = Time.time, in Start()
transform.position = finalVelocity * t + Physics.gravity * t * t * 0.5f;
}
The 2nd sphere follows the right trajectory, but not the first one. This seems to mean that the velocity in Unity 5.3.4 does NOT follow this physics formula: finalVelocity t + Physics.gravity t t 0.5f; ?
Answer by Eno-Khaon · Apr 25, 2016 at 10:28 AM
I've gone over all the formulas and created a few sample scenarios using some of my previous systems, but haven't been able to recreate a trajectory which falls short quite like yours have.
Granted, if Unity 5.3.4 is specifically the problem for any reason, I'm using 5.2.3 and wouldn't be experiencing it in that exact way. On the other hand, I haven't read anything about changes to PhysX between versions which should effect fundamental frictionless motion.
That said, how are you getting your numbers for distance traveled? In my tests, I launched from (0, 0, 0) to (0, 0, 10) and logged an impact position of (0, 0.02290, 9.90379). Without displaying more numerals (.ToString("F5")), I was seeing 0 and 9.9 respectively.
What causes it to not hit exactly on target?
Under Edit>Project Settings>Physics, you will find "Default Contact Offset" -- This creates a buffer around objects to help improve efficiency and aids in simulating visual accuracy of most collisions by helping to predict when a collision will occur. However, it will result in imperfections when you're aiming for exact, precise collisions. Reducing the value can get you closer to an exactly matching result, but it still won't be spot on.
For reference, my testing script looked like this:
using UnityEngine;
using System.Collections;
public class TestTrajectory : MonoBehaviour
{
Trajectory traj;
void Start()
{
// Your version. Exactly 10 speed on launch
Vector3 versionA = Quaternion.Euler(-50.59f, 0, 0) * Vector3.forward;
versionA *= 10.0f;
// My variant. Aims at a point exactly 10 meters away
Vector3 versionB = Trajectory.HitTargetByAngle(Vector3.zero, Vector3.forward * 10, Physics.gravity, 50.59f);
Debug.Log(versionA.ToString("F5") + " --- " + versionA.magnitude.ToString("F5"));
Debug.Log(versionB.ToString("F5") + " --- " + versionB.magnitude.ToString("F5"));
traj = new Trajectory();
// Custom script -- from, to, gravity, angle
traj.SetTargetByAngle(transform.position, transform.position + Vector3.forward * 10, Physics.gravity, 50.59f);
traj.BuildPath();
}
void Update()
{
if(Input.GetKeyDown(KeyCode.P))
{
// Built inside the custom script
GetComponent<Rigidbody>().velocity = traj.velocity;
}
}
void OnDrawGizmos()
{
// Draw sample -- also from the custom script
if(traj != null && traj.path != null)
{
for(int i = 1; i < traj.iterations; i++)
{
Gizmos.DrawLine(traj.path[i - 1], traj.path[i]);
}
}
}
void OnCollisionEnter()
{
// One collision before launch, one upon landing. Results were reliable.
Debug.Log(transform.position.ToString());
Debug.Log(transform.position.ToString("F5"));
}
}
Thanks.
Actually I found the culprit: the difference comes because of the physics Tilmestep. If I step a lower tilmestep to 0.001 for example ins$$anonymous$$d of the basic 0.02, the result becomes accurate.
It should fall exactly at 10.01 to be exact, not 10. This results comes from the physics formula, you can see it in the first image I posted.
What I need would actually be the simulation to be accurate to the Unity physics. So I need to take the Timestep into account in my simulation, ins$$anonymous$$d of relaying on the "perfect" physics formula. This means that "velocity t + Physics.gravity t t 0.5f" is not accurate when it comes to Unity...
I tried your scripts, and I have exactly the same behavior than $$anonymous$$e.
When I use your "HitTargetBySpeed" with a speed of 10 at launch, it finds an angle of 50.59. I put this angle hard coded in my script above to make it more clear, but I actually calculate it in the same way than you.
Then if I use your "HitTargetBySpeed" with the vector associated, I have the same behavior than $$anonymous$$e, meaning it's falling a bit before the final target. At 9.9 indeed, so 10 cm before the right target which is at 10m.
I guess I will stick this, even if it's not perfect I guess that will do. Thanks for your help!
For what it's worth, the second part of what causes the inaccuracy in the exact position hit is the fact that there are only 50 (default) physics updates per second.
If you break down the process and formulas, you have multiple ways of approaching it, two of which are: predictive and iterative.
fixedDeltaTime example: 0.2 (ins$$anonymous$$d of 0.02)
Starting point = (0, 0, 0)
Launch at 45 degrees with a force of 14.14213
---Predictive (Realistic):
position (p) = start + (velocity * time) + (gravity * time^2 * 0.5)
p = (0, 0, 0) + ((0, 10, 10) * 0.0) + ((0, -9.81, 0) * 0.0 * 0.0 * 0.5) = (0, 0, 0)
p = (0, 0, 0) + ((0, 10, 10) * 0.2) + ((0, -9.81, 0) * 0.2 * 0.2 * 0.5) = (0, 2, 2) + (0, -0.1962, 0) = (0, 1.8038, 2)
p = (0, 0, 0) + ((0, 10, 10) * 0.4) + ((0, -9.81, 0) * 0.4 * 0.4 * 0.5) = (0, 4, 4) + (0, -0.7848, 0) = (0, 3.2152, 4)
p = (0, 0, 0) + ((0, 10, 10) * 0.6) + ((0, -9.81, 0) * 0.6 * 0.6 * 0.5) = (0, 6, 6) + (0, -1.7658, 0) = (0, 4.2342, 6)
p = (0, 0, 0) + ((0, 10, 10) * 0.8) + ((0, -9.81, 0) * 0.8 * 0.8 * 0.5) = (0, 8, 8) + (0, -3.1392, 0) = (0, 4.8608, 8)
p = (0, 0, 0) + ((0, 10, 10) * 1.0) + ((0, -9.81, 0) * 1.0 * 1.0 * 0.5) = (0, 10, 10) + (0, -4.905, 0) = (0, 5.095, 10)
---Iterative (matches PhysX):
velocity (v) = velocity + (gravity * deltaTime)
position (p) = position + (velocity * deltaTime)
gravityDelta (g) = (0, -9.81, 0) * 0.2 = (0, -1.962, 0)
v02 = (0, 10, 10) + (0, -1.962, 0) = (0, 8.038, 10)
p02 = (0, 0, 0) + (v * 0.2) = (0, 1.6076, 2)
v04 = (0, 8.038, 10) + g = (0, 6.076, 10)
p04 = (0, 1.6076, 2) + (v * 0.2) = (0, 2.8228, 4)
v06 = (0, 6.076, 10) + g = (0, 4.114, 10)
p06 = (0, 2.8228, 4) + (v * 0.2) = (0, 3.6456, 6)
v08 = (0, 4.114, 10) + g = (0, 2.152, 10)
p08 = (0, 3.6456, 6) + (v * 0.2) = (0, 4.076, 8)
v10 = (0, 2.152, 10) + g = (0, 0.19, 10)
p10 = (0, 4.076, 8) + (v * 0.2) = (0, 4.114, 10)
Now, to compare positions at each time interval:
seconds -- predictive -- iterative
0.0 -- (0, 0.0000, 0) -- (0, 0.0000, 0)
0.2 -- (0, 1.8038, 2) -- (0, 1.6076, 2)
0.4 -- (0, 3.2152, 4) -- (0, 2.8228, 4)
0.6 -- (0, 4.2342, 6) -- (0, 3.6456, 6)
0.8 -- (0, 4.8608, 8) -- (0, 4.0760, 8)
1.0 -- (0, 5.0950, 10) -- (0, 4.1140, 10)
As you can see, they don't match up.
The PhysX approach used to calculate gravitational force applied to an object can only function with a fixed degree of accuracy. It won't be perfect. In fact, with a particularly lousy update rate, it was especially inaccurate.
But that's simply an tradeoff to be made in this case. Predictive physics calculations work well with infinite accuracy when you have strictly defined conditions. These iterative calculations are based around keeping the math simple and close enough.
As for the issue of landing point, that falls back into the issue I mentioned before with the "Default Contact Offset" value.
Your answer
Follow this Question
Related Questions
3D Trajectory Prediction 3 Answers
How to make trajectory 3D (Games like Angry Birds,But 3D)? 0 Answers
Trajectory (line renderer) not showing for some angles 1 Answer
Copy and Pasted code for Trajectory and got one parsing error... 1 Answer
Calculate force needed to reach certain point (AddForce) 1 Answer