2D Scrolling with DirectX

Introduction

Many 2D games require a scrolling background. Vertical and horizontal shooters tend to use it to portray movement over a terrain or space etc.

Simulating Motion

If we simply wish to give the impression of motion we can use a repeating texture.

We will use the features of the Direct3D sprite interfaces. They allow us to position our texture anywhere on the screen and also select a rectangular area of the texture to display. In order to simulate movement we will need to draw at least two sprites. Two sprites are needed if the texture is larger than the screen area. If it is smaller you will need more. The best way to explain this is by working through an example.

Example

In this example our screen area is 256 wide by 400 high. Our texture is 256 wide by 512 high. Since our texture is bigger than the screen area we need to select a rectangular area to draw. If we place the sprite at 0,0 we will need to draw a rectangular area from 512-400=112 to 512 down e.g.

RECT rct;
rct.left=0;
rct.right=256
rct.top=112;
rct.bottom=512;

D3DXVECTOR3 pos(0,0,0);
gSprite->Draw(texture, &rct, NULL,&pos,0xFFFFFFFF);

The texture with the area drawn to the screen outlined in black:

scrolling background

Note: this texture was obtained free from NVIDIA - they provide a number of high quality textures for developers.

To create a scrolling affect we simply need to change the rectangular area being rendered (keeping the position as 0,0,0). In this case I want the effect to simulate moving upward so I subtract from the top and bottom values of the rectangle every update. e.g.

rct.top--;
rct.bottom--;

 So after a few scrolls we may have:

scrolling background

This all works fine until we run out of texture (we reach rct.top equals 0):

scrolling background

We have run out of texture. However since our texture can be tiled we simply need to fill in the missing piece with the bottom part of the texture. This is why we need two sprites, one to render the top part and one to render the bottom part:

scrolling background

I have purposefully left a gap between the textures so that you can see the join.

Above the white line is one sprite and below it is the second. We will need to keep a track of a current texture scroll position, I will call this the offset. If we were to do this manually and we had reached offset=100 the code to draw the two sprites would be:

// top sprite
RECT rct;
rct.left=0;
rct.right=256;
rct.top=512-offset
rct.bottom=512;

D3DXVECTOR3 pos(0,0,0);
gSprite->Draw(texture, &rct, NULL,&pos,0xFFFFFFFF);

// bottom sprite
rct.top=0;
rct.bottom=400-offset

pos.y=offset;
gSprite->Draw(texture, &rct, NULL,&pos,0xFFFFFFFF);

The Algorithm

We now need to derive an algorithm from the above example rather than use all these hard coded numbers. So we need to define a few variables (here I have assigned them to the values used in the example):

int screenWidth=256;
int screenHeight=400;
int textureWidth=256;
int textureHeight=512;

Now to draw our screen we can create a function that takes the current offset position and draw our two sprites:

void RenderScreen(int offset)
{
     RECT rct;
     rct.left=0;
     rct.right=textureWidth;


     // Top sprite
     rct.top=textureHeight-offset;
     rct.bottom=textureHeight;

     D3DXVECTOR3 pos1(0,0,0);
     gSprite->Draw(texture, &rct, NULL,&pos1,0xFFFFFFFF);

     // Bottom sprite
     rct.top=0;
     rct.bottom=screenHeight-offset;

     D3DXVECTOR3 pos2(0,offset,0);
     gSprite->Draw(texture, &rct, NULL,&pos2,0xFFFFFFFF);
}

By incrementing offset each time this will create a scrolling background. Once offset equals the texture height we set it back to 0.

In the first example image above we can see that we do not need the second sprite at all since the texture is larger than the screen area and is not offset enough to cause a gap. We can trap this situation in our code and not render the second sprite.

If our texture is smaller than the height or the screen we will need more sprites to tile the area to be drawn. This makes the code a little bit more complex but the principle described above still holds true you just need to render more sprites.

For horizontal scrolling the principles are the same, simply manipulate the right and left rectangle values rather than the top and bottom.

The Demo

You can download a demo with source code showing all this in action. The demo can be downloaded here:

Summary

A lot of 2D games require a scrolling background to create the illusion of movement. With a seamless texture this can be done by using two sprites, one to fill in the top area of the screen and the other to fill in the bottom. This is achieved by managing the rectangular draw areas and sprite positions.

Further Reading

Notes about the Direct3D graphics for the above can be found via these links:

For books and links to places to get textures etc. please visit the resources section.



© 2004-2016 Keith Ditchburn