Compute a basket ball trajectory

When I first started to develop the basket ball game, one of the first things I obviously wanted to implement was to be able for the players to shoot the ball in the basket. As such, I had to find a way to compute a basket ball trajectory.

As the ball is a physics object, I first tried to use the UDK built-in functions to add an impulse to it when the in-game player shoots to the basket. Before getting my hands dirty to compute the initial velocity I would give to the ball, using equations of motion, so it can go directly through the rim of the basket, I played a bit with a few impulse values It gave some interesting results; but some problems arose when I wanted to be precise enough to target the rim. Indeed, when I press the button on the game pad to make the player shoots, I first make the player jumps, like in any basket ball game, and I trigger the shoot only when I release the button. The problem was that the trajectory was only valid when the player was at the peak of its jump. If I released the button while the player was still in an ascendant motion, the ball was thrown with too much force. On the contrary, when I released the button when the player was going down, the computed force wasn't enough.

I struggled quite some time, before I finally decided to go the "easy" way: when the ball is thrown, I start to set the location of the ball each frame. With hindsight, it's a decision I don't regret, as the resulting code gives me more control, but stays simple 🙂

But enough talking, let's get to maths, shall we?

The theory

First things first, if you ever have to deal with ballistics, or motion of a projectile in space, you have know about Equation of motion. You have the mandatory wikipedia page, but also this one, which is full of equations which will help you, whatever you want to do or to know about motion.

These pages talk about ballistics in a 2D space, whereas in my game, I use 3D coordinates. Problem? Not at all. The easiest way to deal with 3D ballistics is to use the 2D equations, to compute the 2D coordinates of the projectile in the plane on which the shoot happens, and then to use the direction in 3D in which the projectile was shot, to get the 3D coordinates at each frame.

So, to begin with, we know the equations which define the acceleration, the speed, and the position of the projectile on the X and Y axis:

a_x = 0\\V_x = V0_x\\X = V0_x.t

And

a_y = -g\\V_y = V0_y - g.t\\Y = V0_y.t - \cfrac 12.g.t^2

In this case, we have several unknown variables, so we have to define the value of one of those unknown variable. We can choose the angle of the trajectory when we throw the ball for example.

In my case, I arbitrarily chose to fix the maximal height of the curve, and to update that maximal height depending on the location of the shooter on the court, which is a correct rough approximation of what happens in a real basket ball game. Currently, the values I chose are a peak at 4m when the shooter less than 2m from the rim, and I linearly increase that max height when the player goes farther (actually, the max heights increases by 0.25m for each meter in distance). For now, it seems to be a rather good approximation, and the behavior in the game looks like what happens in reality.

Now that we have an unknown variables less, it's time to compute the initial velocity of the ball.

Initial vertical velocity

We know that to compute Y, we must use the derivative of the vertical speed, multiplied by the time:

y = \bar{V_y}.t

We can compute the average vertical velocity to reach the peak of the curve:

\bar{V_y}=\cfrac{V_{0y} + V_{Ymax}}{2}

At the peak of the curve, the ball doesn't go up anymore, and doesn't go down yet. So its vertical speed is null. Resulting in:

\bar{V_y}=\cfrac{V_{0y} + 0}{2}=\cfrac{V_{0y}}{2}

Substituting in y = \bar{V_y}.t, we now have:

Y_{max}=\cfrac{V_{0y}}{2}.t(Y_{max})

To compute t(Y_{max}), we can again take advantage of knowing that at Y_{max}, the vertical velocity is 0. This results in the following equation:

V_y = V0_y - g.t

0 = V0_y - g.t(Y_{max})

t(Y_{max})=\cfrac {V0_y}{g}

Substituting t(Y_{max}) by \cfrac {V0_y}{g}, we have this equation:

Y_{max}=(\cfrac{V_{0y}}{g}).(\cfrac{V_{0y}}{2})=\cfrac{V_{0y}^2}{2g}

V_{0y}^2=2.y.g

V_{0y}=\sqrt{2.y.g}

Yay! We already have one component of the initial velocity! Now let's compute the other one.

Initial horizontal velocity

To do so, we will use this equation:

X = V_{0x}.t

And

V_{0x}=\frac{x}{t}

It's easy to get X_{total} as it's the distance between the ball and the rim when the ball is shot. We then need to get the value of t(total).

We can decompose this duration in 2: the time for the ball to go from the hands of the player to the peak of the curve ( which we'll name Y_{max} ) and the time from that peak down to the rim ( named \Delta Height ).

Then, we have t(total) = t(Y_{max}) + t(Y_{\Delta Height})

We already computed before the value of t(Y_{max}). We now need to compute the time from the peak of the curve to the rim : t(Y_{\Delta Height})

If we again take the equation Y = V_{0y}.t - \cfrac{1}{2}.g.t^2, it's easy to get the descending time, because as at the peak, the vertical velocity is zero, only the gravity affects the vertical position of the ball when it falls down. We can then remove V_{0y}.t from the equation, which gives us (I removed the minus sign, as we the ball is descending, Y is negative also)

Y_{\Delta Height}=\cfrac{1}{2}.g.t(Y_{\Delta Height})^2

t(Y_{\Delta Height})=\sqrt{\cfrac{2.\Delta Height}{g}}

And then, the complete travel duration becomes:

t(total) = \cfrac{V0y}{g} + \sqrt{\cfrac{2.\Delta Height}{g}}

To get the initial horizontal velocity, it is now as simple as using X = V_{0x}.t, and substituting t by what we just computed:

V_{0x}=\cfrac{x}{t}

V_{0x}=\cfrac{x}{ \cfrac{V_{0y}}{g} + \sqrt{\cfrac{2.\Delta Height}{g}}}

Easy, isn't it? 🙂

The practice

Enough of the maths, let's code this in UDK 🙂

So, in my ball class, which of course inherits from KActor, I first declare a structure, which will contain the trajectory informations I will need to set the location of the ball each frame, and an instance of that structure:

Next, I have a ThrowBall function which will fill the TrajectoryInfos instance from its arguments and disable the UDK physics (as I will move the ball by myself):

And now, the big Shoot function, which will do all the computations, to get the variables needed by ThrowBall. One thing to note: The ball location at the moment of the shoot becomes the origin of the trajectory. Thus, all height and distance computations are relative to this position.

And now, the last piece of code: the Tick event override in the Ballistic state of the ball. This is where the ball is moved along its trajectory. We first compute the new position of the ball on the 2D trajectory, using the equation of motions, and then we project that 2D position along the 3D direction stored in LOCAL_TRAJECTORY_INFOS:

Now we're done with this rather long post. Here are a couple of screenshots to illustrate the concept, with a debug view of the path followed by the ball when shot.