The New Captain Chloride


The version of the chloride class in chapter 15, "Captain Chloride Gets Going," used multiple animated sprites for clarity. In a real game, you do not need to do that. The LlamaWorks2D animated_sprite class allows you to put multiple animations into a single animated_sprite object. This enables you to create one animated_sprite object for a character and assign as many animations to it as you need.

Listing 16.1 gives the new version of the chloride class. As you can see, it uses only one animated sprite. As the Captain moves, the program plays different animations depending on which direction he's moving.

Listing 16.1. The new chloride class

 1   class chloride 2   { 3   public: 4       // The Captain will go from one state to another 5       // depending on user input and other events. 6       enum state 7       { 8           STATE_STAND, 9           STATE_WALK_LEFT, 10          STATE_WALK_RIGHT 11      }; 12 13      // These are the indices into 'anim' member. 14      enum anim_index 15      { 16          ANIM_WALK_LEFT=0, 17          ANIM_WALK_RIGHT=1 18      }; 19 20      chloride(); 21 22      bool LoadResources(); 23 24      bool Update( 25          invasion *theGame); 26 27      bool Render( 28          invasion *theGame); 29 30      void X( 31          float upLeftX); 32      float X(); 33 34      void Y( 35          float upLeftY); 36      float Y(); 37 38      void GetKeyboardInput(); 39 40      void Movement( 41         vectorf new_velocity); 42 43      int BitmapHeight(); 44      int BitmapWidth(); 45 46   private: 47      //indexed into by chloride::anim_index enum. 48      animated_sprite theCaptain; 49 50      sound leftWalkSound; 51      sound rightWalkSound; 52      vectorf velocity; 53      vectorf worldPos; 54      chloride::state currState; 55      chloride::state lastWalkState; 56      int lastStepIndex; 57   }; 

This version of the chloride class has a new enumeration on lines 1418. As I mentioned in chapter 7, "Game Program Structure," enumerations are a way of defining constants. This enumeration defines a constant for walking left and another for walking right. Whenever the program needs to select a particular animation, it passes one of these two constants to the animated_sprite class's CurrentAnimation() function. It could pass numbers instead. However, these constants are more descriptive. If you see code like the following, you know that it makes Captain Chloride walk left:

 theCaptain.CurrentAnimation(ANIM_WALK_LEFT); 


However, if I were to write this without constants, it would be less obvious what is going on:

 theCaptain.CurrentAnimation(0); 


Tip

Other programmers find it easier to read your code if you use constants rather than just numbers.


On line 53 of Listing 16.1, the chloride class now has a floating-point vector that it did not have before. I put this in for a specific reason. Game programmers often use vectors to indicate the position of something on the screen. This is perfectly reasonable according to the rules of vectors. The vector on line 53 stores the location of Captain Chloride in the gamespace. Recall that coordinates in gamespaces are also called world coordinates. So we can say that this data member stores the Captain's location in world coordinates.

Factoid

A vector can be used to describe the location of a point.


The member function in the new version of the chloride class works differently because of the differences in the member data. The majority of the changes occur in the Update() and Render() functions. These are shown inListing 16.2

Listing 16.2. The workhorse functions of the chloride class

 1   bool 2   chloride::Update( 3       invasion *theGame) 4   { 5       // update the world position. 6       worldPos += velocity; 7 8      // correct the world position if necessary. 9      theGame->CollisionCheck( 10        worldPos, BitmapWidth(), BitmapHeight()); 11 12     // update the "camera". 13     // this is the world position that we would like 14     // to be in the center of the screen if the world 15     // had no boundaries. 16     float cameraWorldX = 17         X() + 0.5 * (float)BitmapWidth(); 18     theGame->SetCamera(cameraWorldX); 19 20     return (true); 21   } 22 23 24   bool 25   chloride::Render( 26       invasion *theGame) 27   { 28       bool renderOK = true; 29       int currentAnimation; 30 31   switch( currState) 32   { 33      case STATE_STAND: 34          // pause the animation 35          currentAnimation = 36              theCaptain.CurrentAnimation(); 37          theCaptain.Animation( 38             currentAnimation)->CurrentFrame( 39                 lastStepIndex); 40       break; 41 42       case STATE_WALK_LEFT: 43          lastWalkState = STATE_WALK_LEFT; 44 45          theCaptain.CurrentAnimation(ANIM_WALK_LEFT); 46 47          lastStepIndex = 48              theCaptain.Animation( 49                  ANIM_WALK_LEFT)->CurrentFrame(); 50 51          // When the foot touches the ground, play sound. 52          if (lastStepIndex == 3) 53          { 54              leftWalkSound.Play(); 55          } 56      break; 57 58      case STATE_WALK_RIGHT: 59         lastWalkState = STATE_WALK_RIGHT; 60 61         theCaptain.CurrentAnimation(ANIM_WALK_RIGHT); 62 63         lastStepIndex = 64             theCaptain.Animation( 65                 ANIM_WALK_RIGHT)->CurrentFrame(); 66 67         // When the foot touches the ground, play    sound. 68         if (lastStepIndex == 3) 69         { 70             rightWalkSound.Play(); 71         } 72      break; 73 74      default: 75          ASSERT( 0); //Unhandled state! 76      break; 77   } 78 79   // Transform from world to screen and render. 80   vector screenPos; 81   theGame->WorldToScreen(worldPos, screenPos); 82   theCaptain.X(screenPos.X()); 83   theCaptain.Y(screenPos.Y()); 84   theCaptain.Render(); 85 86   return (renderOK); 87  } 

During each frame of screen animation, the program calls the chloride class's Update() function. Updating the Captain when he was walking around the soothing blue background was easy. This version, however, does more work. First, the Update() function sets Captain Chloride's position in world coordinates on line 6 of Listing 16.2. Next, it checks to see if the Captain has collided with anything. Because the chloride class doesn't "know" what's in the level, it must ask the game object to perform collision checking, as shown on lines 910. I'll present this collision-checking function later in this chapter.

Note

This version of Invasion of the Slugwroths only lets Captain Chloride move left or right. Therefore the camera's y value does not change as the game runs. That's why the chloride class only updates the camera's x position.


Before the chloride class's Update() function ends, it updates the position of an imaginary camera that follows Captain Chloride through the world. In actuality, there is no camera. What's really happening here is that the program keeps the Captain at the center of the screen. As he moves left or right, the background scrolls in the opposite direction. However, it's helpful to imagine that there's a camera that follows him. The statement on lines 1617 sets the camera's x position to Captain Chloride's x position plus half the width of his bitmap image. This centers it on his image. The statement on line 18 sets the camera's x position.

The Render() function for the chloride class is similar to the version in chapter 15. Each case in the switch statement still plays the appropriate animation. The primary difference is that all of the animations are now in one animated_sprite object. For example, when the Captain is supposed to walk to the left, the statement on line 45 uses the constants defined in the chloride class to select the animation of him walking left.

To freeze him in place when he stops walking, the Render() function saves the frame number of the current frame in whichever animation is currently playing. For instance, if he's walking left, the program executes the statements on lines 4749. What happens here is that the Render() function invokes the animated_sprite class's Animation() function to get the current animation. The Animation() function returns a pointer. The statements on lines 4749 use that pointer to call the animation class's CurrentFrame() function. This retrieves the current frame number.

Just before returning, the Render() function changes Captain Chloride's position in world coordinates to coordinates on the screen. It then uses the animated_sprite class's Render() function to display his image on the screen.

At this point, Invasion of the Slugwroths has a chloride object that works in a level. This means we're ready to examine how levels work.



Creating Games in C++(c) A Step-by-Step Guide
Creating Games in C++: A Step-by-Step Guide
ISBN: 0735714347
EAN: 2147483647
Year: N/A
Pages: 148

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net