A common, if not ubiquitous, necessity in platformers or other real-time videogamey applications with a vertical dimension is some sort of jumping mechanic. The essence of a jump is straightforward – press a button and launch upwards, reaching an apex and returning to the ground. Was that introduction even required?

In terms of the engine and programming, the nature of this jump is determined by the values given to gravity and the initial impulse. But more practical is defining the height of the jump, and some quality that *could* be labelled the “floatiness coefficient”. Or it could just be the time to the top of the jump.

Standard advice is to experiment with some values until it feels good, but that’s lame. Let’s start from the basics, and quantify what impulse and gravity will yield the desired attributes instead. With calculus! You can gloss over the derivations for the resulting, calculusless formulae; I literally cannot stop you.

First the definitions:

- height above ground is given by
*x*. Maximum height attained is the constant parameter*h*. - time since the initial impulse is given by
*t*. The~~floatiness coefficient~~time of maximum height is the constant parameter*T*. - during the jump is only a constant downwards acceleration, the magnitude of which is the constant parameter
*g*. *v*is an alias for d*x*/d*t*.- the upwards velocity that causes an actual jump is the constant parameter
*u*.

The goal is to find *u*(*h*,*T*) and *g*(*h*,*T*).

Now, the maths. Acceleration, the sole thing governing motion here, has a few equivalent expressions:

\frac{\mathrm dv}{\mathrm dt} = \frac{\mathrm dv}{\mathrm dx}\,\frac{\mathrm dx}{\mathrm dt} = v \frac{\mathrm dv}{\mathrm dx} = -g

So:

\begin{aligned} \frac{\mathrm dv}{\mathrm dt} &= -g \\ -\mathrm dv &= g \,\mathrm dt \\ -\int_u^0 \mathrm dv &= \int_0^T g \,\mathrm dt \\ \left[v\right]_0^u &= \left[-gt\right]_0^T \\ u &= gT \end{aligned}

And:

\begin{aligned} v \frac{\mathrm dv}{\mathrm dx} &= -g \\ -v \,\mathrm dv &= g \,\mathrm dx \\ -\int_u^0v \,\mathrm dv &= \int_0^h g \,\mathrm dx \\ \left[\frac{v^2}{2}\right]_0^u &= \left[gx\right]_0^h \\ \frac{u^2}{2} &= gh \\ u^2 &= 2gh \end{aligned}

These two results together give the formula for *g*.

\begin{aligned} (gT)^2 &= 2gh \\ g^2T^2 &= 2gh \\ g &= \frac{2h}{T^2} \end{aligned}

And so, *u* in terms of *h* and *T*.

\begin{aligned} u &= gT \\ &= \left(\frac{2h}{T^2}\right)T \\ &= \frac{2h}{T} \end{aligned}

And that’s that – with the desired apex and time to it known, the necessary impulse and downwards acceleration have been derived.

Suppose you don’t want to fiddle with gravity just to get a nice-feeling jump. The acceleration is already set and you just want a particular height without caring too much about how quickly or not-quickly that happens. The expression for this case was pretty much already derived:

\begin{aligned} u^2 &= 2gh \\ u &= \sqrt{2gh} \end{aligned}

Every other combination of the parameters probably isn’t important enough to enumerate explicitly, but can be derived pretty easily if necessary.

And what about the case where the height should vary with how long the jump key is held down? The proper way to handle this scenario is to not do it. That mechanic just reduces to the annoyance of always holding the button down regardless, except that one part where the level designer felt they had to make use of their “mechanic” *somehow*, so there’s a low ceiling with spikes or something, and then players have the even greater annoyance of trying not to hold it for too long but they’ll still hold it just a touch too long and get frustrated anyway, so just don’t do it. Build levels around a single, standard jump height and let the gameplay flow. Yes I have opinions.

Now what about numeric integration? Everything above is fine in the land of spherical cows, but suppose the state of the world is updated on a frame-by-frame basis and you don’t have anything resembling an analytical solution with ½gt^{2}. Do the formulae hold up?

They shouldn’t be too far off, because with constant acceleration independent of any current state, there’s no real way for errors to accumulate. Even with semi-implicit Euler integration like so:

```
vel += acc * dt;
pos += vel * dt;
```

Given `acc`

is a constant −*g* and `vel`

starts at *u*, then after the *n* timesteps `vel`

will be `u − n*g*dt`

. Which means the accumulated change in `pos`

will be

\begin{aligned} &(u - g\, dt)\, dt\\ +\;&(u - 2g\, dt)\, dt\\ +\;&\dots\\ +\;&(u - ng\, dt)\, dt \end{aligned}

As an arithmetic series, this sum is equal to

\begin{aligned} &\frac n 2 \big[ (u-g\, dt)\,dt + (u-ng\, dt)\,dt \big] \\ =\;& \frac{n\,dt}{2} \big[ u-g\, dt + u-ng\, dt \big] \\ =\;& \frac{n\,dt}{2} \big[ 2u - g\,dt - ng\,dt \big] \end{aligned}

The top of the jump should be once *n*·*g*·*dt* = *u*, thus *n* = *u*/*g*·*dt*.

\begin{aligned} \frac{n\,dt}{2} \big[ 2u - g\,dt - ng\,dt \big] &= \frac{u\,dt}{2g\,dt} \left[ 2u - g\,dt - \left(\frac{u}{g\,dt}\right) g\,dt \right] \\ &= \frac{u}{2g} [ 2u - g\,dt - u] \\ &= \frac{u}{2g} \big[ u - g\,dt \big] \\ &= \frac{u^2}{2g} - \frac{u\,dt}{2} \end{aligned}

The first part is the “proper” result, so there is a small error of *u*·*dt*/2, which will naturally decrease with smaller timesteps.

Trying to set the initial velocity to *u* + *g*·*dt* in an attempt to cancel the first timestep’s effect on it doesn’t actually help – it just makes the error term positive instead. However, using *u* + *g*·*dt*/2 seems to work fine. The internal representation of velocity at the apex will instead be slightly off (specifically, it will be *g*·*dt*/2 instead of 0), but that hardly matters because the next timestep makes that negative and still produces a descent.

So, at least in the case of semi-implicit Euler integration as above, a better formulation for *u* might be 2*h*/*T* + *h*·*dt*/*T*^{2}. Which is a pretty insignificant difference, and it’s unlikely all timesteps will all be equal anyway… unless you’re doing something with an accumulator, but really, numeric integration is a whole set of textbooks of its own.

No comments found.