## 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:

And

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:

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

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:

Substituting in , we now have:

To compute , we can again take advantage of knowing that at , the vertical velocity is 0. This results in the following equation:

Substituting by , we have this equation:

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:

And

It's easy to get as it's the distance between the ball and the rim when the ball is shot. We then need to get the value of .

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 ) and the time from that peak down to the rim ( named ).

Then, we have

We already computed before the value of . We now need to compute the time from the peak of the curve to the rim :

If we again take the equation , 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 from the equation, which gives us (I removed the minus sign, as we the ball is descending, Y is negative also)

And then, the complete travel duration becomes:

To get the initial horizontal velocity, it is now as simple as using , and substituting t by what we just computed:

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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
struct LOCAL_TRAJECTORY_INFOS { var VECTOR2D CurveInitialVelocity, CurvePosition; var VECTOR Direction, InitialLocation, NewLocation; var FLOAT Timer, Gravity; }; var private LOCAL_TRAJECTORY_INFOS TrajectoryInfos; |

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):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private function ThrowBall( const FLOAT gravity, const VECTOR2D initial_velocity, const VECTOR direction, const optional NAME next_state = 'BallisticBall' ) { TrajectoryInfos.Timer = 0.0f; TrajectoryInfos.Gravity = gravity; TrajectoryInfos.CurveInitialVelocity = initial_velocity; TrajectoryInfos.Direction = direction; TrajectoryInfos.Direction.Z = 0.0f; TrajectoryInfos.InitialLocation = Location; TrajectoryInfos.CurvePosition.X = 0.0f; TrajectoryInfos.CurvePosition.Y = 0.0f; SetPhysics( PHYS_None ); GotoState( next_state ); } |

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.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
function Shoot( const VECTOR rim_position ) { local VECTOR basket_direction, distance_ball_to_rim; local FLOAT gravity, relative_curve_max_height, horizontal_distance_ball_to_rim, ball_height, relative_hoop_height, delta_curve_max_height_and_hoop, absolute_curve_max_height; local VECTOR2D initial_velocity; gravity = Abs( WorldInfo.DefaultGravityZ ); ball_height = Location.Z; relative_hoop_height = rim_position.Z - ball_height; distance_ball_to_rim = rim_position - Location; // basket direction will be used each frame to convert the position of the ball on the 2D trajectory, in a 3D position basket_direction = Normal( distance_ball_to_rim ); // clear the Z component so that VSize returns only the horizontal distance distance_ball_to_rim.Z = 0.0f; horizontal_distance_ball_to_rim = VSize( distance_ball_to_rim ); /* Compute the value of absolute_curve_max_height, which is the absolute height peak of the trajectory. Starting at 400.0f units ( 4 meters ), it increases when the player shoots farther than 2 meters from the rim SHOOT_AbsoluteCurveMaxHeight value is 400.0f SHOOT_MinimalDistanceToIncreaseCurveHeight value is 200.0f SHOOT_DistanceToCurveHeightFactor value is 0.25f */ absolute_curve_max_height = SHOOT_AbsoluteCurveMaxHeight; if ( horizontal_distance_ball_to_rim > SHOOT_MinimalDistanceToIncreaseCurveHeight ) { absolute_curve_max_height += ( horizontal_distance_ball_to_rim - SHOOT_MinimalDistanceToIncreaseCurveHeight ) * SHOOT_DistanceToCurveHeightFactor; } // We are computing the initial velocity of the ball in the ball space, so get the peak height relative to the ball relative_curve_max_height = absolute_curve_max_height - ball_height; // Store the height difference between the peak of the curve and the rim delta_curve_max_height_and_hoop = relative_curve_max_height - relative_hoop_height; // Now, compute! initial_velocity.Y = Sqrt( 2 * gravity * relative_curve_max_height ); initial_velocity.X = horizontal_distance_ball_to_rim / ( ( initial_velocity.Y / gravity ) + ( Sqrt( 2 * delta_curve_max_height_and_hoop / gravity ) ) ); ThrowBall( -gravity, initial_velocity, basket_direction ); } |

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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
state BallisticBall { event Tick( FLOAT delta_time ) { local VECTOR new_position; super.Tick( delta_time ); TrajectoryInfos.Timer += delta_time; // x = V0x.t TrajectoryInfos.CurvePosition.X = TrajectoryInfos.CurveInitialVelocity.X * TrajectoryInfos.Timer; // y = V0y.t - (1/2).g.t² TrajectoryInfos.CurvePosition.Y = TrajectoryInfos.CurveInitialVelocity.Y * TrajectoryInfos.Timer + TrajectoryInfos.Gravity * 0.5f * TrajectoryInfos.Timer * TrajectoryInfos.Timer; new_position = TrajectoryInfos.InitialLocation + TrajectoryInfos.Direction * TrajectoryInfos.CurvePosition.X; new_position.Z = TrajectoryInfos.InitialLocation.Z + TrajectoryInfos.CurvePosition.Y; SetLocation( new_position ); } } |

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.