MULTIMEDIA

Programming with DirectX : The 2D Resurgence - Handling Multiple Sprites

9/25/2012 9:27:35 PM
Drawing more than one sprite and keeping the code clean is going to require a change to the way the sprite information is stored. Previously, the sprite’s position and size were stored in a series of global variables.
float spritePosX = 320;
float spritePosY = 240;
float spriteWidth = 64;
float spriteHeight = 64;

Since you’re going to need this information for multiple sprites, this information is going to be moved to a structure.

Defining a GameSprite Structure

The GameSprite structure contains the sprite’s position and dimensions and is a much cleaner way of storing this information. Additionally, a new Boolean variable called visible is added. The visible variable is used to track whether the sprite is currently able to be seen on the screen. This enables the sprites to be shown or hidden. This is useful if sprites were being used for bullets or other items that have a limited lifetime.

// Sprite structure
typedef struct
{
    // sprite dimensions
    float width;
    float height;
    
    // sprite position
    float posX;
    float posY;
    
    BOOL visible;
} GameSprite;

Since you’ll need multiple sprites, an array of GameSprite structures should be created. The small snippet below creates an array of ten GameSprite structures.

#define MAX_SPRITES 10
GameSprite sprites[MAX_SPRITES] = {0};

Initializing the GameSprite Structures

Initializing the sprite data in the GameSprite structure is very similar to how you set up one sprite. Create a loop to allow all the sprites to be set up at one time.

In the following code, the ten GameSprite structures are initialized with a size of 64 × 64 and a random screen position. Each of the sprites also has its visible variable set to TRUE.

// Loop through and init the active sprites
for (int curSprite = 0; curSprite < MAX_SPRITES; curSprite++)
{
    
    // Set the width and height of the sprite
    sprites[curSprite].width = 64;
    sprites[curSprite].height = 64;
    
    // Create and set a random x, y position
    sprites[curSprite].posX = (float)(rand()%600);
    sprites[curSprite].posY = (float)(rand()%450);
    
    // This sprite is visible
    sprites[curSprite].visible = TRUE;
}

The Sprite Pool

Instead of associating a single D3DX10_SPRITE structure with each GameSprite, you’re going to employ a sprite pool. A sprite pool is an array of D3DX10_SPRITE structures where each structure is used on an as-needed basis. The structures are filled dynamically with information from the GameSprite objects each frame. These sprites are updated using the Update function. By using a sprite pool, the amount of dynamic allocations is kept down and places a restriction on the memory sprites can use.

Because the pool is pre-allocated, the size of the sprite pool array needs to be large enough to hold all the possible sprites that may be visible at one time. In the following code, there is enough space for thirty-two sprites to be allocated. An additional variable, numActiveSprites, is also being declared here. Since the number of sprites visible on the screen will be less than the number of available slots in the sprite pool, it is best to keep track of the number of sprites to be drawn. This variable comes in handy later when the function to draw the sprites is called.

// Maximum number of sprites possible in the pool
#define NUM_POOL_SPRITES 32

// Create the sprite pool array
D3DX10_SPRITE   spritePool[NUM_POOL_SPRITES];

// the number of active sprites
int numActiveSprites = 0;

Tip

Separating the game logic contained in the GameSprite from the rendering of the sprites in the pool allows for the drawing method to be changed without affecting the game logic.


Clearing the Sprites in the Pool

It is always a good idea to clear out all the items in the sprite pool to a default value before they’re used. Doing so keeps you from accidentally using garbage data.

// Loop through and set the defaults for the
// sprites in the pool
for (int i = 0; i < NUM_POOL_SPRITES; i++)
{
    // Texture for this sprite to use
    spritePool[i].pTexture = gSpriteTextureRV;
    spritePool[i].TextureIndex = 0;
    
    // top-left location in U,V coords
    spritePool[i].TexCoord.x = 0;
    spritePool[i].TexCoord.y = 0;
    
    // Determine the texture size in U, V coordinates
    spritePool[i].TexSize.x = 1;
    spritePool[i].TexSize.y = 1;
    
    spritePool[i].ColorModulate = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
}

All the sprites in the pool default to using the same texture in the pTexture variable.

Updating the Sprites

During the main loop, the information contained in each of the GameSprite structures needs to be copied to available sprites available in the sprite pool. Since the GameSprites do not have a specific sprite associated with them, they must be updated dynamically each frame. The UpdateScene function below sets up each sprite in the sprite pool with the correct information and keeps a running total of the number of sprites currently active.

In the following code sample, only sprites that have their visible variable set to TRUE are being updated.

/*******************************************************************
* UpdateScene()
* Updates the scene with the current sprite information
* Inputs - void
* Outputs - void
*******************************************************************/
void UpdateScene()
{
    D3DXMATRIX matScaling;
    D3DXMATRIX matTranslation;
    
    int curPoolIndex = 0;
    
    // Loop through the sprites
    for (int i = 0; i < MAX_SPRITES; i++)
    {
        // only update visible sprites
        if (sprites[i].visible)
        {
            // set the proper scale for the sprite
    D3DXMatrixScaling(&matScaling, sprites[i].width, sprites[i].height,
1.0f);
            // Move the sprite to spritePosX, spritePosY
            // SpriteWidth and SpriteHeight are divided by 2 to move the
            // translation point to the top-left sprite corner instead of
            // the center of the sprite.
            D3DXMatrixTranslation(&matTranslation,
                (float)sprites[i].posX + (sprites[i].width/2),
                (float)(windowHeight – sprites[i].posY - (sprites[i].
                height/2)), 0.1f);
            
            // Update the sprites position and scale
            spritePool[curPoolIndex].matWorld = matScaling * matTranslation;
            // Increment the pool index
            curPoolIndex++;
        }
    }
    
    // set the number of active sprites
    numActiveSprites = curPoolIndex;
}

					  

The UpdateScene function should be called before the Render function in the main game loop.

Drawing More Than One Sprite

Now that you have multiple sprites created and updating, how do you draw them? Well, you remember before where I mentioned that DrawSpritesImmediate was capable of drawing more than one sprite? The first parameter to the DrawSpritesImmediate function is a pointer to an array of D3DX10_SPRITE structures. Because the sprite pool is an array of this type, it can be passed directly into the DrawSpritesImmediate function.

Previously, a value of 1 had been passed into this function to draw only a single sprite. Now that there’s an array of sprites to draw, the number of active sprites should be passed in. The variable numActiveSprites contains the current number of valid sprites in the array.

The DrawSpritesImmediate function isn’t the only available function for drawing sprites. The ID3DX10Sprite object also includes the function DrawSpritesBuffered. The behavior of the two functions is slightly different.

The DrawSpritesImmediate function sends the sprites to the video hardware as soon as it is called.

The DrawSpritesBuffered function builds up a list of sprites to be drawn before actually sending them to the card. This is useful if you have functions where one or only a few sprites are needed each time. Once you’re ready to finally draw the sprites, calling the function Flush sends the sprites to the video card.

The following code sample shows an example usage of the DrawSpritesBuffered function.

/*******************************************************************
* Render
* All drawing happens in the Render function
* Inputs - void
* Outputs - void
*******************************************************************/
void Render()
{
    if (pD3DDevice != NULL)
    {
        
        // clear the target buffer
pD3DDevice->ClearRenderTargetView(pRenderTargetView, D3DXCOLOR(0.0f, 0.0f,
      0.0f, 0.0f));
        
        if (spriteObject != NULL)
        {
           HRESULT hr = spriteObject->SetProjectionTransform(&matProjection);
            
            // start drawing the sprites
            spriteObject->Begin(D3DX10_SPRITE_SORT_TEXTURE);
            
            // Draw all the sprites in the pool
            spriteObject->DrawSpritesBuffered(spritePool,
            numActiveSprites);
            
            // Finish up and send the sprites to the hardware
            spriteObject->Flush();
            spriteObject->End();
        }
        
        // display the next item in the swap chain
        pSwapChain->Present(0, 0);
    }
}
Other  
 
Video
Top 10
New App for Women ‘Remix’ Offers Fashion Advice & Style Tips
SG50 Ferrari F12berlinetta : Prancing Horse for Lion City's 50th
The latest Audi TT : New angles for TT
Era of million-dollar luxury cars
Game Review : Hearthstone - Blackrock Mountain
Game Review : Battlefield Hardline
Google Chromecast
Keyboards for Apple iPad Air 2 (part 3) - Logitech Ultrathin Keyboard Cover for iPad Air 2
Keyboards for Apple iPad Air 2 (part 2) - Zagg Slim Book for iPad Air 2
Keyboards for Apple iPad Air 2 (part 1) - Belkin Qode Ultimate Pro Keyboard Case for iPad Air 2
REVIEW
- First look: Apple Watch

- 3 Tips for Maintaining Your Cell Phone Battery (part 1)

- 3 Tips for Maintaining Your Cell Phone Battery (part 2)
Popular Tags
Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone