Alas, more physics woe

A student asked how to calculate the force required to jump a given height in Unity. A simple enough question, with a simple answer. Or so I thought.

I didn’t remember the equation. I knew there was one. And I remembered that it had a 2 in it. Usually I would jump straight onto the web and look it up. But this time I fancied working it out for myself.

Doing the math

The equations for the motion of objects with a fixed acceleration are sometimes called the SUVAT equations. This is because they share a combination of the variables:

\begin{align*}
s &= \text{displacement}\\
u &= \text{initial velocity}\\
v &= \text{final velocity}\\
a &= \text{acceleration}\\
t &= \text{time}
\end{align*}

If you’ve ever wondered what programmers mean when they talk about technical debt, the reason this doesn’t work as an acronym is a great example.

The two main equations, given these variables, tell us: (1) the velocity v, and (2) the displacement s, after some time t.

\begin{align}
v = u + at\\
s = ut + \frac{1}{2}at^2
\end{align}

In the case we are interested in, the velocity starts at our required upward velocity u, and then decreases due to gravity a as time t increases, until it becomes 0. At that point, v is 0, and t will be the time at the peak of the jump.

We can re-arrange equation (1) to express that. To do these manipulations we just need to remember to do the same things to both sides. I’ll do this carefully, one step at a time, as a mistake in any step could result in the wrong answer.

Subtract at from both sides:

v - at = u

Subtract v from both sides:

-at = u - v

Negate both sides:

at = v - u

Divide both sides by a:

t = \frac{v -u}{a}

And since we want the value when v is 0 we can simply replace it with 0:

t = \frac{0-u}{a}

Or, simpler still:

t = \frac{-u}{a}

Substituting this into equation (2), we get:

s = u \cdot \frac{-u}{a}+\frac{1}{2}\cdot a \cdot (\frac{-u}{a})^2

Now we can re-arrange this equation to tell us u (required initial velocity) given s (target height) and a (acceleration due to gravity, usually -9.81 and provided as Physics.gravity.y in Unity.)

The minuses here are a little fiddly to work with, but we can combine them into squares:

s = \frac{-u^2}{a}+\frac{1}{2} \cdot a \cdot \frac{u^2}{a^2}

Cancel the a terms in the second fraction:

s = \frac{-u^2}{a}+\frac{1}{2} \cdot \frac{u^2}{a}

Multiply out the second fraction:

s = \frac{-u^2}{a}+\frac{u^2}{2a}

Now before we can add these, we need to make the denominators match. We can do this by doubling the first fraction top and bottom:

s = \frac{-2u^2}{2a}+\frac{u^2}{2a}

Now we can add the fractions:

s = \frac{-2u^2 + u^2}{2a}

Add the numerators:

s = \frac{-u^2}{2a}

Now this looks much easier to re-arrange. We proceed as before, applying the same operations to both sides. But let’s start by swapping sides:

\frac{-u^2}{2a} = s

Multiply both sides by 2a:

-u^2 = 2as

Negate both sides:

u^2 = -2as

Take the square root of both sides:

u = \sqrt{-2as}

Magic! We now have a formula for finding the initial velocity given the acceleration due to gravity and the height we want to reach. (I knew there was a 2 in there somewhere.)

Write the code

Now all we need to do is turn that into a force and apply it to a Rigidbody, we can use ForceMode.Impulse to apply it instantaneously.

JumpScript.cs
if (Input.GetButton("Jump"))
{
	float v = Mathf.Sqrt(-2.0f * Physics.gravity.y * height);
	float force = v * body.mass;
	body.AddForce(Vector3.up * force, ForceMode.Impulse);
}

And, drum roll…

Would it help if I got out and pushed?

— Princess Leia

The object reaches a peak of almost, but not quite the required height. To be more precise, with a required height of 10m, it only reaches 9.8604m. For reference, a typical ordinary level school GCSE Physics mark scheme requires calculated answers correct to two significant figures. By this measure, Unity would fail.

Debugging

Manually calculating the positions using the SUVAT equations produces 9.9997m which is at least a pass. Why is it still not exact? The main cause is the fact that the peak occurs between the 0.02 second updates.

Correcting for this, by changing the fixed time step to an interval that lands on our peak time, the manual calculation is correct to 7 significant figures. That is about the limit of floating point accuracy anyway.

However, applying the same correction to the Unity physics system in the same way, the peak is still only 9.863m, and still a GCSE fail.

So what is up with Unity physics?

Doing some spreadsheet calculations, it appears that Unity is missing the factor of a half in their position update calculations. That is, they appear to be using:

s = ut + at^2

I have created a Unity project to demonstrate the differences which you can check out on GitHub. And, of course, I reported this as a bug to Unity.

Resolution

Thanks for reporting your issue.

I have reached out with the developers of PhysX, which is responsible for the physics calculations.

— Unity bug IN-81131 comment

I don’t know what I expected but it seems logical that they would forward physics problems to the developers of their physics middleware. Of course, this escalates the issue to more than just Unity. All users of the PhysX system are impacted.

And what did they have to say for themselves:

As most game physics engines, PhysX uses a semi-implicit Euler integrator which leads to omission of the 0.5 factor. The semi-implicit (also known as symplectic) Euler integrator has beneficial stability criteria. That’s why it’s very common in real-time applications.

— Unity bug IN-81131 comment

Conclusion

PhysX is used by many engines, including Unity and Unreal, and in many other games and engines. The implication here is that not one of them would be capable of simulating physics objects with enough precision to pass an ordinary level GCSE physics exam.

And so, regretfully, I must answer my student that as things stand, there is no simple way to precisely calculate the force required to jump a given height. But perhaps, with enough time, thought, and experimentation, I can come up with a passable solution.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.