Creating a Reusable Sprite Class

The Vector3 class will greatly simplify the code in the Sprite class, which otherwise would have to calculate things such as velocity on its own. In some cases, we’ll use Vector3 just for a simple x,y position. This might seem wasteful when the Vector3 class is so powerful, but it keeps our code uniform and predictable. I’m a firm believer in writing maintainable code rather than slightly faster code.

What do we want to do with sprites? When it comes right down to it, the answer is almost everything! Sprites are at the very core of 2D games, which is, of course, our focus here. We need to load and draw simple sprites (with no animation, just a single image), as well as the more complex animated sprites (with frames of animation). There is a need for both static and animated sprites in every game. In fact, most game objects are animated, which begs the questions how do we create an animation, and how do we animate a sprite? We’ll get to the first question later, when we create some complete game projects. We’ll get to the second question in just a moment.

Sprite.h

To answer the second question requires a bit of work. Let’s take a look at the Sprite class header first. This class is feature rich, meaning that it is loaded with features we haven’t even gone over yet and will not go over until we use some of these features in future chapters (for instance, collision detection, which is not covered until Chapter 9, “Physics”).

 #include "Advanced2D.h"
 #pragma once
 namespace Advanced2D {
     enum CollisionType {
         COLLISION_NONE = 0,
         COLLISION_RECT = 1,
         COLLISION_DIST = 2
     };

     class Sprite {
     private:
         bool visible;
         bool alive;
     int lifetimeLength;
         Timer lifetimeTimer;
         int objecttype;

         Vector3 position;
         Vector3 velocity;
         bool imageLoaded;
         int state;
         int direction;
     protected:
         Texture *image;
         int width,height;
         int animcolumns;
         int framestart,frametimer;
         int movestart, movetimer;
         bool collidable;
         enum CollisionType collisionMethod;
         int curframe,totalframes,animdir;
         double faceAngle, moveAngle;
         int animstartx, animstarty;
         double rotation, scaling;
         D3DXMATRIX matRotate;
         D3DXMATRIX matScale;
         void transform();
         D3DCOLOR color;

     public:
         //screen position
         Vector3 getPosition() { return position; }
         void setPosition(Vector3 position) { this->position = position; }
         void setPosition(double x, double y) { position.Set(x,y,0); }
         double getX() { return position.getX(); }
         double getY() { return position.getY(); }
         void setX(double x) { position.setX(x); }
         void setY(double y) { position.setY(y); }

         //movement velocity
         Vector3 getVelocity() { return velocity; }
         void setVelocity(Vector3 v) { this->velocity = v; }
         void setVelocity(double x, double y) { velocity.setX(x); velocity.setY(y); }

         //image size
         void setSize(int width, int height) { this->width = width; this->height =
height; }
         int getWidth() { return this->width; }
         void setWidth(int value) { this->width = value; }
         int getHeight() { return this->height; }
         void setHeight(int value) { this->height = value; }

         bool getVisible() { return visible; }
         void setVisible(bool value) { visible = value; }
         bool getAlive() { return alive; }
         void setAlive(bool value) { alive = value; }

         int getState() { return state; }
         void setState(int value) { state = value; }

         int getDirection() { return direction; }
         void setDirection(int value) { direction = value; }

         int getColumns() { return animcolumns; }
         void setColumns(int value) { animcolumns = value; }

         int getFrameTimer() { return frametimer; }
         void setFrameTimer(int value) { frametimer = value; }

         int getCurrentFrame() { return curframe; }
         void setCurrentFrame(int value) { curframe = value; }

         int getTotalFrames() { return totalframes; }
         void setTotalFrames(int value) { totalframes = value; }

         int getAnimationDirection() { return animdir; }
         void setAnimationDirection(int value) { animdir = value; }

         double getRotation() { return rotation; }
         void setRotation(double value) { rotation = value; }
         double getScale() { return scaling; }
         void setScale(double value) { scaling = value; }
         void setColor(D3DCOLOR col) { color = col; }

         int getMoveTimer() { return movetimer; }
         void setMoveTimer(int value) { movetimer = value; }

         bool isCollidable() { return collidable; }
         void setCollidable(bool value) { collidable = value; }
         CollisionType getCollisionMethod() { return collisionMethod; }
         void setCollisionMethod(CollisionType type) { collisionMethod = type; }

     public:
         Sprite();
         virtual ~Sprite();
         bool loadImage(std::string filename, D3DCOLOR transcolor = D3DCOLOR_
XRGB(255,0,255));
         void setImage(Texture *);
         void move();
         void animate();
         void draw();

     }; //class
}; //namespace

Sprite.cpp

That was a large header file, I’ll admit, but it was jam-packed with features that we’ll need later, and—as I mentioned back at the Vector3 listing—I prefer to give you the complete listing for a reusable class rather than modifying it. Here is the Sprite class implementation:

#include "Advanced2D.h"
namespace Advanced2D {
     Sprite::Sprite()
     {
         this->image = NULL;
         this->imageLoaded = false;
         this->setPosition(0.0f,0.0f);
         this->setVelocity(0.0f,0.0f);
         this->state = 0;
         this->direction = 0;
         this->width = 1;
         this->height = 1;
         this->curframe = 0;
         this->totalframes = 1;
         this->animdir = 1;
         this->animcolumns = 1;
         this->framestart = 0;
         this->frametimer = 0;
         this->animcolumns = 1;
         this->animstartx = 0;
         this->animstarty = 0;
         this->faceAngle = 0;
         this->moveAngle = 0;
         this->rotation = 0;
         this->scaling = 1.0f;
         this->color = 0xFFFFFFFF;
         this->movetimer = 16;
         this->movestart = 0;
         this->collidable = true;
         this->collisionMethod = COLLISION_RECT;
     }


     Sprite::~Sprite() {
         if (imageLoaded)
             delete image;
     }


     bool Sprite::loadImage(std::string filename, D3DCOLOR transcolor)
     {
         //if image already exists, free it
         if (imageLoaded && image != NULL) delete image;

         //create texture and load image
         image = new Texture();
         if (image->Load(filename,transcolor))
         {
             this->setSize(image->getWidth(),image->getHeight());
             imageLoaded = true;
             return true;
         }
         else
             return false;
     }


     void Sprite::setImage(Texture *image)
     {
         this->image = image;
         this->setWidth(image->getWidth());
         this->setHeight(image->getHeight());
         this->imageLoaded = false;
     }

     void Sprite::transform()
     {
         D3DXMATRIX mat;
         D3DXVECTOR2 scale((float)scaling,(float)scaling);
         D3DXVECTOR2 center((float)(width*scaling)/2, (float)(height*scaling)/2);
         D3DXVECTOR2 trans((float)getX(), (float)getY());
         D3DXMatrixTransformation2D(&mat,NULL,0,&scale,&center,(float)rotation,
&trans);
         g_engine->getSpriteHandler()->SetTransform(&mat);
     }

      void Sprite::draw()
      {
         //calculate source frame location
         int fx = (this->curframe % this->animcolumns) * this->width;
         int fy = (this->curframe / this->animcolumns) * this->height;
         RECT srcRect = {fx,fy, fx+this->width, fy+this->height};

         //draw the sprite frame
         this->transform();
         g_engine->getSpriteHandler()->Draw(
             this->image->GetTexture(),&srcRect,NULL,NULL,color);
     }

     void Sprite::move()
     {
         if (movetimer > 0) {

             if (timeGetTime() > (DWORD)(movestart + movetimer)) {
                 //reset move timer
                 movestart = timeGetTime();

                 //move sprite by velocity amount
                 this->setX(this->getX() + this->velocity.getX());
                 this->setY(this->getY() + this->velocity.getY());
             }
         }
         else {
             //no movement timer- -update at cpu clock speed
             this->setX(this->getX() + this->velocity.getX());
             this->setY(this->getY() + this->velocity.getY());
         }
     }

     void Sprite::animate()
     {
          //update frame based on animdir
          if (frametimer > 0) {
             if (timeGetTime() > (DWORD)(framestart + frametimer)) {
                 //reset animation timer
                 framestart = timeGetTime();
                 curframe + = animdir;

                 //keep frame within bounds
                 if (curframe < 0) curframe = totalframes-1;
                 if (curframe > totalframes-1) curframe = 0;
             }
         }
         else {
             //no animation timer- -update at cpu clock speed
             curframe + = animdir;
             if (curframe < 0) curframe = totalframes-1;
             if (curframe > totalframes-1) curframe = 0;
         }
    }
}

This is a pretty awesome sprite implementation, if I do say so myself. We have here the ability to render a sprite to any desired scale, at any angle of rotation, with timed animation, using alpha channel transparency and timer-based movement. Whew! Obviously, the Sprite class has a lot of functionality that we won’t be able to take advantage of for a while, but when we do, it will greatly simplify our future code!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset