Game Creation with XNA/Mathematics Physics/Ballistics

From Wikibooks, open books for an open world
Jump to: navigation, search

Ballistics[edit]

If one thinks about ballistics the first couple of things that come to mind are guns and various deadly bullets. But especially in games ballistics can be concerned with the movement of any kind of projectile, from balls to bananas and from coconuts to rockets. Ballisitcs help determine how these projectiles behave during movement and what their effects are[1]. This chapter will show and explain what a game programmer needs to know when programming anything related to projectiles.

Basic Physics[edit]

The movement of any projectile will be heavily influenced by its surroundings and the physical laws it should abide by. However, it is important to remember that games do not need to be set on earth and the experience on an alien planet may be completely different from what we know to be valid. Therefore the here listed formulas and explanations may need adjustment to what ever world your are intending to let projectiles move around in.

Mass and Weight[edit]

It is a common misunderstanding that mass is the same thing as weight. But while the weight of an object can change depending on the environment it is placed in, the mass of an object will stay the same[2]. Weight (denoted by W) is defined as a force that exist when gravity effects a mass[3]:

W = m g\, , where g is the gravity present and m denotes the mass of the object

Velocity and Acceleration[edit]

Velocity describes the distance covered by an object through movement over a certain amount of time and the direction of such movement. It is the speed and direction at which your car travels along the Highway or at which a bullet whizzes through the air . Probably the most commonly seen units to denote speed are km/h and m/s. h and s represent a certain amount of time, where h stands for an hour and s for a second, km and m mean kilometer and meter, the distance traveled during this time interval. Velocity is defined by a vector which specifies the direction of movement and its absolute value is the speed.

Imagening a ball that is thrown, it will not have the same speed through its whole flight. It will speed up after leaving the hand and it will slow down eventually. This is called acceleration. It is the rate by which the speed of an object changes over time. Newton's second Law of motion shows that acceleration depends on the force that is exercised on an object (e.g the force from the arm and hand that throw the ball) and the mass of such object (eg. the ball): F = m a\, \implies a = \frac{F}{m}
The acceleration of such object will be in the same direction as the applied force. The unit for acceleration is distance traveled over time squared, for example km/s².

Gravity[edit]

Universal gravitation is a force that takes effect between any two objects, drawing them torwards each other. This force depends on the objects' masses as well as their distance to each other.[4] The general formula to calculate this force looks like this:
F = G \frac{m_1 m_2}{r^2} ,where m_1 and m_2 are the objects' masses, r is the distance and G the universal gravitational constant
The universal gravitational constant is:[5]  G = 6.67428 \times 10^{-11} \ \mbox{m}^3 \ \mbox{kg}^{-1} \ \mbox{s}^{-2} = 6.67428 \times 10^{-11} \ {\rm N}\, {\rm (m/kg)^2}

When talking about the gravity of earth, the acceleration experienced by a mass because of the existing attractive force, is meant. So gravity is nothing other than acceleration torwards the earth's mid point. This is why an object, dropped from a high building, will continue to be in free fall until it is stopped by another object, for example the ground. The gravity of earth is defined as follows: g= G \frac{m_{earth}}{r^2} , where g is the gravity of earth, m the earth's mass and r its radius
The earth's gravity on the surface equals approximately 9.8 meters/second².

Drag[edit]

Drag influences the velocity of objects moving through fluids and gases. This force is opposite to the direction of the object's movement and it hence reduces the object's speed over time. It depends on the objects mass and shape as well the density of the fluid. Because the flight path computation is usually simplified you might not end up needing the drag force. You should however consider the fluid and gases your projectile moves in and fiddle around with the scaling factors to get an appropriate flight path.

Projectile Movement[edit]

In games the world a player acts in is never really a hundred percent accurate representation of the real world. Therefore when programming movement of projectiles it is easier to simplify some of the physics while creating the illusion that the projectile is at least somewhat behaving like a human player would expect it to do. No matter if throwing a ball or shooting a torpedo under water there are two general and simplified patterns how projectiles move in games. These movements can be adapted and refined to match the expected movement of a specific projectile.

Projectile Class[edit]

It is advisable to make your own projectile class that includes all projectile specific variables like velocity as well as functions to manipulate and calculate the flight path. The class' basic framework could look something like this:

public class Projectile{
 
private Vector3 velocity;   //stores the direction and speed of the projectile
public Vector3 pos;         //current projectile position
private Vector3 prevPos;    //previous projectile position
private float totalTimePassed;         //time passed since start 
public bool bmoving = false;        //if the projectile is moving
 
///Constants
private const float GRAVITY = 9.8f;
 
    public void Start(Vector3 direction,int speed, Vector3 startPos){
        this.velocity = speed*Vector3.Normalize(direction);   
        this.pos = startPos;   //in the beginning the current position is the start position
        bmoving = true;
    }
 
    public void UpdateLinear(GameTime time){
        if(bmoving) LinearFlight(time);
    }
 
    public void UpdateArching(GameTime time){
        if(bmoving) ArchingFlight(time);
    }
}

To start with something needs to trigger the movement of the projectile, for example the players mouse click. On that event you create a new instance of your projectile class and call Start() to launch the projectile. You will need to keep a reference to this object because the projectiles position is going to be updated every frame and the projectile is redrawn. The update is done be calling either the UpdateLinear or UpdateArching function, depending on the flight path that's wanted. The new position will have to be part of the transformation matrix that is used to draw the projectile in your game world.

In the Start method the direction vector is normalized to ensure that when multiplied by the speed the result is a velocity vector with the same direction as the initial vector and the absolute value of the desired speed. Remember that the direction vector passed to the Start function is the aim vector of whatever made the projectile move in the first place. Its absolute value can basically be anything when we assume the aim is changeable. Hence, this would not guarantee projectiles of the same kind moving at the same speed, nor would it allow for the player to decide on the force that is excersiced on the projectile before its release, changing its speed accordingly.

If your projectile is of a form that has an obvious front, end and sides it will become necessary to change the projectiles orientation according to its flight path. Following Euler's rotation theorem, vectors of a rotation matrix have to be unit vectors as well as orthogonal[6]. For a linear flight path we could simply take the normalized velocity vector as forward vector of the orientation matrix and construct the matrix's right and up vector accordingly. However, because the projectile's flight direction constantly changes when using an arching flight path it is easier to recalculate the forward vector each update by subtracting the projectile's current position from the position held an update earlier. To do so put the following function in your projectile class. Remember to call it before drawing the projectile and put the result matrix into the appropriate transformation matrix following I.S.R.O.T sequence. This sequence specifies the order by which to multiply the transform matrices, namely the Identiy Matrix, Scaling, Rotation, Orientation and Translation.

public Matrix ConstructOrientationMatrix(){
    Matrix orientation = new Matrix();
 
    // get orthogonal vectors dependant on the projectile's aim
    Vector3 forward = pos - prevPos;     
    Vector3 right = Vector3.Cross(new Vector3(0,1,0),forward);
    Vector3 up = Vector3.Cross(right,forward);
 
    // normalize vectors, put them into 4x4 matrix for further transforms
    orientation.Right = Vector3.Normalize(right);
    orientation.Up = Vector3.Normalize(up);
    orientation.Forward = Vector3.Normalize(forward);
    orientation.M44 = 1;  
    return orientation; 
}


Linear Flight[edit]

Shows the linear movement of a ball with the velocity of (5,3,2)

A linear flight is the movement along a straight line. This kind of movement might be observed when a ball is thrown straight and very fast. Obviously, even a ball like that will eventually fall to the ground if not stopped before. However, if it is for example caught quite early after leaving the throwers hand its flight path will look linear. To simplify this movement, acceleration and gravity are neglected and the velocity is the same at all time. The direction of movement is given by the velocity vector and is the same as the aim direction of the gun,hand etc.

If you have active projectiles in your game, the XNA Update function needs to call a function that updates the position for every active projectile object. The projectile's new position is calculate like this:
pos= pos + velocity\times{timePassed} [7] , where timePassed is the time that has passed since the last update.

All this function needs as a parameter is the game time that has passed since the last update. Cawood and McGee suggest to scale this time by dividing it by 90 because otherwise the positions calculated for every frame will be to far apart.

private void LinearFlight(GameTime timePassed){
    prevPos = pos; 
    pos = pos + velocity * ((float)timePassed.ElapsedGameTime.Milliseconds/90.0f);
}


Arching Flight[edit]

Shows the simplified arching flight path of a ball

The arching flight path is a bit more realistic for most flying objects than the linear flight because it takes gravity into account. Remember that gravity is an acceleration. To calculate the position of a projectile with constant acceleration and at a certain point in time the formula is:
pos =\frac{1}{2} {a}{t}^{2} ,where a is the acceleration and t the time that has passed
Because gravity pulls the projectile towards earth only the y-coordinate of your projectile will be effected. The projectile's ascenting rate will decrease over time until it stops its climb and turns to fall. However, the x and z coordinates remain uneffected by this and are calculated just the way they are with the linear flight path. The following formula shows how to compute the y-position:
{pos_y}= ({pos_y} + {velocity_y}\times{timePassed}) - \frac{1}{2} \times{gravity}\times{totalTimePassed}^{2}
, where totalTimePassed is the time passed since the projectiles started
The minuend is equal to the linear flight formula, the subtrahend is the downwards acceleration due to gravity. It becomes obvious that the lower the projectile's speed and the further the velocity's direction is pointed towards the ground, the faster gravity will win over. This function will update the projectile's flight path:

private void ArchingFlight(GameTime timePassed){
    prevPos = pos; 
    // accumulate overall time
    totalTimePassed += (float)timePassed.ElapsedGameTime.Milliseconds/4096.0f ;
 
    // flight path where y-coordinate is additionally effected by gravity
    pos = pos + velocity * ((float)timePassed.ElapsedGameTime.Milliseconds/90.0f);
    pos.Y = pos.Y - 0.5f * GRAVITY * totalTimePassed * totalTimePassed;
}

I scaled the time that is added to the overall time down again so the gravity does not take immediate effect. For a speed of 1 scaling by 4096 produces a nice flying path. Also, the compiler hopefully does something sensible and optimises the division by 4096 because it is a multiple of two. You might want to play around with the scaling factors. If your game is not set on earth you should also think about if the gravity constant is different.

Impact[edit]

Once your projectile is on the move you might want to do some collision checking if you expect it to hit anything. For more information and details on how to do collision detection check out the chapter about Collision Detection. In case a collision is detected it is time to think about what is going to happen to the projectile and the object that was hit. What the impact will look like is highly dependent on what your projectile is. A ball can bounce back, a really fast and small bullet might penetrate the object and keep on moving, a big torpedo on the other hand would probably explode. It is easier to decide in the hit object's class what the appropriate reaction will be when hit and maybe play specified sounds or animation. Otherwise you have to keep track in the projectile class of all effects that the projectile can have on each object in the game. To keep things simple just include some functions in your projectile class that define a possible behaviour of your the projectile and call the appropriate one from the hit object class when you detect a collision. For example, when a ball hits the ground it would probably simply bounce of. To simulate this behaviour use the following function in your projectile class and call it when you detect the ball reaching the ground. All it does is reflect the incoming direction and reduce the speed. When the speed is zero or smaller the ball has stopped moving and there is no need to keep its flight path updated. The 'reflectionAxis' Vector contains only ones except for the axis along which the direction needs to be inversed, this value will have to be a -1.

public void bounce(Vector3 incomingDirection, Vector3 reflectionAxis){
    //reflect the incoming projectile and normalize it so it's "just" a direction
    Vector3 direction = Vector3.Normalize(reflectionAxis* incomingDirection);
    speed -= 0.5f;                   // reduces the speed so the arche becomes lower
    velocity = speed * direction;    // the new velocity vector
    totalTimePassed= 0;                     // gravity starts all over again
    if (speed <= 0)bmoving= false;   // no speed no movement
}

A call to this function could look something like this when the ball is supposed to bounce back from the ground, hence its y-direction needs to be inversed:

ball.bounce(ball.position - ball.previousPosition, new Vector3(1, -1, 1));


References[edit]