At the time of launch, the rocket is given a position and direction to start it on an outward journey from the tip of the rocket launcher.. To allow access to these variablesthroughout t
Trang 1Figure 19-3 shows several rockets at various elevations (on the Y axis) and at ferent stages of flight Over time, the projectiles lose momentum and gravity pullsthem to the ground The overall effect creates a nice arcing projectile path.
dif-Game developers will often use real-world physics to create more realistic ics effects The physical properties that they consider may include gravity, friction,force, velocity, acceleration, viscosity, and much more In case you’re wondering,game development companies will often implement pseudo-physics in their algo-rithms As long as the effect looks correct and is efficient, an approximation of thelaws of physics is usually the faster and more effective alternative After all, as a simu-lation approaches reality, it can become so complex that it loses its value However,even when the code deviates from the laws of physics, realistic algorithms usuallyconsider some portion of the real physical model
graph-Once the launch velocity and direction have been obtained, the effect of gravitycan be computed and the X, Y, and Z positions of the projectile can be calculatedover time The X and Z positions are calculated using the same equations as the Lin-ear Projectile algorithm to obtain the projectile’s position over time:
X t = X start + V x * t
Z t = Z start + V z * t
The Arcing Projectile algorithm treats the calculation of the Y position over time
as a special case that also considers gravity Initially, the projectile’s velocity is erful enough to defy gravity—otherwise, there would be insufficient energy to launch
pow-F I G U R E 1 9 - 3
Considering the effect of gravity over time
Trang 2the projectile into the air However, over time, the projectile loses its momentum and
gravity becomes the strongest force on the object This gravitational pull is defined by
a constant value of acceleration, g, which represents the Earth’s gravity The
ac-cepted value for g equals 9.8 meters / second2
(32 ft/s2
) After the Earth’s gravity is tored in, the equation used for calculating the Y position over time becomes:
fac-Y t = Y start + V y * t - 0.5 * g * t2
Implementing these projectile algorithms in code is simple The first example in
this chapter implements the Linear Projectile algorithm Then, in the example that
follows, the Linear Projectile algorithm is converted into an Arcing Projectile
algo-rithm
This example demonstrates how to add projectiles that can be launched on a linear
path from a rocket launcher, as shown back in Figure 19-1
In this example, you will shoot ten rockets into the air at a time When a trigger or
spacebar event occurs, the first available rocket (that is not already in flight) is
launched At the time of launch, the rocket is given a position and direction to start it
on an outward journey from the tip of the rocket launcher The rocket launcher’s
po-sition and direction are based on the camera’s current popo-sition andLookdirection
Also, during the launch, the activation state for the projectile is set totrue, and
re-mains set totrueuntil the projectile reaches the end of the path The activation state
prevents the projectile from being reused while it is in flight The projectile properties
are reset every time the projectile is launched
This example begins with either the MGHWinBaseCode or MGH360BaseCode
project located in the BaseCode folder on this book’s website
You will create aProjectile class to assist with the implementation of your
projectiles You will useProjectileto keep track of each rocket and to update its
position TheProjectile class can be created from scratch in the Solution
Ex-plorer To generate it, right-click the project and choose Add New Item Then,
choose the Code File icon and enter Projectile.cs as the Name in the Add New Item
di-alog When you click Add, GS will generate an empty Projectile.cs file
First, add the following code shell to start your Projectile class:
Trang 3Class-level declarations are also required for storing the position, direction, andactivation state of each projectile An additional variable, for storing the size of theworld, enables a check to determine whether the projectile has flown out of sight.This tells you when to deactivate the projectile To allow access to these variablesthroughout the class, we place their declarations at the top of theProjectileclass(inside the class declaration):
public Vector3 position, previousPosition; // rocket position
private Vector3 speed; // relative change in X,Y,Z public Matrix directionMatrix; // direction transformations public bool active; // visibility
private float boundary; // edge of world on X and Z private float seconds; // seconds since launch
private Vector3 startPosition; // launch position
When the program begins, each projectile needs to be created only once After theyare created, the projectiles remain inactive until the user launches them Later, youwill add a method to deactivate a projectile when it flies past the boundaries of theworld To set the projectile flight range and activation state when the projectile is ini-tialized, add this constructor to theProjectileclass:
public Projectile(float border){
public void Launch(Vector3 look, Vector3 start){
position = startPosition = start; // start at camera
speed = Vector3.Normalize(look); // unitize direction
active = true; // make visible
seconds = 0.0f; // used with gravity only
}
As discussed in Chapter 8, an object’s direction can be calculated from the object’sspeed vector AddingSetDirectionMatrix()to yourProjectile class will
Trang 4provide the method you need to make your rocket point in the direction it is
travel-ing This routine applies to both the Linear Projectile algorithm and the Arcing
Pro-jectile algorithm For the Linear ProPro-jectile algorithm, the rocket direction remains
constant as the rocket travels outwards For the Arcing Projectile algorithm,
SetDirectionMatrix()will launch the rocket with the original launcher
direc-tion, and then it will gradually drop the rocket, nose downward, as the gravitational
pull takes over:
private void SetDirectionMatrix(){
Vector3 Look = position - previousPosition;
Look.Normalize();
Vector3 Up = new Vector3(0.0f, 1.0f, 0.0f); // fake Up to get
Vector3 Right = Vector3.Cross(Up, Look);
Right.Normalize();
Up = Vector3.Cross(Look, Right); // calculate Up with
Up.Normalize(); // correct vectors
Matrix matrix = new Matrix(); // compute direction matrix
The projectile’s position is updated before being drawn each frame Also, in every
frame, the projectile’s position is incremented by a time-scaled direction vector,
which ensures that the rocket flies in the path set by the camera when the rocket is
launched When the projectile location exceeds one of the outer boundaries, it is
de-activated so that it can be dede-activated and made available for the next launch The
U p d a t e P r o j e c t i l e ( ) method implements this routine Adding
UpdateProjectile()to the projectile class ensures that your projectile positions
are updated while they are active The method also deactivates the projectiles after
they reach the outer limits of your world
public void UpdateProjectile(GameTime gameTime){
previousPosition = position; // archive last position
position += speed // update current position
* (float)gameTime.ElapsedGameTime.Milliseconds/90.0f;
SetDirectionMatrix();
Trang 5// deactivate if outer border exceeded on X or Z
if (position.Z > 2.0f * boundary || position.X > 2.0f * boundary || position.Z <-2.0f * boundary || position.X <-2.0f * boundary) active = false;
}
TheProjectileclass for a Linear Projectile algorithm is now complete, so youcan reference it from Game1.cs Adding the namespace reference at the top of theGame1.cs file enables your use of this new class:
using Projectiles;
To use theProjectileclass, you need to declare instances of it inside the gameclass An array of 10 is used here, but if you wanted, you could create a more dynamicstructure with anArrayList:
const int NUM_ROCKETS = 10;
private Projectile[] rocket = new Projectile[NUM_ROCKETS];
With these declarations, you should set up each of these ten projectiles from theInitialize() method when the program begins Passing the size of the world tothe constructor will allow us to deactivate the rocket later when it flies beyond theouter boundaries of the world:
for (int i = 0; i < NUM_ROCKETS; i++)
rocket[i] = new Projectile(BOUNDARY);
To make this example more interesting, a model of a rocket will be used for theprojectiles To reference this model in the game class, add a class-level declaration forthe model and the matrix to the game class Also, the starting Y value used in therocket and launcher transformations is tracked with the variable declared here asBASE_HEIGHT:
Model rocketModel; Matrix[] rocketMatrix;
Model launcherModel; Matrix[] launcherMatrix;
const float BASE_HEIGHT = 0.6f; // start height for models
The rocket and rocket launcher models are loaded from the rocket.fbx andlauncher.fbx files Adding theInitializeModels()method to your game classprovides the code to load these models using theContentManagerobject The di-rectory path in this code assumes you have copied the rocket.fbx, rocket.bmp,launcher.fbx, and launcher.bmp files from the Models folder on this book’s website
Trang 6Once the rocket.fbx and launcher.fbx models are referenced from the Solution
Ex-plorer,InitializeModels()can initialize the model objects and their
Remember to only reference the models in your project Both the models and
textures should be placed in the Models folder However, the content pipeline is
unable to load more than one file with the same name from the same folder because no
extension is required
Now that the models have been loaded, projectile objects can be created to track
each rocket’s direction and whereabouts Declaring ten projectile objects in the game
class in the module declarations area will make them available for your use
through-out the game class
Next, we’ll draw the rocket launcher The rocket launcher travels with the camera
and rotates about the X axis—with changes to the view position on Y whenever the
user moves the mouse or right thumbstick up or down The model rocket launcher
was designed to simplify the transformations for this movement The rocket
launcher’s base is positioned at the origin, and the barrel is centered around the Z
axis and is positioned further out on Z By design of the camera, the launcher’s
rota-tion range about the X axis is half a circle (or π radians) If the rocket launcher is
pointed directly upward, the view position on Y would equal 0.5, and if the rocket
launcher is pointed directly downward, the view position would be –0.5 (see Figure
19-4)
As discussed in Chapter 7, because XNA uses the Right Hand Rule, a negative
ro-tation around the X axis will point the launcher upward Using the same logic, a
posi-tive rotation about the X axis will point the launcher downward The launcher must
be rotated about the X axis to match the camera’sLookdirection about the Y axis
Trang 7With this information, you can calculate the rocket launcher’s rotation angle aboutthe X axis with the following equation:
rotationX = Matrix.CreateRotationX(-MathHelper.Pi*look.Y );
The launcher must also be rotated about the Y axis to match the camera’sLookrection about the Y axis And finally, to finish the transformation using theI.S.R.O.T sequence, the launcher must be translated by an amount that is equivalent
di-to the distance from the origin di-to the camera An extra shift downward on the Y axis
is added to this translation to move the launcher downward slightly so it does notblock your view
Add DrawLauncher() to your game class to move and rotate the rocketlauncher with your camera:
private void DrawLauncher(Model model){
Trang 8Vector3 look = cam.view - cam.position;
rotationX = Matrix.CreateRotationX(-MathHelper.Pi*look.Y );
rotationY = Matrix.CreateRotationY((float)Math.Atan2(look.X,
look.Z));
// 3: build cumulative matrix using I.S.R.O.T sequence
// identity,scale,rotate,orbit(translate & rotate),translate
world = scale * rotationX * rotationY * translation;
// 4: set shader parameters
foreach (ModelMesh mesh in model.Meshes){
foreach (BasicEffect effect in mesh.Effects){
To actually see the rocket launcher, you obviously need to call the method to draw
it AddingDrawLauncher()to the end of theDraw()method will draw the rocket
when other objects are rendered:
DrawLauncher(launcherModel);
Because the rocket launcher’s rotation angle about the X axis changes with the
view position on Y, if the right thumbstick or mouse shifts the view all the way up or
all the way down, you can actually see the base of the launcher, which spoils the
ef-fect Inside the camera class in theUpdateView()method, you’ll replace the code
that caps the Y view position so that it can no longer exceed 0.30 or fall below –0.10,
which prevents you from pointing the launcher into the ground The end result is that
whatever angle you point, it looks as though you are always holding the rocket
launcher:
const float LOWER_LIMIT = -0.1f; const float UPPER_LIMIT = 0.3f;
if (Qlook.Y > LOWER_LIMIT && Qlook.Y < UPPER_LIMIT)
The code that you use to launch the rocket (from the game class) is contained in the
LaunchRocket()method This routine searches through the array of projectiles
Trang 9and finds the first inactive projectile available When an inactive projectile is found,LaunchRocket()sets the starting position and direction to equal the camera posi-tion andLookdirection.
The transformations use the I.S.R.O.T sequence Their implementation to angleand position the rocket at the tip of the launcher is summarized in the comments in-cluded with this code
The starting position is needed to help track the location of each rocket To createthe required transformation, and record the initial starting position of the rocket, wecan use the matrix math discussed in Chapter 8 and Chapter 16 Once the startingposition is computed using matrices, the first row of the matrix that contains the po-sition information is stored in a vector This position vector can be used later to up-date the position of the rocket by incrementing the position by a time-scaled directionvector As you can see, it really does pay to understand how to employ linear algebrabeyond just using theMatrixobjects and methods that are shipped with XNA.AddLaunchRocket()to your game class to find the first available rocket when
a launch is triggered and to calculate and store the starting position and direction ofthe rocket:
private void LaunchRocket(int i){
Matrix orbitTranslate, orbitX, orbitY, translate, position;
Vector3 look, start;
// create matrix and store origin in first row
position = new Matrix(); // zero matrix
position.M14 = 1.0f; // set W to 1 so you can transform it
// move to tip of launcher
orbitTranslate = Matrix.CreateTranslation(0.0f, 0.0f, -0.85f);
// use same direction as launcher
look = cam.view - cam.position;
// offset needed to rotate rocket about X to see it with camera
float offsetAngle = MathHelper.Pi;
// adjust angle about X with changes in Look (Forward) direction
orbitX = Matrix.CreateRotationX(offsetAngle-MathHelper.Pi*look.Y);
// rocket's Y direction is same as camera's at time of launch
orbitY = Matrix.CreateRotationY((float)Math.Atan2(look.X,look.Z));
Trang 10// move rocket to camera position where launcher base is also located
translate = Matrix.CreateTranslation(cam.position.X,BASE_HEIGHT,
cam.position.Z);
// use the I.S.R.O.T sequence to get rocket start position
position = position * orbitTranslate * orbitX * orbitY * translate;
// convert from matrix back to vector so it can be used for updates
start = new Vector3(position.M11, position.M12, position.M13);
rocket[i].Launch(look, start);
}
At this point, the projectile objects are initialized and your launcher is in place Your
rockets are ready, but a mechanism is required to trigger their launch In this case, you
will add code to initiate their launch when the left mouse button is clicked, or when the
right trigger on the controller is pressed To ensure that all ten rockets are not launched
during this press event—which lasts over several frames— the current and previous
states of the game pad and mouse are compared To enable this input device state
checking, you must add a declaration for game pad and mouse states at the class level:
#if !XBOX
MouseState mouseCurrent, mousePrevious;
#endif
GamePadState gamepad, gamepadPrevious;
The projectile trigger events can now be handled at the end of the Update()
method In this block of code, you will update the mouse and game-pad states Then
you can determine new mouse button click or trigger-pull events by comparing the
button press states in the current frame with release states from the previous frame:
// refresh key and button states
#if !XBOX
mouseCurrent = Mouse.GetState();
#endif
gamepad = GamePad.GetState(PlayerIndex.One);
// launch rocket for right trigger and left click events
if (gamepad.Triggers.Right > 0 && gamepadPrevious.Triggers.Right == 0
#if !XBOX
|| mouseCurrent.LeftButton == ButtonState.Pressed
&& mousePrevious.LeftButton == ButtonState.Released
#endif
Trang 11){ // if launch event then launch next available rocket
for (int i = 0; i < NUM_ROCKETS; i++)
if (rocket[i].active == false){
LaunchRocket(i);
break;
} }
// archive current state for comparison next frame
ani-for (int i = 0; i < NUM_ROCKETS; i++)
// 3: build cumulative matrix using I.S.R.O.T sequence
world = scale * rotateX * rocket[i].directionMatrix * translate;
// 4: set shader parameters
foreach (ModelMesh mesh in model.Meshes){
foreach (BasicEffect effect in mesh.Effects){
Trang 12To ensure that projectiles are actually drawn,DrawRockets()needs to be called
from theDraw()method This code loops through all projectile objects and draws
the active ones at their current position with their corresponding direction:
for (int i = 0; i < NUM_ROCKETS; i++)
if (rocket[i].active)
DrawRockets(rocketModel, i);
When you compile and run this program, it shows the Linear Projectile algorithm
in action Whenever the left mouse button is clicked, or a game controller trigger is
pulled, a rocket is launched Each projectile shoots outward until it reaches an
arbi-trary boundary located at the outer limits of the world
This Arcing Projectiles example picks up where the Linear Projectile algorithm ends
When this example is complete, and the effect of gravity is factored in, the flight of
each projectile will rise to a peak and then follow a descending path to the ground
Most of the code in this revised routine remains the same However, the method that
updates the rocket position will be replaced so that the gravitational pull over time is
taken into consideration In this new routine, however, initially the linear projectile
algorithm is implemented long enough for the projectile to safely leave the barrel of
the launcher in case it is moving Use of constant speed at the onset creates a mini
turbo boost so your players don’t risk blowing themselves up every time they pull the
trigger Once the rocket is far enough away from the launcher, gravity kicks in—then
the count to the touchdown begins
The UpdateProjectile() method updates the position by factoring speed
over time In Y’s case, the height is also adjusted with the pull of gravity over time
Trang 13SetDirectionMatrix() makes this effect even more realistic by adjusting therocket’s direction The transformation matrix set when calling this method ensuresthat the rocket flies in the proper direction about the Y axis, and it also gives therocket a tilt on the X axis, so it points upward as it climbs and then lowers as therocket descends to the ground.
Replace the existingUpdateProjectile()method with this one to implementthe change:
public void UpdateProjectile(GameTime gameTime){
previousPosition = position; // store position from last frame
// gravity takes over after rocket clears the launcher
Vector3 distanceTravelled = position - startPosition;
* SCALAR; // Y uses speed and gravity vs time }
// turbo boost needed for rocket to clearly leave the launcher
else
position += speed // speed uses constant scalar
*(float)gameTime.ElapsedGameTime.Milliseconds/90.0f;
SetDirectionMatrix(); // rocket direction considers speed
if (position.Y < -0.5f) // de-activate if below ground
Trang 14C HAPTER 19 REVIEW EXERCISES
To get the most from this chapter, try out these chapter review exercises
1. Follow the step-by-step examples shown in this chapter to implement the
Linear Projectile algorithm and Arcing Projectile algorithm, if you have not
already done so
2. State how the projectile update routine for linear projectiles differs from
that for arcing projectiles
3. Replace the model rocket with your own 3D object and make it point in
the direction that it travels Add bounding-sphere collision detection to an
object in your world so that something happens when you hit it