Sprites that Come Alive


Sprites move around the screen, and as they do they typically change appearance. Imagine you're making a game called Mole Thumper. The object of the game is to guide a farmer around the screen and have him thump moles on the head so they don't eat his corn. As the farmer moves, he must appear to walk. When he thumps the moles, he must swing his mallet down on their pesky little heads. The moles must react to being thumped. All of this means that the farmer and the moles must change appearance as the game progresses. That's precisely the purpose of animated sprites.

Sprites and Animation Frames

To create an animated sprite, you must imagine a sprite as a rectangular movie screen that moves around the computer's display. Because the sprite is like a miniature movie screen, your game can display successive images on the sprite's "screen" to give the appearance of movement and change.

For example, suppose you want to display a farmer walking across the screen. To do so, you display a succession of images of the farmer with his legs and arms in a slightly different position. Each successive image is one frame of the sprite's animation. Every frame of sprite animation has to fit into the animation frames of the screen as a whole. In other words, to get the farmer to walk across the screen, your animated sprite must display the first frame of its animation in a frame of screen animation. The sprite then draws the next frame of its animation into the next screen frame. The sprite continues in this way until it reaches the end of its animation frames. It then repeats the animation to continue the appearance of the farmer walking.

In professional games, there are typically many sprites displayed on the screen at once. During each frame of screen animation, each sprite busily displays one frame of its animation. The result is that, during each frame of screen animation, all of the sprites change appearance. The movement and the variation is what helps make the game come alive.

Animated Sprites in LlamaWorks2D

LlamaWorks2D provides two classes that enable you to easily add animated sprites to your games. The first is the animated_sprite class. The animated_sprite class acts like a regular sprite in that it moves around the screen. In addition, each animated_sprite object can contain one or more animation. An animation is implemented in a class called animation. An animation object holds the bitmap images that it uses as the frames of its animation.

Objects of type animated_sprite can have multiple animations. In our mole thumping game, the animated_sprite object the game uses to represent the farmer needs animations of the farmer walking left and right. It also needs animations of the farmer swinging his mallet. It may have an animation of the farmer crying when the moles get all his corn, or one of him dancing when he thumps the last mole on the level. The ability to hold multiple animations in an animated_sprite object enables you to create a wide range of behaviors for your game's characters.

To build an animated sprite, your game begins by creating at least one animation object. It loads frames into the animation. Next, your program creates an animated_sprite object and adds the animation object to it. It can add more animations as necessary. Only one animation can be displayed at a time. Therefore, one of the animations in the animated sprite is always considered the current animation. By default, the last animation you add to the sprite is the current animation. However, you can select any animation that the animated_sprite object contains to be the current animation.

Animations in LlamaWorks2D can loop. This enables your game to display motion that looks continuous. The walking farmer is an example. As the farmer walks across the screen, his animation loops over and over until he changes direction or thumps a mole. Then a different animation plays.

Note

Looping frames in forward and then reverse order is very common. When your game does this, it displays each frame of the sprite from first to last. It then displays them from last to first. This style of looping is usually what is used to make characters walk.


Your game can display animations that loop forward, reverse, or forward and then reverse. Forward looping means that the animation is displayed from the first frame to the last frame. The animation then starts over with the first frame. Reverse animation is just the opposite. The animation starts with the last frame and then displays each frame in reverse order until it gets to the first frame. It then starts over. This enables your game to do some nice effects very simply.

Cannonshoot Revisited

Let's dive into a sample program to see how animated sprites work. We'll use a revised version of the cannon simulator program from chapter 9. In this revised version, the program displays an animation of an explosion when the cannonball hits the ground.

If you look at the files that make up the CannonShoot program, you'll see that in addition to the LlamaWorks2D files, CannonShoot includes the files CannonShoot.h and CannonShoot.cpp. These files contain the logic of how the game works. However, CannonShoot.h and CannonShoot.cpp do not need to be changed in order to add animated sprites. Instead, the animated sprite is added to the cannonball object, which is in the files Cannonball.h and Cannonball.cpp. Let's begin by looking at the new version of Cannonball.h in Listing 13.1.

Listing 13.1 presents part of the file Cannonball.h. To save space, I've omitted the parts that have not changed since chapter 9. On line 48, you can see that there's a new data member of type animated_sprite. This is in addition to the sprite object declared on line 47. While the cannonball is in flight, the cannonball object displays the sprite object that contains the image of the ball. When the cannonball hits the ground, the cannonball object displays the animated_sprite object. This causes the animation of the explosion to be played, as shown inFigure 13.1.

Figure 13.1. When the cannonball is in flight (left frame), the program uses a sprite object because it doesn't change appearance. After the cannonball hits the ground (right frame), the program uses an animated sprite to display the explosion.


Note

You can find Cannonball.h on the CD in the folder Source\Chapter13\Prog_13_01.


Listing 13.1. Adding an animated sprite to the cannonball class

 1     class cannonball 2     { 3     public: 4         cannonball(); 5 6         bool LoadImage( 7             std::string fileName, 8             image_file_base::image_file_format fileFormat); 9 10        bool LoadExplosionAnimation(); 11 12        bool LoadHitSound( 13            std::string fileName); 14 15        void BallHit(); 16        bool DidBallHit(); 17 18        bool Move(); 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        int BitmapHeight(); 30        int BitmapWidth(); 31 32        void BoundingRectangle( 33             bitmap_region rectangle); 34        bitmap_region BoundingRectangle(); 35 36        void BitmapTransparentColor( 37            color_rgb theColor); 38        color_rgb BitmapTransparentColor(); 39 40        void Velocity( 41            vectorf direction); 42        vectorf Velocity(); 43 44        bool BallInFlight(); 45 46    private: 47        sprite theBall; 48        animated_sprite ballExplosion; 49        vectorf velocity; 50        float x, y; 51        bool ballInFlight; 52        bool ballHit; 53        sound ballHitSound; 54   }; 

In addition, there's a new data member on line 52 that keeps track of when the cannonball hits something. There is also a new member function in the cannonball class on line 16 of Listing 13.1. It tells other classes when the cannonball has hit something. The cannonball class contains a new member function for loading the explosion animation on line 10.

Most of the member functions of the cannonball do not need to change. However, those that do, along with the new member functions, are shown in Listing 13.2.

Listing 13.2. New and updated member functions for the cannonball class

 1     cannonball::cannonball() : 2         velocity(0.0f,0.0f) 3     { 4         ballInFlight = false; 5         ballHit = false; 6     } 7 8 9     bool 10    cannonball::LoadExplosionAnimation() 11    { 12        bool loadOK = true; 13        animation *theAnimation = new animation; 14 15        if (theAnimation!=NULL) 16        { 17            theAnimation>BitmapTransparentColor( 18                color_rgb(0.0f,1.0f,0.0f)); 19 20            loadOK = theAnimation>LoadImage( 21                "Expl1.bmp", 22                image_file_base::LWIFF_WINDOWS_BMP); 23        } 24        else 25        { 26            loadOK = false; 27            theApp.AppError(LWES_OUT_OF_MEMORY); 28        } 29 30        if (loadOK) 31        { 32            loadOK = theAnimation>LoadImage( 33                "Expl2.bmp", 34                image_file_base::LWIFF_WINDOWS_BMP); 35        } 36 37        if (loadOK) 38        { 39            loadOK = theAnimation>LoadImage( 40               "Expl3.bmp", 41               image_file_base::LWIFF_WINDOWS_BMP); 42        } 43 44        if (loadOK) 45        { 46            loadOK = theAnimation>LoadImage( 47                "Expl4.bmp", 48                image_file_base::LWIFF_WINDOWS_BMP); 49        } 50 51        if (loadOK) 52        { 53            loadOK = theAnimation>LoadImage( 54                "Expl5.bmp", 55                image_file_base::LWIFF_WINDOWS_BMP); 56        } 57 58        if (loadOK) 59        { 60            loadOK = theAnimation>LoadImage( 61                "Expl6.bmp", 62                image_file_base::LWIFF_WINDOWS_BMP); 63         } 64 65        if (loadOK) 66        { 67            loadOK = theAnimation>LoadImage( 68                "Expl7.bmp", 69                image_file_base::LWIFF_WINDOWS_BMP); 70        } 71 72        if (loadOK) 73        { 74            theAnimation>LoopAnimation( 75                true, 76                animation::FORWARD_THEN_REVERSE); 77        } 78 79        if (loadOK) 80        { 81            // Add the animation to the animated sprite. 82            ballExplosion.Animation(theAnimation); 83        } 84 85        return (loadOK); 86    } 87 88 89 90    void 91    cannonball::BallHit() 92    { 93        velocity.X(0.0f); 94        velocity.Y(0.0f); 95        ballHitSound.Play(); 96        ballInFlight = false; 97        ballHit = true; 98    } 99 100 101   bool 102   cannonball::DidBallHit() 103   { 104       return (ballHit); 105   } 106 107 108   bool 109   cannonball::Render() 110   { 111       bool renderOK = true; 112 113       if (ballInFlight) 114       { 115           renderOK = theBall.Render(); 116       } 117       else if (ballHit) 118       { 119           animation::animation_status explosionStatus = 120               ballExplosion.AnimationStatus(); 121           if ((explosionStatus != animation::ANIMATION_ ERROR) && 122               (explosionStatus != animation::AT_END_OF_ LOOP)) 123           { 124               ballExplosion.X(theBall.X()); 125               ballExplosion.Y(theBall.Y()); 126               renderOK = ballExplosion.Render(); 127           } 128           else if (explosionStatus == animation::AT_END_OF_ LOOP) 129           { 130               ballHit = false; 131               ballExplosion.ResetAnimation(); 132           } 133           else if (explosionStatus == animation::ANIMATION_ ERROR) 134           { 135               renderOK = false; 136           } 137       } 138 139       return (renderOK); 140   } 

Note

These member functions are from Cannonball.cpp on the CD in the folder Source\Chapter13\Prog_13_01.


Listing 13.2 starts with the updated constructor from the cannonball class. The difference between this version and the one in chapter 9 is that this one initializes the new ballHit data member. The constructor does not need to initialize the cannonball class's sprite or animated_sprite members. Their constructors are automatically called whenever the cannonball class constructor is called. As a result, the sprite and animated_sprite members are automatically initialized whenever the program creates a cannonball object.

The cannonball class's new LoadExplosionAnimation() function begins on line 9 of Listing 13.2. On line 13, LoadExplosionAnimation() uses the C++ new keyword to dynamically allocate an animation object. Like the animated_sprite class, the animation class is provided by LlamaWorks2D.

If the animation object is allocated successfully, the LoadExplosionAnimation() function sets its transparent color. Setting the transparent color for the animation sets the transparent color for all of its frames.

Next, by calling the animation class's LoadImage() function repeatedly, the LoadExplosionAnimation() function loads the individual frames of the animation. The animation class lets a program load as many frames as needed into an animation object. The program just keeps calling LoadImage() until all of the frames are loaded. The frames of the animation each have an index number. Numbering starts with 0, so the seven frames of the animation in Listing 13.2 are numbered 06.

On lines 7476, the LoadExplosionAnimation() function tells the animation object that its animation should loop. Because the loop direction is set to animation::FORWARD_THEN_REVERSE, the animation object first plays frames 06 of the animation. It then plays frames 5, 4, 3, 2, 1, and 0 to complete the loop. If the animation is allowed to continue looping, the animation object plays frames 1, 2, 3, 4, 5, and 6 on the next loop cycle.

The LoadExplosionAnimation() function ends by storing the animation object in the cannonball class's animated_sprite object on line 82.

The next function in Listing 13.2 is called BallHit(). It is almost the same as the version in chapter 9. However, the version shown here sets the ballHit data member to true. This is important because of the way animations work in LlamaWorks2D. When the explosion animation begins playing, it displays one of its animation frames during each screen animation frame. While the cannonball's explosion animation is playing, the program should not do anything else except play the explosion sound. That's exactly how this new version of CannonHit works. The ballHit member becomes false when the explosion animation is done. At that point, the program lets the player shoot the cannon again. The CannonShoot program calls the DidBallHit() function, which starts on line 102, to determine if the cannonball class's ballHit member is true or false.

Note

As long as the cannonball class's ballHit member is true, the program doesn't do anything but display successive frames of animation and play an explosion sound.


The final function in Listing 13.2 is the cannonball class's Render() function, which starts on line 108. This version of the Render() function renders the cannonball class's sprite object if the cannonball is in flight. That's shown on lines 113116. If the ballHit data member is true, the Render() function renders the cannonball class's animated_sprite object instead. It does this by first getting the status of the animated_sprite object's animation. If the animation is currently playing or has not yet started, it sets the x and y position of the animated_sprite. It then renders one frame of the cannonball's explosion animation.

In the CannonHit program, the idea is to have the animated_sprite object play its animation once, and then let the program continue. Therefore, the cannonball class's Render() function checks to see whether the animated_sprite object's animation is at the end of its loop on line 128. If it is, the Render() function sets ballHit to false. This tells the CannonHit program to stop rendering the explosion animation and continue. The Render() function also calls the animated_sprite class's Reset() function to reset the animation. This ensures that the animation will start playing from the first frame (frame 0) the next time the cannonball hits something.

So that's how the cannonball class works. Now let's examine how the cannon class uses the new version of the cannonball class. Listing 13.3 shows the LoadImage() and Render() functions from the cannon class, which were the only two that changed.

Listing 13.3. The updated functions from the cannon class

 1    bool cannon::LoadImage() 2    { 3        bool loadOK=true; 4        bitmap_region boundingRect; 5 6 7        theCannon.BitmapTransparentColor(color_rgb (0.0f,1.0f,0.0f)); 8        loadOK = 9            theCannon.LoadImage( 10               "cannon.bmp", 11               image_file_base::LWIFF_WINDOWS_BMP); 12 13       if (loadOK) 14       { 15           theBall.BitmapTransparentColor(color_rgb (0.0f,1.0f,0.0f)); 16           loadOK = 17               theBall.LoadImage( 18                   "cannonball.bmp", 19                   image_file_base::LWIFF_WINDOWS_BMP); 20       } 21 22       if (loadOK) 23       { 24           loadOK = theBall.LoadExplosionAnimation(); 25       } 26 27       if (loadOK) 28       { 29           boundingRect.top = 0; 30           boundingRect.bottom = theBall.BitmapHeight(); 31           boundingRect.left = 0; 32           boundingRect.right = theBall.BitmapWidth(); 33           theBall.BoundingRectangle(boundingRect); 34       } 35 36       return (loadOK); 37   } 38 39 40   bool cannon::Render() 41   { 42       bool renderOK = theCannon.Render(); 43 44       if (renderOK) 45        { 46       if ((theBall.DidBallHit()) || (theBall. BallInFlight())) 47       { 48                 renderOK = theBall.Render(); 49       } 50   } 51 52   return (renderOK); 53   } 

The cannon class's LoadImage() function starts on line 1 of Listing 13.3. It is extremely similar to the version presented back in chapter 9. The difference is on lines 2225, where it calls the cannonball class's LoadExplosionAnimation(). This enables all of the frames of the explosion animation to be loaded into the cannonball object.

Note

These member functions are from Cannon.cpp on the CD in the folder Source\Chapter13\Prog_13_01.


The Render() function for the cannon class renders the cannon as in the version from chapter 9. However, unlike the previous version, this version renders the cannonball object both if it is in flight and if its explosion animation is playing.

As you can see, the impact of using animated sprites is small on the cannon class. The cannonshoot class requires no changes to use animated sprites.

In general, you should use animated sprites in your games whenever you can. They add a greater feeling of liveliness and energy to your games. As a result, players typically feel that the game is more engaging than games that do not use animated sprites.



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