Introducing Captain Chloride


Captain Chloride, erstwhile defender of the planet Earth, requires the use of animated sprites to seem lifelike. His most common movement is walking, so that's the first thing I'll implement.

Because Invasion of the Slugwroths is a side-scrolling game, Captain Chloride must be able to walk either left or right. Each of these movements requires its own animation. Also, the game must display an image when he is not moving. I like to start simply, so I'll demonstrate walking Captain Chloride using an animated sprite for all of Captain Chloride's walking movements. Although it doesn't look all that professional, I'll have Captain Chloride just freeze in place when he stops walking.

Note

When the main character is not moving, many games do more than just display an image of it frozen in a walking position. They typically show something like an animation of the character looking out at the player and impatiently tapping its foot. It adds both life and humor to the game.


Making Captain Chloride walk in a particular direction requires several animation frames. Figure 15.1 displays the frames for walking left.

Figure 15.1. Each frame shows Captain Chloride in a slightly different position.


Note

The frames for walking left are mirror images of those used for walking right. Graphics programs let you flip bitmaps. I actually only drew the frames for walking right. I then flipped each frame so that the Captain faced left and saved the flipped frames under different file names. This is a technique you should use whenever possible.


To display Captain Chloride walking right, the game needs a set of animations that are mirror images of the ones shown in Figure 15.1. These are given in Figure 15.2.

Figure 15.2. This series of frames is a mirror image of those used to walk left.


Both sets of frames are included with the sample programs on the CD. You can find them in the folder Source\Chapter15\Prog_15_01. I drew these animation frames using CorelDraw, but they could have been done just as easily with GIMP, which I've provided for free on the CD. When I drew each frame, I saved it as a Windows BMP file.

Designing the Chloride Class

It's often the case in your games that the main character will be implemented in a class especially designed for that character. Therefore, that's the way I'll write Invasion of the Slugwroths. Listing 15.1 shows the chloride class.

Listing 15.1. The class that implements Captain Chloride

 1     class chloride 2     { 3     // Public types. 4     public: 5         enum chloride_state 6         { 7             STAND, 8             WALK_LEFT, 9             WALK_RIGHT 10        }; 11 12    // Public member functions 13    public: 14        chloride(); 15 16        bool LoadResources(); 17 18        bool Update(); 19        bool Render(); 20 21        void X( 22            float upLeftX); 23        float X(); 24 25        void Y( 26            float upLeftY); 27        float Y(); 28 29        void GetKeyboardInput(); 30 31        void Movement( 32            vectorf new_velocity); 33 34        int BitmapHeight(); 35        int BitmapWidth(); 36 37    private: 38        animated_sprite leftWalk; 39        animated_sprite rightWalk; 40        animated_sprite *standingSprite; 41        sound leftWalkSound; 42        sound rightWalkSound; 43        vectorf velocity; 44        float x,y; 45        chloride_state state; 46        chloride_state lastWalkState; 47        int lastStepIndex; 48    }; 

The chloride class defines a set of constants on lines 510. The class uses these constants to keep track of what Captain Chloride is doing during any given frame of screen animation.

Factoid

Many Windows programmers use function names like LoadResources() in their classes because sounds and images are referred to as resources in Windows.


Next, the chloride class contains the prototypes for its member functions. There are a few noteworthy items here. First, the chloride class has a member function called LoadResources(). This function, as we'll see shortly, loads all of the images and sounds that the chloride class uses.

The second important item in the chloride class member functions is the fact that the chloride class contains its own X() and Y() functions. These versions use floating-point numbers rather than integers. Although this is not necessary for this first example program, implementing the chloride class this way makes it easier to put Captain Chloride into a custom gamespace later.

Notice also that there is a member function in the chloride class called GetKeyboardInput(). This function gets the player's input and tells Captain Chloride to move left or right accordingly. It is in this class rather than the game class because it's an easy way to control Captain Chloride. You don't have to do it this way. You could put this into your game class instead.

Lines 3840 show that I'm using three different animated sprites in this program to store Captain Chloride's animations. In the next chapter, I'll show how to put all of them into one animated sprite.

Lastly, notice that lines 4546 introduce two data members of type chloride_state. These private members keep track of what the good Captain is currently doing and what he was doing in the last frame. If he was walking, the data member on line 46 stores the animation frame that was current at that time.

Note

A blue background is the default background in LlamaWorks2D.


Implementing the Chloride Class

Listing 15.2 shows the implementations of several of the most important member functions. To save space, I won't show all of the member functions. However, you can find them on the CD in the folder Source\Chapter15\Prog_15_01. The file that contains the chloride class is Chloride.cpp.

Listing 15.2. Some member functions of the chloride class

 1     void chloride::GetKeyboardInput() 2     { 3         vectorf captainDirection; 4 5         if (theApp.IsKeyPressed(KC_UP_ARROW)) 6         { 7             captainDirection.Y(-1); 8         } 9         else if (theApp.IsKeyPressed(KC_DOWN_ARROW)) 10        { 11            captainDirection.Y( 1); 12        } 13        if (theApp.IsKeyPressed(KC_LEFT_ARROW)) 14        { 15            captainDirection.X(-1); 16        } 17        else if (theApp.IsKeyPressed(KC_RIGHT_ARROW)) 18        { 19            captainDirection.X( 1); 20        } 21 22        if (captainDirection.MagnitudeSquared() == 0.0) 23        { 24            state = STAND; 25        } 26        else 27        { 28            // Make the direction be a unit vector, in 29            // case he is moving diagonally, then scale 30            // it by the speed. 31            captainDirection = captainDirection.Normalize(); 32            captainDirection *= 15.0; 33 34            if (captainDirection.X() < 0.0) 35            { 36                state = WALK_LEFT; 37            } 38            else if (captainDirection.X() > 0.0) 39            { 40                state = WALK_RIGHT; 41            } 42            else 43            { 44                state = lastWalkState; 45            } 46        } 47 48        Movement(captainDirection); 49    } 50 51 52    bool 53    chloride::Update() 54    { 55        x += velocity.X(); 56        y += velocity.Y(); 57 58        if (x < 0.0) 59        { 60            x = 0.0; 61        } 62        else if (x > theApp.ScreenWidth() - BitmapWidth()) 63        { 64            x = theApp.ScreenWidth() - BitmapWidth(); 65        } 66 67        if (y < 0.0) 68        { 69            y = 0.0; 70        } 71        else if (y > theApp.ScreenHeight() - BitmapHeight()) 72        { 73            y = theApp.ScreenHeight() - BitmapHeight(); 74        } 75 76        return true; 77    } 78 79 80    bool 81    chloride::Render() 82    { 83        bool renderOK = true; 84        animated_sprite *currentSprite; 85 86        switch( state) 87        { 88            case STAND: 89                // pause the animation 90           standingSprite->Animation(0)->CurrentFrame 91                    (lastStepIndex); 92                currentSprite = standingSprite; 93            break; 94 95            case WALK_LEFT: 96                lastWalkState = WALK_LEFT; 97                standingSprite = &leftWalk; 98 99                lastStepIndex = 100                   leftWalk.Animation(0)->CurrentFrame(); 101               if (lastStepIndex == 3) 102               { 103                   leftWalkSound.Play(); 104               } 105 106                   currentSprite = &leftWalk; 107               break; 108 109               case WALK_RIGHT: 110                   lastWalkState = WALK_RIGHT; 111                   standingSprite = &rightWalk; 112 113                   lastStepIndex = 114                       rightWalk.Animation(0)->CurrentFrame(); 115                   if (lastStepIndex == 3) 116                   { 117                       rightWalkSound.Play(); 118                   } 119 120                   currentSprite = &rightWalk; 121           break; 122 123           default: 124               ASSERT(0); //Unhandled state! 125           break; 126       } 127 128       currentSprite->X((int)x); 129       currentSprite->Y((int)y); 130       currentSprite->Render(); 131 132       return (renderOK); 133   } 

The first member function in Listing 15.2 is the GetKeyboardInput() function. It begins by declaring a vector that is used to store the direction Captain Chloride moves. This variable is of type vectorf rather than vector. Recall from chapter 5, "Function and Operator Overloading," that the difference between vectorf and vector is that vectorf uses floating-point numbers for its x and y components rather than integers. The variable captainDirection is of type vectorf because Captain Chloride will be in a gamespace defined with floating-point numbers in the final game.

On lines 520, the GetKeyboardInput() function contains statements that test the direction arrow that the player presses and sets Captain Chloride's direction accordingly. Notice in particular that, in addition to the left and right arrows, this function handles moving Captain Chloride up and down. Try running this program, which is called Prog_15_01.exe on the CD. You'll see that Captain Chloride moves up and down when you press the up or down arrow. If he's facing left, he looks like he's walking left, but he just moves up. We'll fix that later. For now, try pressing both the left and up arrows. When you do, you'll see the reason I put in the code on lines 512 of Listing 15.2; Captain Chloride walks diagonally up and left across the screen. If you want your character to walk diagonally, this is how you do it.

Next, line 22 of the GetKeyboardInput() function tests to see whether Captain Chloride's velocity is zero. If it is, line 24 sets his current state to indicate that he's standing still.

Tip

I've pointed this out before in previous chapters but it bears repeating. Whenever you can, test the magnitude squared of a vector rather than the magnitude itself. Calculating the magnitude squared is faster for your game than calculating the magnitude.


If Captain Chloride is moving, line 31 of Listing 15.2 sets his direction vector to a unit vector. Recall that unit vectors can point in any direction, but they always have a magnitude of 1. Line 32 multiplies the unit vector by 15 to make Captain Chloride move by 15.

The GetKeyboardInput() function sets Captain Chloride's current state on lines 3445. This enables the object that represents Captain Chloride to keep track of what he's doing. At any given time, this object must "know" whether Captain Chloride is walking, standing, and so forth.

Just before the GetKeyboardInput() function ends, it sets Captain Chloride's movement vector.

During each frame of screen animation, the program calls the chloride::Update() function. If you look in the file Invasion.cpp on the CD, you'll see that the game class's Update() function gets the keyboard input and then calls the chloride::Update() function.

Note

You'll find the file Invasion.cpp in the folder \Source\Chapter15\Prog_15_01.


The chloride::Update() function moves Captain Chloride by setting his x and y position on lines 5556. If he hits the edge of the screen (any edge), lines 5874 adjust his x and y position to keep him on the screen.

The chloride::Render() function begins on line 81 of Listing 15.2. It declares a pointer to an animated_sprite object as a temporary variable on line 84. On lines 72108 the chloride::Render() function figures out what Captain Chloride is doing and points the variable currentSprite at the appropriate animated_sprite object. For instance, if he's walking left, the switch statement executes the case statement that begins on line 95. This results in setting currentSprite to point at the animated sprite containing the animation of Captain Chloride walking left on line 106. When the Render() function actually renders Captain Chloride, it renders the animated sprite that currentSprite points at (see line 130).

Notice also that the case statements in the Render() function set the private data member standingSprite. This is necessary to correctly freeze Captain Chloride in place when he's standing still. Whenever Captain Chloride takes a step, the Render() function sets standingSprite so that it points either at the animated sprite of him walking left or the one of him walking right. If, during the next frame of screen animation, Captain Chloride is standing still, the Render() function displays Captain Chloride frozen in the last position he was in. It does this by setting the currentSprite variable to point at the same animation as the standingSprite data member on line 78. In addition, it uses the data member lastStepIndex to set the current animation frame. In this way, Captain Chloride freezes when the player stops walking him.



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