Tying it into the framework

We have covered a lot on the subject of drawing images with SDL but we have yet to tie everything together into our framework so that it becomes reusable throughout our game. What we will now cover is creating a texture manager class that will have all of the functions we need to easily load and draw textures.

Creating the texture manager

The texture manager will have functions that allow us to load and create an SDL_Texture structure from an image file, draw the texture (either static or animated), and also hold a list of SDL_Texture*, so that we can use them whenever we need to. Let's go ahead and create the TextureManager.h file:

  1. First we declare our load function. As parameters, the function takes the filename of the image we want to use, the ID we want to use to refer to the texture, and the renderer we want to use.
    bool load(std::string fileName,std::string id, SDL_Renderer* pRenderer);
  2. We will create two draw functions, draw and drawFrame. They will both take the ID of the texture we want to draw, the x and y position we want to draw to, the height and width of the frame or the image we are using, the renderer we will copy to, and an SDL_RendererFlip value to describe how we want the image to be displayed (default is SDL_FLIP_NONE). The drawFrame function will take two additional parameters, the current frame we want to draw and which row it is on in the sprite sheet.
    // draw
    void draw(std::string id, int x, int y, int width, int height, SDL_Renderer* pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);
    
    // drawframe
    
    void drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer* pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);
  3. The TextureManager class will also contain std::map of pointers to the SDL_Texture objects, keyed using std::strings.
    std::map<std::string, SDL_Texture*> m_textureMap;
  4. We now must define these functions in a TextureManager.cpp file. Let's start with the load function. We will take the code from our previous texture loading and use it within this load method.
    bool TextureManager::load(std::string fileName, std::string id, SDL_Renderer* pRenderer)
    {
      SDL_Surface* pTempSurface = IMG_Load(fileName.c_str());
    
      if(pTempSurface == 0)
      {
        return false;
      }
    
      SDL_Texture* pTexture = 
      SDL_CreateTextureFromSurface(pRenderer, pTempSurface);
    
      SDL_FreeSurface(pTempSurface);
    
      // everything went ok, add the texture to our list
      if(pTexture != 0)
      {
        m_textureMap[id] = pTexture;
        return true;
      }
    
      // reaching here means something went wrong
      return false;
    }
  5. When we call this function we will then have SDL_Texture that can be used by accessing it from the map using its ID; we will use this in our draw functions. The draw function can be defined as follows:
    void TextureManager::draw(std::string id, int x, int y, int width, int height, SDL_Renderer* pRenderer, SDL_RendererFlip flip)
    {
      SDL_Rect srcRect;
      SDL_Rect destRect;
    
      srcRect.x = 0;
      srcRect.y = 0;
      srcRect.w = destRect.w = width;
      srcRect.h = destRect.h = height;
      destRect.x = x;
      destRect.y = y;
    
      SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect, 
      &destRect, 0, 0, flip);
    }
  6. We again use SDL_RenderCopyEx using the passed in ID variable to get the SDL_Texture object we want to draw. We also build our source and destination variables using the passed in x, y, width, and height values. Now we can move onto drawFrame:
    void TextureManager::drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer *pRenderer, SDL_RendererFlip flip)
    {
      SDL_Rect srcRect;
      SDL_Rect destRect;
      srcRect.x = width * currentFrame;
      srcRect.y = height * (currentRow - 1);
      srcRect.w = destRect.w = width;
      srcRect.h = destRect.h = height;
      destRect.x = x;
      destRect.y = y;
    
      SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect, 
      &destRect, 0, 0, flip);
    }

    In this function, we create a source rectangle to use the appropriate frame of the animation using the currentFrame and currentRow variables. The source rectangle's x position for the current frame is the width of the source rectangle multiplied by the currentFrame value (covered in the Animating a sprite sheet section). Its y value is the height of the rectangle multiplied by currentRow – 1 (it sounds more natural to use the first row, rather than the zeroth row).

  7. We now have everything we need to easily load and draw textures throughout our game. Let's go ahead and test it out using the animated.png image. Open up Game.h. We will not need our texture member variables or the rectangles anymore, so delete any of the code dealing with them from the Game.h and Game.cpp files. We will however create two new member variables.
    int m_currentFrame;
    TextureManager m_textureManager;
  8. We will use the m_currentFrame variable to allow us to animate our sprite sheet and we also need an instance of our new TextureManager class (ensure you include TextureManager.h). We can now load a texture in the game's init function.
    m_textureManager.load("assets/animate-alpha.png", "animate", m_pRenderer);
  9. We have given this texture an ID of "animate" which we can use in our draw functions. We will start by drawing a static image at 0,0 and an animated image at 100,100. Here is the render function:
    void Game::render()
    {
    
      SDL_RenderClear(m_pRenderer);
    
      m_textureManager.draw("animate", 0,0, 128, 82, 
      m_pRenderer);
    
      m_textureManager.drawFrame("animate", 100,100, 128, 82, 
      1, m_currentFrame, m_pRenderer);
    
      SDL_RenderPresent(m_pRenderer);
    
    }
  10. The drawFrame function uses our m_currentFrame member variable. We can increment this in the update function like we did before, but we now do the calculation of the source rectangle inside the draw function.
    void Game::update()
    {
      m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
    }

    Now we can build and see our hard work in action.

Creating the texture manager

Using texture manager as a singleton

Now that we have our texture manager in place we still have one problem. We want to reuse this TextureManager throughout our game so we don't want it to be a member of our Game class because then we would have to pass it into our draw function. A good option for us is to implement TextureManager as a singleton. A singleton is a class that can only have one instance. This works for us, as we want to reuse the same TextureManager throughout our game. We can make our TextureManager a singleton by first making its constructor private.

private:

TextureManager() {}

This is to ensure that it cannot be created like other objects. It can only be created and accessed using the Instance function, which we will declare and define.

static TextureManager* Instance()
{
  if(s_pInstance == 0)
  {
    s_pInstance = new TextureManager();
    return s_pInstance;
  }

  return s_pInstance;
}

This function checks whether we already have an instance of our TextureManager. If not, then it constructs it, otherwise it simply returns the static instance. We will also typedef the TextureManager.

typedef TextureManager TheTextureManager;

We must also define the static instance in TextureManager.cpp.

TextureManager* TextureManager::s_pInstance = 0;

We can now use our TextureManager as a singleton. We no longer have to have an instance of TextureManager in our Game class, we just include the header and use it as follows:

// to load
if(!TheTextureManager::Instance()->load("assets/animate-alpha.png", "animate", m_pRenderer))
{
   return false;
}
// to draw
TheTextureManager::Instance()->draw("animate", 0,0, 128, 82, m_pRenderer);

When we load a texture in our Game (or any other) class we can then access it throughout our code.

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

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