MULTIMEDIA

DirectX 10 : The 2D Resurgence - Getting the Sprites Moving

10/10/2012 7:25:52 PM
Getting the sprites to move around within the window isn’t very difficult. The position of each sprite, updated every frame, is stored in the posX and posY variables within the GameSprite structure. These variables are used during the Update function to correctly position the sprite on the screen. By manipulating the values in these variables before the Update function is called, the sprite’s position can be changed.

Changing the Sprite’s Position

Since each sprite can be moved around at a different rate, you’re going to add movement variables to the GameSprite structure so movement can be controlled on a per-sprite basis; these variables are called moveX and moveY.

The updated GameSprite structure is shown here:

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

The moveX and moveY variables store the current amount that each sprite should be moved in both the X and Y directions each frame. By changing the value in these two variables and how often these variables are applied, it will appear as though the sprites are moving around the screen.

The posX and posY variables, which control the actual sprite location, need to be updated each frame with the values contained in the moveX and moveY variables.

A function called MoveSprites, which not only updates the location of a sprite but makes sure it remains within the confines of the game window, is shown next.

/*******************************************************************
* MoveSprites
* Moves the sprites around the screen
* Inputs - void
* Outputs - void
*******************************************************************/
void MoveSprites()
{
    // Loop through and update all sprites
    for (int i = 0; i < MAX_SPRITES; i++)
    {
        // only update visible sprites
        if (sprites[i].visible)
        {
            // clamp the sprite position to the current window
            if ((sprites[i].posX > windowWidth) ||(sprites[i].posX <= 0))
            {
                sprites[i].moveX = -sprites[i].moveX;
            }
            // move the sprite in the X direction
            sprites[i].posX += sprites[i].moveX;
            
            // clamp the sprite position to the current window
            if ((sprites[i].posY > windowHeight) ||(sprites[i].posY <= 0))
            {
                sprites[i].moveY = -sprites[i].moveY;
            }
            // move the sprite in the Y direction
            sprites[i].posY += sprites[i].moveY;
        }
    }
}

					  

Using Sprites with Transparent Areas

Most game characters aren’t square, and they don’t normally take up the entire area of a sprite’s image. Up until now all the sprites you’ve been drawing were square and completely opaque, but take a look at sprites in use in games. They have transparent areas around the characters allowing you to see what’s behind them. Implementing transparent areas when drawing sprites isn’t difficult, but it does take some explanation to describe how it happens.

The blending state dictates how Direct3D draws overlapping objects. Without blending, the object closest to the viewer is completely opaque, obscuring anything beneath it. When blending is enabled, overlapping objects can be made to be partly translucent or have completely transparent areas. Blending works by merging the colors of multiple overlapping objects to determine the final output drawn to the screen.

Different areas of a sprite can contain alpha components, which affect the amount of transparency a sprite has. Areas with an alpha value of 0 have no transparency at all and are drawn completely opaque. Areas with an alpha value of 1 are drawn as see through. Alpha values between 0 and 1 give the sprite a partial see-through appearance.

Whether Direct3D pays attention to the alpha component of an image is determined by the current blend state.

Note

The alpha component is applied to a sprite’s image when the source texture is created in your art tool.


Creating and Setting a New Blend State

Before a new blend state can be applied, you have to describe how it will behave. The behavior of a blend state is defined by the criteria set in a D3D10_BLEND_DESC structure.

The D3D10_BLEND_DESC structure defines the criteria of how a blend operation between a source and a destination will work. The source is the object being applied, whereas the destination is the color already existing at that pixel. Blending works by merging the color values of the two. For instance, blending the red component of a source and destination will create a final red output. How these two components are blended is up to you.

The D3D10_BLEND_DESC structure is shown here:

typedef struct D3D10_BLEND_DESC {
    BOOL AlphaToCoverageEnable;
    BOOL BlendEnable[8];
    D3D10_BLEND SrcBlend;
    D3D10_BLEND DestBlend;
    D3D10_BLEND_OP BlendOp;
    D3D10_BLEND SrcBlendAlpha;
    D3D10_BLEND DestBlendAlpha;
    D3D10_BLEND_OP BlendOpAlpha;
    UINT8 RenderTargetWriteMask[8];
} D3D10_BLEND_DESC;

The BlendEnable variable is an array of BOOL values, each one representing a rendertarget. In most cases, you’ll be dealing with only the first item in the array.

The D3D10_BLEND_DESC structure splits the full RGBA components of an object into two pieces. The first piece, controlled by the SrcBlend and DestBlend variables, dictates how the RGB components are blended. The second piece, using SrcBlendAlpha and DestBlendAlpha, controls the blending of the A (alpha) component.

As I mentioned earlier, you get to control how the components are blended; this is controlled through the BlendOp and BlendOpAlpha variables. These variables, of type D3D10_BLEND_OP, allow you to add, subtract, or otherwise manipulate how the components are blended.

An example of a completed D3D10_BLEND_DESC structure is shown next. This one blends on the sprite’s alpha component only, allowing a sprite to be drawn with transparency.

// The variable that will contain the new blend state.
ID3D10BlendState*       pBlendState10 = NULL;

// Initialize the blend state for alpha drawing
D3D10_BLEND_DESC StateDesc;
ZeroMemory(&StateDesc, sizeof(D3D10_BLEND_DESC));
StateDesc.AlphaToCoverageEnable = FALSE;
StateDesc.BlendEnable[0] = TRUE;
StateDesc.SrcBlend = D3D10_BLEND_SRC_ALPHA;
StateDesc.DestBlend = D3D10_BLEND_INV_SRC_ALPHA;
StateDesc.BlendOp = D3D10_BLEND_OP_ADD;
StateDesc.SrcBlendAlpha = D3D10_BLEND_ZERO;
StateDesc.DestBlendAlpha = D3D10_BLEND_ZERO;
StateDesc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
StateDesc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
pD3DDevice->CreateBlendState(&StateDesc, &pBlendState10);

The creation of a new blend state is handled using the function CreateBlendState. The CreateBlendState function simply takes a pointer to the D3D10_ BLEND_DESC structure you created earlier and returns an ID3D10BlendState object.

ID3D10BlendState*   pBlendState10 = NULL;
// Create the new     blend state
pD3DDevice->Create BlendState(&StateDesc, &pBlendState10);

The ID3D10BlendState object then needs to be applied before it can take affect. Applying a blend state object happens using the OMSetBlendState function. OMSetBlendState uses the blend state you created, as well as two more parameters, a blend factor color and a sample mask.

The blend factor color is a default color used in the instance where either the source or destination color is set as D3D10_BLEND_BLEND_FACTOR or D3D10_BLEND_ INVBLEND_FACTOR.

The sample mask is used to determine which samples get updated when using multisampling. By default, this value should be set to 0xffffffff.

Because of the simple nature of the blend state, default values for the blend factor and sample mask are used.

FLOAT NewBlendFactor[4] = {0,0,0,0};
pD3DDevice->OMSetBlendState(pBlendState10, NewBlendFactor, 0xffffffff);

At this point the blend state is applied and active in the scene. When the sprite is drawn, any areas with an alpha value above 0 will appear to be slightly or completely transparent.

Storing the Current Blend State

Before you change the blend state, it is a good idea to save the previous state so it can be restored when the new one is no longer needed. The OMGetBlendState function is used to collect the current state. There are three parameters that OMGetBlendState requires.

The first parameter is a pointer to an ID3D10BlendState object. This object will be the one holding the original state.

The second parameter is a pointer to an array holding four float values. This array will be used to store the original blend factor.

The final parameter is an unsigned integer that will hold the original sample mask.

The following small example shows how to save the original blend state so it can be restored later.

ID3D10BlendState* pOriginalBlendState10 = NULL;
FLOAT OriginalBlendFactor[4];
UINT   OriginalSampleMask = 0;

// Save the current blend state
pD3DDevice->OMGetBlendState(&pOriginalBlendState10, OriginalBlendFactor,
&OriginalSampleMask);

Restoring the original blend state when you’re done is a simple matter of calling the OMSetBlendState function with the values you stored.

// Restore the previous blend state
pD3DDevice->OMSetBlendState(pOriginalBlendState10, OriginalBlendFactor,
OriginalSampleMask);

An updated Render function supporting sprites with transparent areas is shown next.

/*******************************************************************
* Render
* All drawing happens in the Render function
* Inputs - void
* Outputs - void
*******************************************************************/
void Render()
{
    FLOAT OriginalBlendFactor[4];
    UINT   OriginalSampleMask = 0;
    
    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);
            
            // Save the current blend state
            pD3DDevice->OMGetBlendState(&pOriginalBlendState10,
            OriginalBlendFactor, &OriginalSampleMask);
            
            // Set the blend state for alpha drawing
            if(pBlendState10)
            {
                FLOAT NewBlendFactor[4] = {0,0,0,0};
                pD3DDevice->OMSetBlendState(pBlendState10, NewBlendFactor,
                0xffffffff);
            }
            
            // Finish up and send the sprites to the hardware
            spriteObject->Flush();
            spriteObject->End();
        }
        // Restore the previous blend state
        pD3DDevice->OMSetBlendState(pOriginalBlendState10,
        OriginalBlendFactor, OriginalSampleMask);
        
        // display the next item in the swap chain
        pSwapChain->Present(0, 0);
    }
}

					  

Figure 1 shows a series of sprites with a transparent area at their center.

Figure 1. Multiple sprites with a transparent area.

Other  
 
Top 10
Review : Sigma 24mm f/1.4 DG HSM Art
Review : Canon EF11-24mm f/4L USM
Review : Creative Sound Blaster Roar 2
Review : Philips Fidelio M2L
Review : Alienware 17 - Dell's Alienware laptops
Review Smartwatch : Wellograph
Review : Xiaomi Redmi 2
Extending LINQ to Objects : Writing a Single Element Operator (part 2) - Building the RandomElement Operator
Extending LINQ to Objects : Writing a Single Element Operator (part 1) - Building Our Own Last Operator
3 Tips for Maintaining Your Cell Phone Battery (part 2) - Discharge Smart, Use Smart
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)
VIDEO TUTORIAL
- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 1)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 2)

- How to create your first Swimlane Diagram or Cross-Functional Flowchart Diagram by using Microsoft Visio 2010 (Part 3)
Popular Tags
Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Biztalk Exchange Server Microsoft LynC Server Microsoft Dynamic Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Indesign Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe After Effects Adobe Photoshop Adobe Fireworks Adobe Flash Catalyst Corel Painter X CorelDRAW X5 CorelDraw 10 QuarkXPress 8 windows Phone 7 windows Phone 8