Overall the code is basically working correctly, but I’ve noticed that t__he character doesn’t actually reach a displacement of 3 meters, instead closer to 2.9. (like 0.01499969 to the apex at 2.906404)__
The values I’m using are the same ones used in the first video (unless I missed something of course). Granted I didn’t do the calculations in-code but afaik that shouldn’t be a problem here.
Should I be doing something differently (in the code, or something like changing Rigidbody2D properties)? Again, it seems to basically be working as expected other than not quite reaching the apex.
I changed your code a little to capture what was happening at each physics step:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//make character jump using suvat equation 5
public class JumpCharacter : MonoBehaviour
{
private Rigidbody2D rb2d;
private float finalVelocity = 0; //0 m/s
private float displacement = 3; //+3 m
private float initialVelocity;
private float accelerationDueToGravity = -20; //(m/s)^2
private bool dataLog = false;
private int fixedUpdates = 0;
// Use this for initialization
void Start()
{
rb2d = GetComponent<Rigidbody2D>();
initialVelocity = Mathf.Sqrt(120);
Debug.Log("Initial Velocity: " + initialVelocity.ToString("F30"));
}
public void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Launch();
}
}
public void FixedUpdate()
{
if (dataLog)
{
float time = fixedUpdates * Time.fixedDeltaTime;
float vel = rb2d.velocity.y;
float pos = rb2d.transform.position.y;
Debug.Log("Step: " + fixedUpdates + " Time: " + time + " Velocity: " + vel +" Position: " + pos);
if (vel < -0.1f) dataLog = false;
fixedUpdates++;
}
}
void Launch()
{
dataLog = true;
Physics2D.gravity = Vector2.up * accelerationDueToGravity;
rb2d.gravityScale = 1;
rb2d.velocity = initialVelocity * Vector2.up;
}
}
Then I got the output and compared it to the hand calculations for each step in a spreadsheet… Note the Pos value is taken from the next output line as it requires the physics solver to have run, I’ve just moved them all up a line to match the hand calcs. This could be a bit confusing as we later establish that this pos is calculated from the updated velocity (which is still listed on the following line).
That was unexpected for me… the Unity Physics model perfectly matched the velocity prediction, but produced a precise delta of 0.004m every step of the position calculation. Note, the offset that you saw of 0.015m is the collider thickness offset, add this gap between your rb and supporting surface to remove the effect.
This made me think perhaps there was a small error in the 2D physics engine, so I ran the same experiment in 3D… and got the same answers as the 2D engine, which made me think there was nothing wrong with Unity and I had made an incorrect assumption somewhere.
Let’s see if someone smarter comes along!
EDIT
I thought about this a bit more and I believe the 0.004m delta is due to the physics engine assuming that velocity is constant for the physics cycle when calculating displacement (i.e. not including the 0.5 at^2 part).
@AlTheSlacker
A possible cause of systemic error is described in the article below. See the section on Euler integration where the author makes a somewhat similar calculation experiment and explains the results:
I can’t find a definitive answer on which integration PhysX is using, but some unconfirmed sources suggest it’s a derivative of Euler, and if that is the case, you are pretty much correct about the engine assuming constant velocity.
On a side note, why 20 for acceleration due to gravity?
What a great paper, thanks for that link, I think it perfectly explains what we are seeing here.
The 20 m/s/s comes from the original tutorial, I believe it is just chosen as a whole number to keep the maths easy and 20 instead of 10 to show that a game does not need to be the same as earth like.
Not sure how much it helps but Box2D uses a semi-implicit Euler approach. The crux of the work is done here integrating acceleration to give velocity (also adding in user-forces, gravity & damping terms) followed by contact solving and finally integrating velocity to give position.
In terms of testing above, you can get finer control by performing manual simulation rather than waiting on the FixedUpdate by using Physics2D.Simulate.
Thanks for taking the time to respond, everyone. I’ll read over the posts more and read/study the Gaffer On Games article soon. (Sounds like “semi-implicit Euler” in the article is especially relevant here; I have basic experience with Euler angles and pitch-yaw-roll from another tutorial)
I dunno if I should have stated early on I don’t have much experience with physics (still early on in the Khan academy reading). If someone could give a basic overview of what I currently need to know, I’d appreciate that.
For now, I get the sense that making changes to be more accurate to the kinematic equation to get a 3 displacement are very much out of my hands (other than increasing the initial velocity some amount), as this would involve having to mess with Unity’s underlying code. So presumably I need to simply accept what’s happening (which I’m basically fine with; I was mainly posting here to see if there were errors and changes I was likely capable of fixing/making).
If you look at the first line of my spreadsheet you will see initial velocity is 10.95445 m/s and that after one physics step it becomes 10.55445 m/s that’s your acceleration of -20 m/s/s being applied for 0.02 s (the FixedDeltaTime). Using the semi-Euler approach the engine is calculating displacement based on a constant velocity of 10.55445 m/s, so it is missing the displacement that occurred due to the acceleration from the higher velocity. You just need to bump the initial velocity up enough to make it use the average of the two velocities to calculate displacement.
In short + 0.2 m/s to your initial velocity will give you the correct displacement.
That correction factor is 0.5 * FixedDeltaTime * acceleration * -1
The -1 is because you are working backwards from the final velocity (just a sign change)
The 0.5 is to get you the average of the two velocities.
Thanks for the explanation, I appreciate it. I’ll keep looking over/studying this thread and related material and try to really comprehend what’s going on (maybe one day :p).
“s = ut + 0.5 at ^2” is just SUVAT equation 3 written slightly differently, right? (equals “s = ut + (at^2)/2”)