Docstoc

Space Missiles

Document Sample
Space Missiles Powered By Docstoc
					Space Missiles
  By Dan Dressler
     10/17/04




                    1
Game Description...................................................................................................................3
Design ....................................................................................................................................6
     The GameLevels Class:..................................................................................................7
     The Level Class: .............................................................................................................8
     The Block Class: ............................................................................................................8
     The Path Class:...............................................................................................................9
     The Side Class:...............................................................................................................9
     The Point Class: ...........................................................................................................10
     The Missile Class: ........................................................................................................10
     The Explosion Class: ...................................................................................................11
Implementation – The Bare Essentials .................................................................................12
     The myInit Function: ...................................................................................................12
     The Display Function: ..................................................................................................16
     The Mouse Function: ...................................................................................................23
     The DrawPlane Function: ............................................................................................25
Implementation – Interactions Between the Classes: ..........................................................29
     The DrawLevel Function: ............................................................................................29
     The DrawMissiles Function: ........................................................................................31
     The HandleMissileCollisions Function: .......................................................................32
     The CheckJetExplosion Function: ...............................................................................35
     The DisplayExplosions Function: ................................................................................35
Extendibility .........................................................................................................................37
     Adding AI: ...................................................................................................................37
     Random Levels: ...........................................................................................................39
     Other Possible Extensions:...........................................................................................40
Class Structure .....................................................................................................................42
     GameLevels: ................................................................................................................42
     Level:............................................................................................................................43
     Block: ...........................................................................................................................45
     Path: .............................................................................................................................47
     Side: .............................................................................................................................48
     Point: ............................................................................................................................49
     Missile: .........................................................................................................................49
     Explosion: ....................................................................................................................50




                                                                                                                                          2
                                Game Description




       The program Space Missiles is a demonstration of what potentially could become a

full-fledged 3D space-flight game. Although many space-flight games have come and

gone over the years, this one introduces a fairly unique concept – jets flying around inside

buildings! In Space Missiles, the player views the jet from behind (3rd person perspective)

and navigates it through a treacherous indoor environment while attempting to seek out

and destroying each of the targets.


                                                                                               3
       The demonstration begins by placing the jet in the center of a large texture-mapped

room filled with hovering blocks that serve as obstacles. These blocks can have any

length, height, and width, and may even move. Among these obstacle blocks are distinctly

texture-mapped target blocks, which must all be destroyed to end the game. Once the jet is

displayed, it begins to move forward. At this point, the player is responsible for avoiding

obstacle blocks while attempting to destroy each of the target blocks. If the player’s jet

hits any block, the jet immediately freezes and creates a simple explosion in the form of a

growing transparent red sphere. Once the jet turns completely black, the game ends.

       In order to destroy the targets, the jet has been equipped with two different kinds of

projectiles. The smaller, weaker Bullet Projectile is fired using the left-mouse button. The

advantage of this projectile is that it moves quickly and ten can be in play at any one time.

As for the larger, more potent Spinning Ball Projectile, although it moves more slowly and

only two can be in play at any one time, it does many times the damage of a bullet. For

effect, each type of projectile causes a small explosion upon impact with any obstacle,

including the target blocks. Because the Bullet Projectile causes less damage, it has a

smaller explosion radius than the Spinning Ball Projectile.

       In order to destroy the Target Blocks, the player must first navigate around any

obstacles. This requires the player to quickly grasp how to steer the jet. Luckily, this is

fairly easy since the jet always flies towards the mouse cursor. For added steering control,

the further the cursor is away from the center of the screen, the tighter the jet turns in that

direction. Therefore, moving the mouse cursor towards the upper-right portion of the

screen will cause the jet to both increase its incline and to bare more to the right. This

simple steering mechanism was intentional. As simulator software turns many gamers




                                                                                                  4
away with its technical gameplay procedures, Space Missiles would allow new gamers to

dive right into the game.

        As an added feature, to make the jet’s turns appear more realistic, whenever the jet

changes direction (whenever the mouse cursor is not in the center of the screen) the jet

visibly banks into the turn just as a real jet normally would. This nice effect allows the

player to better grasp where the jet is heading in 3D space.

        The ‘w’ key and the ‘s’ keys influence the jet’s velocity. The ‘w’ key makes the jet

fly faster whereas the ‘s’ key makes the jet fly slower. To improve gameplay and add

realism, maximum and minimum velocity cutoffs have been imposed. When pressing the

‘w’ key to increase the jet’s velocity, the distance between the jet and the camera/screen

increases. This effect gives the illusion that as the jet accelerates it pulls further ahead of

the camera, thus making it appear as though there is a lag before the camera can match the

jet’s velocity. The reverse is true when the jet slows down. Another feature directly

related to the jet’s speed is the size of the afterburner flare. Although this quivering flare

exists at all speeds, its size is relative to how fast the jet is going; the greater the jet’s

velocity, the larger the afterburner flare.




                                                                                                  5
                                             Design


        Like any program, strip away the features and you have left a functional program

that one might call the bare essentials. The bare essentials of Space Missiles consist of just

five parts:


                 main()
                 MyInit()
                 reshape()
                 Display()
                 event handling functions


These five parts combined provide the player with the ability to maneuver the jet through a

3D world. Let us take a brief look at the three that really need an explanation, namely

myInit(), Display(), and the event handling functions. The function MyInit() is responsible

for loading the obstacles (Block objects) into each of the levels, loading all of the texture

maps from files, defining the display lists, and loading the lighting information. The

function Display() changes the flight path of the jet according to the mouse cursor

coordinates. It also is responsible for displaying the jet and handling its destruct sequence

when it hits an obstacle. Naturally, every iteration of Display() is another frame of the

game. Finally, the event handling functions respond to various input from the mouse and

keyboard and make the necessary updates to objects and global variables.

        The bare essentials of Space Missile only allow the player to maneuver the jet

through 3D space. What it does not provide is a world around jet for it to fly through.

This is the duty of its classes:


                                                                                                6
              GameLevels
              Level
              Block
              Path
              Side
              Point
              Missile
              Explosion


Let us take a look at each class individually from a high level perspective.




The GameLevels Class:




       GameLevel is the highest class in the hierarchy and there is only one instance of

this class in the entire program. This single global object consists primarily of a list of

Level objects. Since each Level object represents a separate stage/level in the game,

GameLevel represents the game as a whole. GameLevels’ primary duty is to manage the

order of levels and call the major methods on the current level, including:


              DrawLevel() – Displays all of the blocks in the current level
              DrawMissiles() – Displays all of the projectiles currently in the level
              HandleMissileCollisions() – Handles the case when a projectile hits a block,
               including target blocks
              CheckJetExplosion() – Checks to see if the jet has hit an obstacle
              DisplayExplosions() – Displays all of the explosions currently in the game
               caused by projectiles and the jet.




                                                                                              7
The Level Class:




       The Level class is perhaps the most important class because it is responsible for

managing all of the projectiles, obstacles, and explosions belonging to the level. We use

the word ‘manage’ because the lists that hold these objects are dynamic. For example,

there are two lists of Missile objects, one for bullet projectiles and one for spinning ball

projectiles. When a mouse button is pressed, a new Missile object is added to the

appropriate list, and when the missile hits an obstacle it is then removed from the list. The

list of Explosions is dynamic because an Explosion object is added whenever the plane or a

projectile hits an obstacle and is removed when it has surpassed its maximum radius. In

the case of Blocks, the Block list is dynamic because target blocks are removed from the

list whenever they are destroyed. The Level class manages these lists by calling methods

on each object to determine when elements should be removed, and in the case of

Explosions and Missiles objects, when they should be added.




The Block Class:



                                                                                               8
       In Space Missiles, every obstacle block and target block the player encounters is an

instance of the Block class. A Block consists of a path (empty by default), six Side objects

which conceptually form the rectangular sides of the block, a color, and a type denoting

whether or not it is also a target Block. The primary functions performed on a Block

include displaying it and checking whether or not a generic point (such as a projectile’s

location) lies inside the block or not.




The Path Class:




       A Path object consists primarily of a list of Points. For stationary Blocks, this

object is empty. However, in the case that points should be assigned to a Block’s Path

object, the Block will travel smoothly along the points defined for the path at the rate

proportional to the velocity value it is given at loading time.




The Side Class:



                                                                                            9
       Recall from the discussion of the Block object that each Block contains six Side

objects and each Side object is conceptually one of the six rectangular sides of the block.

This means that a Side must contain four points (as Point objects in this case) to define the

four vertices of the polygon forming the side, as well as normal vector, which in this case

is defined using another Point object to store the three vector components.




The Point Class:

       The Point class is quite simple; its purpose is to bundle the coordinates of a point

into a single object. Although not critical, Point is used for clarity purposes which allows

for greater functionality to be added to Space Missiles.




The Missile Class:




       Each instance of the Missile class has a Point defining its current location, a Point

containing its three direction vector components.




                                                                                              10
The Explosion Class:




       An instance of the Explosion class is added to the current Level object whenever a

projectile (Missile object) has impacted an obstacle. When this occurs, the Point location

of the missile is made the Point location of the explosion. The current radius value is set to

0 and will grow for each frame the explosion is displayed. The explosion also has a

maximum value which defines how big the explosion radius can be before it is to be

removed from the level.




                                                                                           11
                Implementation – The Bare Essentials



The myInit Function:

       After main() initializes the display window, main() calls myInit(). The myInit()

function then sets the stage for the game to begin by loading extraneous data and setting up

the display environment. It does this by calling four major initialization functions:


              LoadLevels()
              LoadTextures()
              LoadDisplayLists()
              LoadLights()


The function LoadLevels() does just that; it creates a group of Level objects, and then for

each Level object created, a series of Block objects are created and added to the level.

Below is an example of how blocks are added to a new Level object.


       //create a new level with the default wall blocks
       Level level1 = Level(1);

       //add a normal Block to Level 1
       level1.AddBlock(
             Block(10, 10, 10,                       //dimensions
                   0, 3, -10,                        //coordinates
                   0.2, 0.4, 0.8));                  //color

       //add a normal Block to Level 1
       level1.AddBlock(
             Block(1, 50, 5,                         //dimensions
                   17, 0, 29,                        //coordinates
                   0.9, 0.0, 0.3));                  //color

       //add a Target Block to Level 1 with a path to travel
       Block targetBlock3 =
             Block(4, 4, 4,                //dimensions
                   -40, 10, 8,             //coordinates
                   1.0, 1.0, 1.0,          //color


                                                                                           12
                   1,                      //type – target block
                   -35, 25, 3,             //next target point in path
                   .1f);                   //velocity
             targetBlock3.addPointToPath(-30, 25, 7);
             targetBlock3.addPointToPath(-25, 5, 7);
             targetBlock3.addPointToPath(-20, 8, 7);
             targetBlock3.addPointToPath(-10, 8, 7);
             targetBlock3.addPointToPath(-10, 3, -5);
             targetBlock3.addPointToPath(-12, 25, -8);
             targetBlock3.addPointToPath(-20, 28, -15);
             targetBlock3.addPointToPath(-25, 25, -15);
       level1.AddBlock(targetBlock3);

       //add the complete level to the GameLevel object
       gameLevel.AddLevel(level1);



       After myInit() has called LoadLevels() – that is, after each Level has been assigned

its Block objects – we can then load the texture maps that will be used throughout the

course of the game. This is the role of the next major initialization function called by

myInit(), namely, LoadTextures(). All of the image information loaded by LoadTextures()

is located in portable pixmap files (*.ppm). The images used to create these files were

originally in a bitmap format. The file conversion was implemented to ease the process of

extracting the picture information. In bitmap files, the pixel information is formatted in

binary and is preceded by significant header data. In portable pixmap files, the information

takes the form of ASCII values.

       In order to perform the file conversion, the bitmap images were first cropped so

that both the height and width pixel dimensions were equivalent to 2^n for some positive

integer n. After cropping, they were opened in a program called Matlab, where the imwrite

command, similar to

“imwrite(Zap,'New_PPM_File.ppm','ppm','Encoding','ASCII')” for the file

Zap.bmp, was executed to convert each bitmap file into a .ppm file.




                                                                                             13
       Once the bitmap files have been converted to pixmap files, loading the information

into the program is relatively simple. Observe the top portion of a .ppm file below. The

first four ASCII values correspond to the magic number, the width in pixels, the height in

pixels, and the maximum pixel color values respectively. Following the header are triplets

of values ranging from 0 to 255 (the maximum pixel color value in this case). Each triplet

represents a pixel, and the three values of that triplet represent the Red Green and Blue

values of that pixel respectively.




Since we used Matlab to convert each bitmap file to a portable pixmap file, we can safely

assume that each pixmap file has the same formatting. Notice that most other file types

assign a distinct number of bytes to their values and also rely heavily upon formatting and

structure to ensure their readability. Pixmap files instead use the ‘ ’ character to separate

distinct values. This makes .ppm files extremely easy to read in C. Observe the code

below. Here we are assuming that none of the header values will be longer than four

ASCII characters. We therefore use a standard infile variable and a character array called f

(of length four) to read and immediately discard the first four values of the header. Then

we systematically read in each pixel’s three RGB values as integers and store them

individually into a three dimensional array representing the image. The first index of this



                                                                                                14
array corresponds to the row at which the pixel is located. The next index indicates which

column. The final index is a number – 0 for Red, 1 for Green, and 2 for Blue. Naturally,

given the image’s array and both a column number and row number, we can then extract

the three RGB values of the pixel at that location in the image.


       //initializing variables
       GLubyte floorCeilingTexture[64][64][3];              //should be global
       ifstream infile;
       int i,j,asciival;
       char f[4];
       infile.open("InTheHeart.ppm");

       infile>>f;             //discard    magic number
       infile>>asciival;      //discard    length
       infile>>asciival;      //discard    width
       infile>>asciival;      //discard    max color info ‘255’

       for(i=0; i<64; i++){
             for(j=0; j<64; j++){
                   infile>>asciival;
                   floorCeilingTexture[i][j][0]=(GLubyte)(asciival);
                   infile>>asciival;
                   floorCeilingTexture[i][j][1]=(GLubyte)(asciival);
                   infile>>asciival;
                   floorCeilingTexture[i][j][2]=(GLubyte)(asciival);
             }
       }

       infile.close();



Finally, whenever we later need to use the texture map on a display list, we do this:


       //64 represents both the length and width
       //floorCeilingTexture is the Glubyte variable containing the
             texture map
       glTexImage2D(
             GL_TEXTURE_2D, 0, 3, 64, 64, 0,
             GL_RGB, GL_UNSIGNED_BYTE, floorCeilingTexture);
       glEnable(GL_TEXTURE_2D);
       SetMaterialFloor();
       glCallList(FLOOR); //the polygon(s) being texture mapped
       glDisable(GL_TEXTURE_2D);


       The remaining major functions belonging to myInit() – LoadDisplayLists() and

LoadLights() – are trivial as far as complexity is concerned. The function


                                                                                        15
LoadDisplayLists() is self-descriptive. This is where all of the display lists are defined.

For Space Missiles, these include the jet, floor, ceiling, walls, and projectiles, which are

each routinely called later in the program. The function LoadLights() is also self-

descriptive. It defines each of the lights used in the programs.

       After the myInit() function has called LoadLevels(), LoadTextures(),

LoadCallLists(), and LoadLights(), myInit() completes the loading phase of Space Missiles

with following glEnable function calls:


       glEnable(GL_LIGHTING);
       glEnable(GL_LIGHT0);
       glEnable(GL_LIGHT1);
       glEnable(GL_DEPTH_TEST);
       glEnable(GL_NORMALIZE);
       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       glEnable(GL_BLEND);
       glEnable(GL_CULL_FACE);




The Display Function:

       Now that myInit() has set the stage using its initialization functions, we can discuss

how the Display() function works. When called, the Display() function renders a frame of

the game by drawing the jet on the screen and then causing a cascade of method calls that

result in displaying the level. To accomplish this, Display() performs the following

operations:


                 Modify the values representing the direction of the jet based upon the
                  position of the mouse cursor on the screen
                 Update the jet to a new location according to the newly computed direction
                  values
                 Display the jet on the screen by calling drawPlane()
                 Display the current level by calling DrawCurrentLevel() upon the
                  GameLevels object.



                                                                                               16
       Possibly the most difficult task of the entire program is modifying the direction of

the jet according to the mouse cursor’s position on the screen. We begin our discussion of

this problem by defining the following key variables for the jet, and presenting some of the

code that moves it through 3D space:


              locX, locY, and locZ for the jet’s absolute location in 3D space
              x, y, and z for the vector representing both the direction of travel and its
               velocity
              phi, theta, and rho for storing the direction vector in terms of spherical
               coordinates
              flip = 1 for the orientation of the frustum


       The difficulty of the display function lies in modifying the direction vector

whenever the mouse cursor is not in the center of the screen. Consider the following

problem:


       Suppose the jet is at the origin and flying with a direction vector of <-1, 0, -2>.
       According to the location of the mouse cursor, this direction vector should be
       changed such that the jet should now be flying 67% more to the left and have a
       51% greater incline relative to its current direction. How do we modify the
       direction vector accordingly such that the velocity (the length of the vector)
       remains constant?




(Above) The mouse cursor
position relative to the
center of the screen

(Right) Direction vector of
jet changing based upon


                                                                                              17
mouse cursor position


Since we cannot directly increase or decrease the x, y, and z components of the direction

vector to solve the problem, we must consider alternative means. Observe that when we

change the vector such that its position is 67% more to the left, we are attempting to rotate

the direction vector in a counterclockwise manner about the y-axis. With this in mind, it

becomes obvious why we would want to implement a spherical coordinate system (see

http://www.math.montana.edu/frankw/ccp/multiworld/multipleIVP/spherical/body.htm for

review). If the direction vector were in spherical coordinates, simply increasing the value

of theta would move the direction vector counterclockwise. Similarly, decreasing the

value of phi would increase the incline of the vector. At a first glance, using spherical

coordinates instead of Cartesian coordinates appears to be a simple solution. However, the

problem is that the coordinate system of the rest of the game is in Cartesian coordinates,

including the position of the jet relative to the level (locX, locY, and locZ). It turns out

that we can overcome this problem using coordinate system conversions (see

http://astronomy.swin.edu.au/~pbourke/projection/coords/ for a review). Observe the

below code used in Space Missiles to change the direction of the jet.


       /***************************************************/
       /*** CHANGING CARTESIAN TO SPHERICAL COORDINATES ***/
       /***************************************************/

               //setting rho to the distance of the direction vector
               rho = sqrt(x*x + y*y + z*z);

               /****** controlling vertical flip ******/
               //    Obtain current phi value from the direction vector
               double tempPhi = acos(y/rho);
               //    Increase or decrease phi by an amount depending on
               //    the mouse cursor’s height (newY [-1,1]) on the screen
               tempPhi = tempPhi - (newY*turnConst);
               //    Should we flip the mouse and frustum?
               if(tempPhi < 0 || tempPhi > 3.14159){
                     tempPhi = -tempPhi;


                                                                                               18
                      x=-x;
                      z=-z;
                      flip = -flip;
                      reshape();
               }
               phi = tempPhi;

               /****** controlling horizontal flip ******/
               //    Obtain current theta value from the direction vector
               theta = atan(x/z);
               //    Increase or decrease thata by an amount depending on
               //    the mouse cursor’s horizontal position (newX [-1,1])
               theta = theta - (newX*(turnConst));
               if(z < 0){
                     //    Add pi
                     theta = theta + 3.14159;
               }

       /***************************************************/
       /*** CHANGING SPHERICAL TO CARTESIAN COORDINATES ***/
       /***************************************************/

               z = rho * sin(phi) * cos(theta);
               x = rho * sin(phi) * sin(theta);
               y = rho * cos(phi);



       As seen by the comments above, here we use Cartesian coordinates as the primary

system. Then, whenever we need to change the values of the jet’s direction vector, we

simply convert the old direction vector values into spherical coordinates, add or subtract

values from phi and theta depending on where the mouse is on the screen (described in

detail under Mouse()), and then convert the direction vector’s spherical coordinates back

into Cartesian coordinates.

       This solution spawns two more problems. First, unless the z-axis is considered to

be the ‘up’ direction, a conversion straight from the book will not work since the ‘up’

direction in OpenGL is usually the y-axis. In the case of Space Missiles, the problem was

solved (in the code above) by meticulously interchanging the z and y variables used in the

coordinate conversions.




                                                                                             19
       The most important problem, however, is caused by the arcos and arctangent

functions because they only return values between 0 and pi instead of 0 and 2*pi. This

means that if we keep turning the vector counterclockwise by increasing theta, eventually

theta will skip from 178˚ to perhaps 3˚ instead of 178˚ to 183˚.




The problem is easy enough to solve for the case of theta; simply add pi whenever the z

component of the direction vector should be less than zero.

       Notice that we encounter a very similar problem with phi. Since phi is measured

from the positive y-axis, phi grows towards zero as the jet’s direction vector approaches

the positive y-axis. If the jet continues to climb into a loop and consequently forces its

direction vector to surpass the y-axis threshold, then phi should take on negative values.

However, since we use the arc cosine function to obtain phi, we will never get a negative

value for phi.




                                                                                             20
In the case of theta, we tested to see if the value of z was negative, in which case we added

pi. Unfortunately, we cannot simply test the values of x, y, or z this time to know when we

should add pi to phi. Instead, we might use a global variable to memorize when we should

do so. However, we can avoid another global variable as in the code above by attempting

a slightly more clever technique. At the critical point where phi is outside the range

computable by the arc cosine function – specifically, when phi is between zero and pi and

we have just added to phi a value (based on the mouse height) that pushed phi out of range

– we can make a correction to solve the arc cosine issue. Observe that for the remainder of

this pass of the Display() function, x, y, and z will be computed correctly because phi is

appropriately outside the range of zero to pi. However, for the following passes, phi will

not be computed as having a negative value and so the direction vector will be computed as

180˚ off from the value we want. At the point where our phi value is still correct, but

outside the range computable for the arc cosine function, we can manually move the

direction vector to the appropriate location by negating its current x and z values (which is




                                                                                             21
essentially rotating the direction vector 180˚ about the y-axis) and then negating the value

of phi to create an equivalent direction vector with a phi value in the needed range.

       Note that following the conversion, the mouse cursor is highly likely to be in close

proximity to where it was the last time the display function was called. This implies that

even though the jet is upside down, the mouse cursor is still telling the jet to once again

increase phi. This means that we need to flip both the frustum and the mouse coordinate

system such that up and down are now reversed. Fortunately, this is not difficult. In the

code above, a global variable named ‘flip’ is negated whenever the jet changes from right

side up to upside down or visa versa. Every time this value changes, reshape() is also

called so that the flipping of the frustum can be applied by a redefinition of glFustum(…).

In turn, the mouse coordinate system reacts to the value of flip behind the scenes by

negating both its x and y coordinates in the Mouse(…) function whenever the jet is upside

down (that is, when flip is -1).

       In order for the jet to be realistic, not only must it be able to change direction, but it

must also be able to move forward. This is why the position of the jet is updated to a new

location in the direction of the direction vector every time the display function is called.

To accomplish this, we may simply take each direction vector component (x, y, or z) and

add it to the appropriate jet location coordinate (locX, locY, or locZ). Observe that in

order for this to work, the magnitude of the direction vector must always be relatively

small in comparison to the length of the jet. For example, if the direction vector’s

magnitude were the length of the jet, it would move one jet-length each time the display

function was called, resulting in a jet that moves uncontrollably fast. The magnitude of the

direction vector should therefore hover around 5% of the jet’s body length.




                                                                                               22
       //moving the jet to a new location
       locX = locX + x;
       locY = locY + y;
       locZ = locZ + z;



       The camera must always be positioned directly behind the jet. With some thought,

this is easy to arrange by redefining the gluLookAt(…) function each time the display

function is called. Since the jet is always pointing in the direction of the direction vector,

placing the camera behind the jet means positioning it in the opposite direction of the

direction vector. To get this new position, we negate the direction vector, multiply it by a

constant representing the desired camera distance, and then add it to the location of the jet

(locX, locY, and locZ).


       //repositioning the camera directly behind the jet
       CamX = x * camConst;
       CamY = y * camConst;
       CamZ = z * camConst;
       gluLookAt(
             locX-CamX, locY-CamY, locZ-CamZ,          //eye
             locX, locY, locZ,                         //at
             0,1,0                                     //up
       );



       Once the jet and camera have been properly repositioned, the display function can

finally call both drawPlane() and DrawCurrentLevel() to complete the frame.




The Mouse Function:

       As mentioned before, there are two variables used globally in Space Missiles to

represent the current mouse cursor position – newX and newY. For convenience, Mouse(),

which is defined as the event handler for both glutPassiveMotionFunc(Mouse) and

glutMotionFunc(Mouse), converts the mouse position in OpenGL’s mouse coordinate



                                                                                             23
system from having the origin at the top left to a coordinate system with the origin at the

center of the screen. Additionally, instead of newX and newY containing values

corresponding to the length and width of the screen in pixels, Mouse(…) converts these

coordinates so that they range from -1 to 1 across the screen.




       Clearly, this new coordinate system provides an easy means for changing the jet’s

direction vector (as per our discussion of the Display() function). More specifically, for

every pass of the display function, the newX value is multiplied by a constant which is

then added to the theta value of the jet’s direction vector in spherical coordinates before

theta is converted back into Cartesian coordinates. Conversely, the newY value is

multiplied by yet another constant, which is then added to the phi value of the jet’s

direction vector in spherical coordinates before phi is converted back into Cartesian

coordinates. This produces the effect of rotating the direction vector according to the

position of the mouse cursor on the screen.

       Below is how the mouse coordinate conversions are handled by the Mouse()

function event handler. Note that in order to perform the conversion in the case that the



                                                                                              24
window size changes, global variables representing the screen’s length and width are used

here and are also updated in the reshape(…) function. Recall that the flip variable is used

here to tell Mouse(…) whether or not the jet is upside down. As mentioned before, when

this is the case, the global mouse coordinates (newX and newY) are negated.


       void Mouse(int xxx, int yyy){
             newX = ((float)xxx)/((windowLength)/2)-1;
             newY = -(((float)yyy)/(windowHeight/2)-1);
             if(flip == -1){
                   newX = -newX;
                   newY = -newY;
             }
             glutPostRedisplay();
       }



       In the case that a mouse button is pressed to fire a projectile, glutMouseFunc(…)

must assign a separate mouse function to handle the event. In such a case, Mouse2(…)

will call AddMissileToCurrentLevel(…) upon the global GameLevels object with either

the parameter 1 or 2 depending on which button was pressed.




The DrawPlane Function:

       DrawPlane() is responsible for displaying the jet in a realistic way. DrawPlane()

does this by completely abandoning world coordinate system and relying upon the

correctness of the Display() function’s redefinition of the gluLookAt(…) function. We can

therefore begin by saving the current matrix with a glPushMatrix() and then clearing all

matrix data using a glLoadIdentity().

       We then must consider whether or not the jet is upside-down. This, of course, can

be determined from the global flip variable mentioned in the previous section. Note that

whenever flip is equal to -1, the jet will also appear upside down because the frustum will


                                                                                           25
be flipped. To correct for this, whenever the jet is upside down, we also model the jet

upside down so that whenever the frustum is reversed, the jet appears right side up. This

can be easily implemented with the following code:


        float tempX = newX;
        float tempY = newY;
        glLoadIdentity();
        if(flip == -1){
              glPushMatrix();
              glRotatef(180, 0, 0, -1);
              tempX = tempX * -1; //tempX will be used in later code
              tempY = tempY * -1; //tempY will be used in later code
        }



Notice that we begin the above code by making local copies of the mouse cursor

coordinates newX and newY. We do this so that if we need to change them, the changes

are not reflected in the rest of the program. Then, after loading the identity matrix we test

to see whether or not the jet is currently upside down. If so, we add a rotation matrix to

essentially rotate the screen 180˚ so that the image is modeled upside down and will be

flipped upright by the frustum specifications. We also negate the local copies of the mouse

cursor coordinates so that even when the jet is displayed upside down, the jet still banks in

the correct direction when image is rendered.

        Now that the matrix has been cleared and rotated 180˚ when necessary, we can

begin to display the jet. However, notice that the origin is located at the location of the

camera and so calling a display list now will produce undesired results. We instead want

the look-at point of the camera to be the center of origin so that when we call a display list,

the jet will appear directly in front of the camera. This is why we immediately translate

back the distance of the jet’s direction vector (rho) before calling the display lists that

display the jet.




                                                                                              26
       The final task of DrawPlane() before calling the jet’s display lists is to add rotation

matrices such that the jet will appear to bank into its turns. This means that whenever the

mouse cursor is not in the very center of the screen (newX and newY are not both 0), the

jet will exhibit some degree of banking. Naturally, the further the mouse is away from the

center of the screen, the greater the change in direction, and thus the steeper the bank. To

create this effect, we add the rotation matrices as follows:


       glRotatef(tempX*20, 0,-1,0);
       glRotatef(tempY*20, 1,0,0);
       glRotatef(tempX*50, 0,0,-1);



The first of these matrices will pivot the jet along the-y axis an amount depending on the

value of newX. The constant 20 is used here to specify the maximum number of degrees

(clockwise and counterclockwise) that the jet can pivot along this axis from its original

position (facing directly forward). The following two rotation matrixes work in much the

same manner. The second rotation matrix pivots the jet along the x-axis at most 20

degrees depending on where the mouse cursor is vertically. The final rotation matrix rolls

the jet clockwise and counterclockwise along the z-axis depending on where the mouse



                                                                                             27
cursor is horizontally. Observe that these matrices combined add a realistic banking effect

to the jet.

        Finally, now that the translation and rotation matrices have been properly set up,

we can conclude DrawPlane() by calling the display lists that will display the jet and

restoring the original matrix which we pushed on earlier.




                                                                                             28
    Implementation – Interactions Between the Classes:


       The GameLevels class has two primary responsibilities. The first responsibility is

to add projectiles to the current level. Each time a mouse button is pressed, a mouse-event

handler function tells GameLevel to create a new Missile object and push it onto the

current Level’s appropriate Missile vector. The other responsibility of GameLevels is to

redisplay the level around the jet for every iteration of the Display() function. This means

that for each frame, several major methods are called upon the current Level object:

DrawLevel(), DrawMissiles(), HandleMissileCollisions(), CheckJetExplosion(), and

DisplayExplosions(). Let us examine how each of these work.




The DrawLevel Function:

       The purpose of DrawLevel() is to display all of the obstacle blocks in the current

level. It does this by iterating through its vector of Block objects and calling the

DrawBlock() method on each one. In the process, it also assesses each Target Block’s

damage variable and removes it from the vector if destroyed.


       DrawLevel(){
             for(each Block in the vector){
                    if(Block is a Target Block &&
                       Block’s damage counter is past the threshold value){
                           Remove Block from Level’s Block vector
                    }else{
                           DrawBlock();
                    }
             }
       }




                                                                                            29
       Whenever DrawBlock() is called on a Block, DrawBlock first updates the Block’s

position if it is a moving Block. Then, the DrawSide() method is called on each of the

Block’s Side objects. DrawSide() begins by first determining whether or not the Block is a

Target Block by inspecting the Block’s type flag. If so, it displays a texture mapped

polygon representing that side of the Block. If not, the polygon is displayed without any

texture mapping.


       DrawBlock(){
             if(Block has a path to follow){
                    Point p = path.UpdatePosition();
                    Move Block to p
             }
             leftSide.DrawSide();
             rightSide.DrawSide();
             topside.DrawSide();
             bottomSide.DrawSide();
             frontSide.DrawSide();
             rearSide.DrawSide();
       }


       We just mentioned that one of the tasks of DrawBlock() is to update the position of

a moving Block. In Space Missiles, each moving Block has a Path object containing a

series of guide Points which together form the outline of the path that the block follows. In

order to move the block from one guide point (Current) to the next (Target), Path

maintains a parametric equation between the two points. Since the t value can be

converted into an absolute location between the two points, steadily increasing t results in

smoothly moving the block from Current to Target.


       UpdatePosition(){
             distance = Distance from Path’s Current point to Path’s
                   Target point via distance formula
             //Update the current location between current & target
             t = t + velocity/distance;
             //If the current location is past the Target point
             if(t >= 1){
                   //Target point is now the Path’s Current point



                                                                                            30
                       current = target;
                       //The present location is at the Path’s Current point
                       t = 0;
                       //Assign to Target the next Point in Path’s Point list
                       if(Target Point is last Point in list){
                              Target Point now equals First Point in list
                       }else{
                              Target Point is next point in list
                       }
                       Redefine the equation for the newly chosen points
               }
       }




The DrawMissiles Function:

       The DrawMissiles() method iterates through the Missile vector of type 1 (For

Bullet Projectiles) and then the Missile vector of type 2 (for Spinning Ball Projectiles) and

calls either DrawMissileType1() or DrawMissileType2() on each Missile.


       DrawMissiles(){
             for(each Missile in the missile-type1 vector)
                   missile.drawMissileType1();
             for(each Missile in the missile-type2 vector)
                   missile.drawMissileType2();
       }



       Both DrawMissileType1() and DrawMissileType2() begin by updating the

Missile’s location according to its direction vector. This of course is done to create the


                                                                                             31
illusion that the projectile is quickly moving through 3D space. After the update, we

translate to the Point representing that Missile’s location and then call a display list to

display the appropriate missile type at that location.


       void drawMissileType1(){
             //update missile’s location Point
             location.x = location.x + (direction.x * velocity);
             location.y = location.y + (direction.y * velocity);
             location.z = location.z + (direction.z * velocity);

               //draw the missile
               glPushMatrix();
               glTranslatef(location.x, location.y, location.z);
               glCallList(Projectile_type_1);
               glPopMatrix();
       }




The HandleMissileCollisions Function:

       The method HandleMissileCollisions() is responsible for determining whether or

not any projectile in the level has impacted an obstacle block, and if so, performing the

following actions:


              Create an Explosion object
              Push the explosion onto the Level’s Explosion vector
              Removing the Missile from the Missile vector
              Increase the Block’s damage counter if the projectile collided with a Target
               Block.


Although inefficient, the simplest way to perform this test is by comparing each Missile to

each Block using an intersection test. To do this we are going to have to use nested loops.

Observe the following pseudo code representation:


       void HandleMissileCollisions(){
             for(each Missile in the missile-type1 vector){
                   for(each Block in the Level’s vector of Blocks){
                         if(missile.CheckImpact(block) == 1){



                                                                                              32
                                       Create an explosion at the location of
                                             missile
                                       Push the explosion onto the Level’s vector
                                             of explosions
                                       Remove the missile from the vector of
                                             missiles
                                       Increase the Block’s damage counter if it’s
                                             a Target Block
                               }
                          }
               }
               for(each Missile in the missile-type2 vector){
                     . . .
               }
       }


       The purpose of the CheckImpact(Block) method is to test whether or not a Missile

is inside a Block object. Since all of the blocks have sides parallel to the x, y, and z axis,

we can perform this by comparing the Missile’s location to each of the planes formed by

the sides of the block.




If the Point that defines the Missile’s location is bounded within these six planes (left,

right, back, front, top, bottom), then clearly the missile has entered inside the block and

therefore we have an impact and thus perform the actions dictated in

HandleMissileCollisions().




                                                                                                 33
       Notice that here we make the assumption that the projectile which has caused the

explosion was not moving extremely fast. Otherwise, it would penetrate the obstacle block

so deeply before the impact was detected that none of the explosion could be seen. This

means that the maximum radius of the Explosion resulting from a projectile hitting a block

must be greater than the projectile’s absolute distance between any two consecutive frames

in the game. Notice also that we are making the assumption that all of the obstacles have

length, height, and width dimensions larger than the greatest distance that any projectile

can move between any two frames of the game. Otherwise, the projectile may never

register an impact because in one frame the projectile will be before the block, whereas in

the next frame it will be past the block and therefore fail the intersection test in both cases.

       Avoiding these two constraints means implementing a more sophisticated method

for detecting an impact. One method is to use the test of whether or not a line intersects a

plane. Suppose we have two consecutive frames A and B. Suppose that in frame A, the

projectile is displayed at point p1 and in frame B, the projectile is displayed at point p2.

Then we can compute the equation of the line formed by these two points and test whether

or not this line intersects any of the planes formed by the sides of Block objects. However,

since any side of a block is not a true plane, but a finite polygon, we would then have to

test that the point of intersection falls within the boundaries of this polygon. Also, since

the line formed by p1 and p2 is not a line, but a line segment, then further tests would have

to ensure that the intersection takes place in that portion of the segment. Since this is not

the method used by Space Missiles, we will not investigate it in detail. However, observe

that this would significantly increase the complexity of the intersection test and could




                                                                                               34
therefore slow the performance of the game down dramatically, especially if the number of

obstacle Blocks in the game is large.




The CheckJetExplosion Function:

       The method CheckJetExplosion() begins the destruct sequence of the jet once it has

impacted an obstacle Block. It does this by taking three test points on the jet, namely the

tip and wing tips, and performing an intersection test using each of these points on each

Block in the level. This works by calling the method CheckForCollision() on the current

Level object. CheckForCollision(), like HandleMissileCollisions(), iterates through every

Block in the vector and calls CheckImpact() repeatedly on each Block for each of the test

points. If any of the intersection tests come back positive, the jet then begins its destruct

sequence, which halts all jet movement and creates an Explosion object at the present jet’s

location before ending the game.

       Notice here that the same assumptions applied to HandleMissileCollisions() must

apply to CheckJetExplosion() as well, namely, the distance moved by the jet between any

two consecutive frames in the game must be less than the following:


              Any Block’s length, width, or height
              The maximum Explosion radius caused by the jet on impact with a Block




The DisplayExplosions Function:

       The method DisplayExplosions() displays a growing transparent sphere as an

explosion wherever a projectile or the jet has conceptually collided with a Block. It works



                                                                                                35
by first iterating through each Explosion object currently in the Level’s Explosion vector

and calling DisplayExplosion() one each. DisplayExplosion() first increments the radius

value of the Explosion object. It then translates to the location of the Explosion and calls

the glutSolidSphere() function with the appropriate explosion radius value. Whenever the

Explosion’s radius value has surpassed the maximum radius value allowable for that

Explosion, it is removed from the Level’s vector.




                                                                                             36
                                     Extendibility


       Space Missiles was built knowing that some day we may wish to extend it to

include more functionality. This was why the data was structured as much as possible into

the classes we discussed. Now that we know how Space Missiles works, let us explore

some other ways to make it more interesting.




Adding AI:

       One interesting extendibility option would be to allow for intelligent computer

opponents. To even begin this discussion, let us assume that we have an instance of our

new class Jet for each flying jet in the game, including the player. Since we need the AI to

intelligently plot a course towards the player, we can see that the current Path class is

clearly inadequate. Suppose that we instead have a class called Graph containing a list of

guide points positioned throughout various open areas in the level. Then, upon loading the

game, Space Missiles compares each of these points to one another and determines

whether or not they are obstructed by an obstacle. If there is indeed an unobstructed

flyable path between them, (providing they are not too far apart) then the pair of points are

stored into the graph structure belonging to the Graph class. For example, observe the

below diagram representing a top view of a level containing just three obstacles. Note that

if any two points are relatively close and not obstructed by a block, then there is an edge

between those two points in the graph.



                                                                                              37
Observe that a graph such as the one above can be created using the following pseudo code

procedure:


        for(each guide point X){
              for(each guide point Y){
                    if(distance between X and Y is > SomeConstant){
                          for(each Block object Q){
                                 if(line from X to Y does not intersect any
                                 side of Q){
                                       add edge XY and its distance to graph
                                 }
                          }
                    }
              }
        }



Once a graph such as this has been created, a Jet can then determine the shortest path to the

guide point closest to the player, physically travel that path (just as we did for Blocks in

Path), and then fire at the player whenever there is another unobstructed path from the

location of that jet to the location of the player.




                                                                                               38
       Of course, this procedure has its limitations. For one, moving blocks may cause

some serious problems unless we meticulously maintain which edges are currently

unobstructed. Another problem is that the AI is very limited in the ways in which it can

maneuver its jet. For example, unless we can assure the computer that all edges in the

graph are at least a certain number of units away from any obstacles, the AI jet will not be

able to cut corners (as a jet naturally would) or dodge projectiles.




Random Levels:

       One interesting improvement could be the use of generating random levels. Since

all obstacles in Space Missiles are simply Block objects, randomizing levels would not be

too difficult so long as we begin with six Blocks to form the walls of the level. From there

we can generate a variety of random blocks and place them randomly throughout the level.

       Suppose we want to generate random blocks and then add them to the level; what

are the problems we may encounter? One concern is how to keep randomly generated

blocks from being placed outside of the level. If we treat the entire Level (bounded by the

insides of its walls) as a block, we can perform an intersection test on of the block’s

corners. If all of the points fail the intersection test, we generate another block to take its

place. Once we finally generate a Block object that passes the intersection test for one of

its points, we then add it to the Level object.

       Another concern is how to prevent the jet from starting at a place too close to the

surrounding Blocks. This also is relatively easy to solve. If we start the jet at a fixed place

in every level, then so long as we keep the area immediately around the jet clear of blocks,

then the jet will not have any problems. So, how do we keep the area around the jet clear


                                                                                              39
of randomly generated blocks? Imagine an invisible block that contains the jet at its center

and has a height, length, and width a certain number of times the length of the jet. This

invisible block will be the safety zone for the jet. Now, any time we generate a random

Block and propose to add it to the Level vector, we first perform two tests:


           1. Do any of the corners of the safety zone fall inside the proposed block?
           2. Do any of the proposed block’s corners fall within the safety zone?


If either of the two test functions above return true, then the proposed Block object is

rejected because it is too close to the jet. Then whenever a Block is rejected, another is

generated and tested in the same way to take its place. This process continues until both

functions return false for every Block that is supposed to be in the levcl.




Other Possible Extensions:

       One addition we could make to Space Missiles is to include other projectile types.

If we had a class for each type of projectile and changed Level so that it contained a vector


                                                                                             40
of vectors of each type of projectile, then we could fairly easily devise other types of

projectiles and implement them as part of the game. The only other concern we may have

the system used for changing weapons. We can solve this problem by pressing the 1

through 9 keys to select which weapon type to use.

       Another possibility would be to extend the Space Missiles to include multiplayer

game play. If we implemented the new class Jet, we could very easily keep track of both

players’ locations and have them interact within the same game. Once Space Missiles is

set up to support the exchanging of information between multiple connected computers,

then implementing multiplayer game styles such as co-op, one-on-one death match, or one-

on-one score competition would be fairly easy. The trouble is complexity involved in

creating such a connection.

       One other possible extension could be the addition of other obstacle types. So far,

the only kind of obstacle we can create are 3D blocks with sides paralleling one of the

three axis. The next option would be to perform intersection tests on a per-polygon basis

instead of a per-block basis as was discussed in the section Handle Missile Collisions.

Although this would be costly to implement, it would allow for any object composed of

any number of polygons to be an obstacle. This would include objects like pyramids,

parallelograms, ellipses, and just about any other object without holes.




                                                                                           41
                                   Class Structure

        Although there is an infinite number of ways to code Space Missiles, some ways of

approaching it are better than others. For some simple structuring ideas, see the below

class tables. They provide one effective way of recreating a functional program that

replicates Space Missiles.




GameLevels:

                                   CLASS: GameLevels
           FIELD LIST                            DESCRIPTION
 vector<Level> levelList                * Holds all of the level objects
 int lvlCounter                         * Holds the integer value of the current level
                  METHOD LIST                                            DESCRIPTION
 GameLevels()                                              * Sets lvlCounter to 1
 void LoadNextLevel()                                      * Increments lvlList if there is a next
                                                           Level in the vector
 void DrawCurrentLevel()                                   * Calls DrawMissiles(),
                                                           HandleMissileCollision,
                                                           DisplayExplosions(), DrawLevel(), and
                                                           CheckForCollision() on the current
                                                           Level
 void AddMissileToCurrentLevel(int x)                      * Depending on whether x is 1 or x is 2,
                                                           a new missile object will be added to
                                                           the missile1 or missile2 vector
                                                           respectively. The location and direction
                                                           parameters are set identically to those
                                                           of the jet.
 void AddLevel(Level newLevel)                             * Takes a level and adds it to the
                                                           levelList



                                                                                                     42
Level:

                                        CLASS: Level
          FIELD LIST                                         DESCRIPTION
 vector<Block> blockList;              * Holds all of the level’s block objects
 vector<Missile> missiles1;            * Holds all of the Missiles of type 1 (bullet projectiles)
                                       currently in play
 vector<Missile> missiles2;            * Holds all of the Missiles of type 2 (spinning ball
                                       projectiles) currently in play
 vector<Explosion> explosions;         * Holds all of the Explosions currently in play
 int UseDefaultBlocks;                 * Holds the value 1 or 2 representing whether or not to
                                       include wall blocks in the current level
 int maxMissile1                       * Holds the value representing the maximum number of
                                       Missile type 1 (bullet projectiles) allowable in play at any
                                       one time
 int maxMissile2                       * Holds the value representing the maximum number of
                                       Missile type 2 (spinning ball projectiles) allowable in play
                                       at any one time
                    METHOD LIST                                         DESCRIPTION
 Level(int UDB, int M1=10, int M2=2)                       * Takes a value of 0 or 1 for
                                                           assignment to UseDefaultBlocks
                                                           * Takes value M1 for assignment to
                                                           maxMissile1
                                                           * Takes value M2 for assignment to
                                                           maxMissile2
 void AddBlock(                                            * Takes the parameters needed to
         double len, double ht, double wid,                create a block, creates it, and adds it to
         double loc_x, double loc_y, double loc_z,         blockList
         double red, double green, double blue,
         int type=0)
 void DrawFloor()                                          * Draws a texture mapped polygon just
                                                           above the block comprising the floor
 void DrawSides()                                          * Draws texture mapped polygons just
                                                           inside of the blocks comprising the 4
                                                           walls




                                                                                                      43
void DrawLevel()                 * If UseDefaultBlocks is set to 1, calls
                                 DrawFloor() and DrawSides()
                                 * Iterates through blockList and counts
                                 the number of type 1 blocks (target
                                 blocks) remaining
                                 * If the number of remaining type 1
                                 blocks is 0, program ends
                                 * Checks the damage variable of each
                                 Block in blockList. If damage > 30, the
                                 block is removed from blockList
int CheckForCollision()          * Calls CheckPlaneCollision on each
                                 Block in blockList and returns 0 or 1
                                 depending on what
                                 CheckPlaneCollision returns
void DrawMissiles()              * Iterates through missiles1 and
                                 invokes updateMissile() and
                                 drawMissileType1()
                                 * Iterates through missiles2 and
                                 invokes updateMissile() and
                                 drawMissileType2()
void HandleMissileCollisions()   * Iterates through missile1. For each
                                 iteration, the programs iterates through
                                 blockList and calls checkImpact() on
                                 each block. If checkImpact() returns 1,
                                 the Missile object is removed from
                                 missile1, an Explosion object is created
                                 and pushed onto explosions, and the
                                 blocks damage is updated providing it is
                                 a type 1 block.
                                 * Iterates through missile2. For each
                                 iteration, the programs iterates through
                                 blockList and calls checkImpact() on
                                 each block. If checkImpact() returns 1,
                                 the Missile object is removed from
                                 missile2, an Explosion object is created
                                 and pushed onto explosions, and the
                                 Block’s damage is updated if it is a type



                                                                            44
                                                  1 block.
void DisplayExplosions()                          * Iterates through explosions and
                                                  invokes expand() and
                                                  displayExplosions()
                                                  * Iterates through explosions and
                                                  checks if radius is bigger than
                                                  maxRadius. If it was true, the Missile
                                                  object is removed from explosions
void drawProjectionCounters()                     * Loads the identity matrix and calls
                                                  drawBall() looping maxMissile1 -
                                                  missiles1.size() times while translating
                                                  down the screen
                                                  * Loads the identity matrix and calls
                                                  drawBall() looping maxMissile2 –
                                                  missiles2.size() times while translating
                                                  down the screen




Block:

                                 CLASS: Block
             FIELD LIST                             DESCRIPTION
Side front                      * An object with 4 points for creating and displaying a
                                polygon representing a side of a Block object
Side back                       * An object with 4 points for creating and displaying a
                                polygon representing a side of a Block object
Side left                       * An object with 4 points for creating and displaying a
                                polygon representing a side of a Block object
Side right                      * An object with 4 points for creating and displaying a
                                polygon representing a side of a Block object
Side top                        * An object with 4 points for creating and displaying a
                                polygon representing a side of a Block object
Side bottom                     * An object with 4 points for creating and displaying a
                                polygon representing a side of a Block object
Double red                      * The amount of the color red when displaying the Block
                                object
Double blue                     * The amount of the color blue when displaying the Block



                                                                                             45
                                           object
Double green                               * The amount of the color green when displaying the
                                           Block object
int damage                                 * An integer value representing the amount of damage
                                           the block has received
int type                                   * An integer value that is 0 for normal Blocks and 1 for
                                           Target Blocks
Path path                                  * An object responsible for updating the Block’s location
                                           based upon the list of points it is supposed to follow
                        METHOD LIST                                       DESCRIPTION
Block(                                                        * Takes len, ht, and wid values as the
           double len, double ht, double wid,                 length, height, and width for a new
           Point p,                                           Block object, as well as a starting point
           double r, double g, double b,                      Point p. It uses these to generate the
           int t = 0)                                         points to define its Side objects: front,
           double xxx2 = 0,                                   back, left, right, top, and bottom.
           double yyy2 = 0,                                   * r, g, and b comprise the color of the
           double zzz2 = 0,                                   block
           float v = .05)                                     * The t value denotes its type, normal or
                                                              target. The default, 0, is normal.
                                                              * xxx2, yyy2, zzz2, if specified to be
                                                              something other than 0, form the first
                                                              point added to the Path object.
                                                              * v is the velocity from the Block’s
                                                              starting point to the new point along the
                                                              Path.
addPointToPath(double xxx, double yyy, double zzz)            * Takes a given point and adds it to the
                                                              path object.
defineSides(Point p)                                          * Takes a point and (re)defines each
                                                              side object relative to the location of the
                                                              point
void drawBlock()                                              * Calls drawSide() on each Side object:
                                                              front, back, left, right, top, and bottom
                                                              * Calls defineSides on the coordinates
                                                              returned from updatePosition(), which is
                                                              called on path if Points have been
                                                              defined for the Block’s Path



                                                                                                          46
int checkImpact(                                             * Takes three coordinates and returns
          double tempx, double tempy, double tempz,          1 if it lies within the boundaries of a
          double offset=0.0)                                 Block and 0 otherwise.
int checkPlaneCollision()                                    * calls checkImpact() on coordinates
                                                             approximating the tip of the jet
                                                             * calls checkImpact() on coordinates
                                                             approximating both of the jet’s wingtips
                                                             * Returns 1 if any of the points sent to
                                                             checkImpact() returned 1




Path:

                                           CLASS: Path
           FIELD LIST                                             DESCRIPTION
vector<Point> path;                      * path is a vector containing all of the points to which the
                                         Block is to travel to.
float t                                  * The parametric value defining how far the block is
                                         between two points along a path.
Point current                            * The point from which the block is currently coming from
Point target                             * The point to which the block is currently heading
Point component                          * The x, y, z components calculated from current, target,
                                         and t
Point vector0                            * The vector of travel
float velocity                           * The rate of travel
          METHOD LIST                                             DESCRIPTION
Path()                                                 Initializes t to 0
Path(Point start, Point next, float v)                 * Adds start and next to path.
                                                       * Defines velocity = v
                                                       * Calls redefineEquasion()
AddPointToPath(Point p)                         * Takes a point p and pushes it onto path
Point updatePosition()      compute the distance between target and current
                            t = t + velocity/distance;
                            if t>=1 if Block is at, or past the target point){
                                  t = 0
                                  current = target;
                                  if(target is the last point in the vector){
                                         target is the first point in the vector
                                  }else{
                                         make target the next point in the vector
                                  }


                                                                                                        47
                              redefineEquasion();
                        }
                        return the block's new position
void redefineEquasion()                    component.x                    = current.x;
                                           vector0.x =                    target.x - current.x;
                                           component.y                    = current.y;
                                           vector0.y =                    target.y - current.y;
                                           component.z                    = current.z;
                                           vector0.z =                    target.z - current.z;




Side:

                                               CLASS: Side
          FIELD LIST                                           DESCRIPTION
Point bottomLeft                          * A point defining the bottom left corner of the side
Point bottomRight                         * A point defining the bottom right corner of the side
Point topRight                            * A point defining the top right corner of the side
Point topLeft                             * A point defining the top left corner of the side
Point NormalVector                        * A point with coordinates that serve as the normal vector
                                          of the polygon generated by bottomLeft, bottomRight,
                                          topRight, and topLeft.
                    METHOD LIST                                          DESCRIPTION
Side()                                                       * An empty constructor
Side(Point bl, Point br, Point tr, Point tl)                 * Takes Points bl, br, tr, and tl and
                                                             assigns them to bottomLeft,
                                                             bottomRight, topRight, and topLeft
                                                             respectively.
void drawSide(int type)                                      * Takes an integer representing the
                                                             type of block and displays a polygon
                                                             with the points bottomLeft, bottomRight,
                                                             topRight, and topLeft and with the
                                                             normal vector represented by
                                                             NormalVector. If type is 1, the polygon
                                                             is texturemapped because it is a target
                                                             Block. If type is not 1, it is not texture
                                                             mapped.




                                                                                                          48
Point:

 CLASS: Point
       FIELD LIST                                           DESCRIPTION
 double x                             * The x coordinate component of the point
 double y                             * The y coordinate component of the point
 double z                             * The z coordinate component of the point
                       METHOD LIST                                    DESCRIPTION
 Point()                                                  * Empty constructor
 Point(double xxx, double yyy, double zzz)                * Takes values xxx, yyy, and zzz and
                                                          assigns them to x,y, and z respectively




Missile:

                                       CLASS: Missile
             FIELD LIST                                     DESCRIPTION
 double VectX                         * The x component of the direction vector of the Missile
                                      object
 double VectY                         * The y component of the direction vector of the Missile
                                      object
 double VectZ                         * The z component of the direction vector of the Missile
                                      object
 double LocX                          * The x component of the location of the Missile object
 double LocY                          * The y component of the location of the Missile object
 double LocZ                          * The z component of the location of the Missile object
 int type                             * Value is 1 if the missile is a Bullet Projectile.
                                      * Value is 2 if the missile is a Spinning Ball Projectile
 float rhoInterval                    * The increment to increase rho each time the Missile
                                      object is displayed
 float thetaInterval                  * The increment to increase theta each time the Missile
                                      object is displayed
 float rho                            * The value of which to spin the ball horizontally via a
                                      matrix rotation
 float theta                          * The value of which to spin the ball vertically via a matrix
                                      rotation
                       METHOD LIST                                    DESCRIPTION
 Missile(                                                 * Takes Vx, Vy, and Vz as the



                                                                                                    49
        double Vx, double Vy, double Vz,                   directional vector values to assign to
           double Lx, double Ly, double Lz, int t)         VectX, VectY, and VectZ respectively
void drawMissileType1()                                    * Translates to the missiles location -
                                                           LocX, LocY, LocZ, and calls drawBall()
void drawMissileType2()                                    * Translates to the missiles location -
                                                           LocX, LocY, LocZ, and calls
                                                           drawShape()
void updateMissile()                                       * Increments the values of LocX, LocY,
                                                           and LocZ according to VectX, VectY,
                                                           and VectZ




Explosion:

                                        CLASS: Explosion
           FIELD LIST                                  METHOD LIST
double x                                 * The x component of the location of the Explosion object
double y                                 * The y component of the location of the Explosion object
double z                                 * The z component of the location of the Explosion object
double radius                            * Value representing the radius of the Explosion
float maxRadius                          * Value representing the maximum allowable size of the
                                         Explosion
                    METHOD LIST                                        DESCRIPTION
Explosion()                                                * Empty constructor
Explosion(                                                 * Takes xxx, yyy, and zzz and assigns
        double xxx, double yyy, double zzz,                them to x, y, and z respectively
        float MR)
void displayExplosion()                                    * Translates to the Missile object’s
                                                           location at x,y, and z and calls
                                                           drawBall() with radius as the parameter
void expand()                                              * Increases the value of radius by a
                                                           fixed amount




                                                                                                     50

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:20
posted:5/12/2012
language:
pages:50
Description: College Graduation Project essay on how to build a 3D Flight Sumulator Game Engine using OpenGL.