# Vertical Scrolling Help

## Recommended Posts

Hey All! Ok so first off here is some background info: Language: C++ IDE: VS 2008 GameLIB: DirectX 9 March 2008 SDK Ok so I am making a vertical scrolling top down plane shooter. I am using a book entitled : Beginning Game Programming Second Edition by Johnathan S. Harbour. I have been able to implement the function shown in the book ,which I will show you guys in a sec, but the background scrolls downward and not upward like a top down plane shooter should. So basically I am still new to all this and was hoping that someone could help me out with this little dilema. I am certain that it is an easy fix its just that I am not seeing it: First off the array that holds the tile numbers:

int MAPDATA[MAPWIDTH*MAPHEIGHT] = {
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,
26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,
70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,
92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,
110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,
126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,
142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,
158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,
174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,
190,191,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,
42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,
63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,
84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,
104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,
120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,
136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,
152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,
168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,
184,185,186,187,188,189,190,191
};


Next the function calls:
//create the scroll buffer surface in memory, slightly bigger
//than the screen
result = d3ddev->CreateOffscreenPlainSurface(
SCROLLBUFFERWIDTH, SCROLLBUFFERHEIGHT,
D3DFMT_X8R8G8B8, D3DPOOL_DEFAULT,
&scrollbuffer,
NULL);

void Game_Run(HWND hwnd)
{
//make sure the Direct3D device is valid
if (d3ddev == NULL)
return;
Poll_Mouse();
Poll_Keyboard();

//after short delay, ready for next frame?
//this keeps the game running at a steady frame rate
if (GetTickCount() - start >= 30)
{

//reset timing
start = GetTickCount();

//move the sprite
Player.CallMovement();

//update the scrolling view

//start rendering
if (d3ddev->BeginScene())
{
//erase the entire background
d3ddev->StretchRect(tiles, NULL, backbuffer, NULL, D3DTEXF_NONE);

//start sprite handler
sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);

//create vector to update sprite position
D3DXVECTOR3 position((float)Player.x, (float)Player.y, 0);
//draw tiles onto the scroll buffer
DrawTiles();
//draw the scroll window onto the back buffer
DrawScrollWindow();

//draw the sprite
sprite_handler->Draw(
player_ship,
NULL,
NULL,
&position,
D3DCOLOR_XRGB(255,255,255));

sprite_handler->End();
//stop rendering
d3ddev->EndScene();
}

}

//display the back buffer on the screen
d3ddev->Present(NULL, NULL, NULL, NULL);

//check for escape key (to exit program)
if (Key_Down(DIK_ESCAPE))
DestroyWindow(hwnd);

}


And now the functions:
//This function updates the scrolling position and speed
{
SpeedY = 4;

//update horizontal scrolling position and speed
ScrollX += SpeedX;

if (ScrollX < 0)
{
ScrollX = 0;
SpeedX = 0;
}
else if (ScrollX > GAMEWORLDWIDTH - WINDOWWIDTH)
{
ScrollX = GAMEWORLDWIDTH - WINDOWWIDTH;
SpeedX = 0;
}
//update vertical scrolling position and speed
ScrollY += SpeedY;
if (ScrollY < 0)
{
ScrollY = 0;
SpeedY = 0;
}
if (ScrollY > GAMEWORLDHEIGHT - WINDOWHEIGHT)
{
ScrollY = GAMEWORLDHEIGHT - WINDOWHEIGHT;
SpeedY = 0;
}
}

//This function does the real work of drawing a single tile from the
//source image onto the tile scroll buffer. Parameters provide much
//flexibility.
void DrawTile(LPDIRECT3DSURFACE9 source,	// source surface image
int tilenum,				// tile #
int width,					// tile width
int height,					// tile height
int columns,				// columns of tiles
LPDIRECT3DSURFACE9 dest,	// destination surface
int destx,					// destination x
int desty)					// destination y
{

//create a RECT to describe the source image
RECT r1;
r1.left = (tilenum % columns) * width;
r1.top = (tilenum / columns) * height;
r1.right = r1.left + width;
r1.bottom = r1.top + height;

//set destination rect
RECT r2 = {destx,desty,destx+width,desty+height};

//draw the tile
d3ddev->StretchRect(source, &r1, dest, &r2, D3DTEXF_NONE);
}

//This function fills the tilebuffer with tiles representing
//the current scroll display based on scrollx/scrolly.
void DrawTiles()
{
int tilex, tiley;
int columns, rows;
int x, y;
int tilenum;

//calculate starting tile position
tilex = ScrollX / TILEWIDTH;
tiley = ScrollY / TILEHEIGHT;

//calculate the number of columns and rows
columns = WINDOWWIDTH / TILEWIDTH;
rows = WINDOWHEIGHT / TILEHEIGHT;

//draw tiles onto the scroll buffer surface
for (y=0; y<=rows; y++)
{
for (x=0; x<=columns; x++)
{
//retrieve the tile number from this position
tilenum = MAPDATA[((tiley + y) * MAPWIDTH + (tilex + x))];

//draw the tile onto the scroll buffer
DrawTile(tiles,tilenum,TILEWIDTH,TILEHEIGHT,16,scrollbuffer,
x*TILEWIDTH,y*TILEHEIGHT);
}
}
}

//This function draws the portion of the scroll buffer onto the back buffer
//according to the current "partial tile" scroll position.
void DrawScrollWindow()
{
//calculate the partial sub-tile lines to draw using modulus
int partialx = ScrollX % TILEWIDTH;
int partialy = ScrollY % TILEHEIGHT;

//set dimensions of the source image as a rectangle
RECT r1 = {partialx,partialy,partialx+WINDOWWIDTH,partialy+WINDOWHEIGHT};

//set the destination rectangle
//This line draws the virtual scroll buffer to the screen exactly as is,
//without scaling the image to fit the screen. If your screen does not
//divide evenly with the tiles, then you may want to scale the scroll
//buffer to fill the entire screen. It's better to use a resolution that
//divides evenly with your tile size.

//use this line for scaled display
//RECT r2 = {0, 0, WINDOWWIDTH-1, WINDOWHEIGHT-1};

//use this line for non-scaled display
RECT r2 = {0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1};

//draw the "partial tile" scroll window onto the back buffer
d3ddev->StretchRect(scrollbuffer, &r1, backbuffer, &r2, D3DTEXF_NONE);
}


I understand that this is alot of code but Im sure you would need to know what all these functions looked like in order to understand how everything works. If anyone could help me and tell me how to make the background scroll upwards instead of downwards that would be amazing! Thanks! Mike

##### Share on other sites
I didn't read the whole lot of the code but why do you set the speed to 0 if the Y position is 0. I don't think that should be there, make it your scroll speed

##### Share on other sites
Quote:
 Original post by I Am LegendI didn't read the whole lot of the code but why do you set the speed to 0 if the Y position is 0. I don't think that should be there, make it your scroll speed

I believe that just stop the window so it doesnt go past the background then you would just have black. However I did change it and everything still worked exactly the same.

##### Share on other sites
You do know that looking at the screen +Y is down and -Y so your code is doing exactly what you are telling it to scroll downwards and not upwards!
So an easy fix would be to change the sign if you want it to go the other way.

##### Share on other sites
Quote:
 Original post by daviangelYou do know that looking at the screen +Y is down and -Y so your code is doing exactly what you are telling it to scroll downwards and not upwards!So an easy fix would be to change the sign if you want it to go the other way.

Yes I know how the x and y axises work on the screen but simply changing it to - y isnt enough because then the screen doesnt move it is already at the top. I need to know how to make it start at the bottom.

##### Share on other sites
Maybe try flipping your drawing routine to start at the bottom of the screen and work its way up.

change this:
//draw tiles onto the scroll buffer surface
for (y=0; y<=rows; y++)

to this:
//draw tiles onto the scroll buffer surface
for (y=rows; y<=0; y--)

That will invert the map data as well so the first row will be at the bottom of the map with each row atop that.

But if that isn't what you are looking for then figure out how tall the map is in pixel space (Map_Y * Tile_height) subtract the screen display height in pixels (-480 if 640X480 display) and start the camera there

##### Share on other sites
Quote:
 Original post by MSWMaybe try flipping your drawing routine to start at the bottom of the screen and work its way up.change this: //draw tiles onto the scroll buffer surface for (y=0; y<=rows; y++) to this: //draw tiles onto the scroll buffer surface for (y=rows; y<=0; y--)That will invert the map data as well so the first row will be at the bottom of the map with each row atop that.But if that isn't what you are looking for then figure out how tall the map is in pixel space (Map_Y * Tile_height) subtract the screen display height in pixels (-480 if 640X480 display) and start the camera there

I am trying to better understand your last statement could you perhaps elaborate like with pseudocode?

Thanks!

##### Share on other sites
Where are ScrollX and ScrollY declared, and what are they initialized with? I would really like to see that. It seems that everything should work, you just need to make SpeedY negative, and set ScrollX and ScrollY to where they would be at the bottom of the map. That'll be somthing like ScrollY = world_height - screen_height, in either tile coords or pixels.

It appears that your scroll speeds and positions are globals. That's really not necessary.

##### Share on other sites
Okay lets say you have a set of tiles 32 by 32 pixels. And you have a map that is 20 tiles wide by 800 tiles tall.

So if you were to draw out the map completely, each tile in the correct place. The picture you end up with would be 640 pixels wide (32 tile pixel width * 20 tiles wide) and 25,600 pixels tall (32 * 800).

Now if your game screen is 640 pixels wide by 480 pixels tall and you draw the huge map image at 0,0. It would only draw the top area of the map.

However if you told it to draw the huge map image at 0,-25,120 (25,600 pixels - 480 screen pixels) then it would draw the bottom of the image onscreen.

Then if you were to draw the same image again at 0,-25,119 it would appear to have moved down one row of pixels. Then again at 0,-25,118 it would move down another row of pixels. Then draw it at 0,-25,117, another row...and on and on until you draw it at 0,0.

This is NOT to say that you need to create a huge image of your map in memory. Rather think of the map and tiles as a form of image compression. Thats basicly all it is...one big image that has been reduced to a number of identical same size tiles and a simple data structure (the map) describeing how to put the image back together again.

Except we don't need to draw the whole image, only the stuff thats onscreen. We do that by figureing out what section of the imaginary huge image would be onscreen.

Or screens upper left corner is at 0,0. The bottom right corner is at 639 and 479 (640 pixels wide by 480 tall includeing 0,0).

Or imaginary huge image's upper left corner is 0,0. And the lower right is 639 and 25,599.

So if we try to draw that huge image at 0, -25,120. We are actualy inverting the sign and trying to draw the huge image pixel at 0, 25,120 onto screen pixel at 0,0. In effect we are going to draw from 0, 25,120 to 639, 25,599 (0+ 639 screen width and 25,120 + 479 screen height)

Now you know that you can calculate the map location of these two points by divideing by the tile size (32 pixels). So 0, 25,120 upper left corner = map location 0,785 (truncate the remainder) and the lower right corner of 639, 25,599 = map location map location 19, 799 (again truncated remainder).

So if we set up our loop to draw map tiles from 0,785 to 19,799 we will have the correct section of map. So how much offset do we need inorder to draw the tiles correctly?

Thats easy, simply take the location we were to draw this imaginary huge image at ( 0, -25,120 ) and use the MOD function with the tile size ( Offset_X = 0 MOD 32 = 0 and Offset_Y = -25,120 MOD 32 = 0 ). So we draw the first tile (map location 0,785) at screen location 0 + Offset_X, 0 + Offset_Y. Then next tile (map location 1, 785) is drawn at 0 + tile_width + Offset_X , 0 + Tile_height + Offset_Y. simply moveing over one map location and tile_width pixels each time until we move down another map location collom and tile_height to draw the next row of tiles. And yadda, yadda, yadda until the whole visable section of the map is drawn.

##### Share on other sites
Quote:
 Original post by MSWOkay lets say you have a set of tiles 32 by 32 pixels. And you have a map that is 20 tiles wide by 800 tiles tall.So if you were to draw out the map completely, each tile in the correct place. The picture you end up with would be 640 pixels wide (32 tile pixel width * 20 tiles wide) and 25,600 pixels tall (32 * 800).Now if your game screen is 640 pixels wide by 480 pixels tall and you draw the huge map image at 0,0. It would only draw the top area of the map.However if you told it to draw the huge map image at 0,-25,120 (25,600 pixels - 480 screen pixels) then it would draw the bottom of the image onscreen.Then if you were to draw the same image again at 0,-25,119 it would appear to have moved down one row of pixels. Then again at 0,-25,118 it would move down another row of pixels. Then draw it at 0,-25,117, another row...and on and on until you draw it at 0,0.This is NOT to say that you need to create a huge image of your map in memory. Rather think of the map and tiles as a form of image compression. Thats basicly all it is...one big image that has been reduced to a number of identical same size tiles and a simple data structure (the map) describeing how to put the image back together again.Except we don't need to draw the whole image, only the stuff thats onscreen. We do that by figureing out what section of the imaginary huge image would be onscreen.Or screens upper left corner is at 0,0. The bottom right corner is at 639 and 479 (640 pixels wide by 480 tall includeing 0,0).Or imaginary huge image's upper left corner is 0,0. And the lower right is 639 and 25,599.So if we try to draw that huge image at 0, -25,120. We are actualy inverting the sign and trying to draw the huge image pixel at 0, 25,120 onto screen pixel at 0,0. In effect we are going to draw from 0, 25,120 to 639, 25,599 (0+ 639 screen width and 25,120 + 479 screen height)Now you know that you can calculate the map location of these two points by divideing by the tile size (32 pixels). So 0, 25,120 upper left corner = map location 0,785 (truncate the remainder) and the lower right corner of 639, 25,599 = map location map location 19, 799 (again truncated remainder).So if we set up our loop to draw map tiles from 0,785 to 19,799 we will have the correct section of map. So how much offset do we need inorder to draw the tiles correctly?Thats easy, simply take the location we were to draw this imaginary huge image at ( 0, -25,120 ) and use the MOD function with the tile size ( Offset_X = 0 MOD 32 = 0 and Offset_Y = -25,120 MOD 32 = 0 ). So we draw the first tile (map location 0,785) at screen location 0 + Offset_X, 0 + Offset_Y. Then next tile (map location 1, 785) is drawn at 0 + tile_width + Offset_X , 0 + Tile_height + Offset_Y. simply moveing over one map location and tile_width pixels each time until we move down another map location collom and tile_height to draw the next row of tiles. And yadda, yadda, yadda until the whole visable section of the map is drawn.

Thanks so much MSW and theOcelot!

You guys have both helped me tremendously and I now have it scrolling correctly. Only one thing seems weird once the window reaches the top of the map it begins to flash and look weird. Any ideas as to why? Like why doesnt it just stop moving and thats it?

Also what would be the best way to make the map go on for a while at least 2 mins worth of scrolling?

Thank you all so much I am learning alot from you guys.

##### Share on other sites
Quote:
 Original post by xM1k3xYou guys have both helped me tremendously and I now have it scrolling correctly. Only one thing seems weird once the window reaches the top of the map it begins to flash and look weird. Any ideas as to why? Like why doesnt it just stop moving and thats it?Also what would be the best way to make the map go on for a while at least 2 mins worth of scrolling?Thank you all so much I am learning alot from you guys.

Is it flashing to black, up and down, what? Weird in what way? Up and down would probably be because of the way you reset your scroll and speed variables. Black, or anything else, I don't know.

Timing: determine how many animation frames you want the scrolling to last (2mins * framesPerSecond), then divide the number of pixels of the level by that number. That's how many pixels you move per frame.

##### Share on other sites
Or you'll have to loop the data when you're accessing the tile map. If the currently requested index is beyond the tile map, it is wrapped around to the begining, so your data will scroll continuously for ever.

For example:
const int GRIDWIDTH = 20;const int GRIDHEIGHT= 20;const char TileMap [GRIDWIDTH * GRIDHEIGHT] = { . . . };char GetTile(int width, int height){  //the following line will make sure actualHeight is always < GRIDHEIGHT  // no matter how large height may get. look up "modulus operator"  int actualHeight = height % GRIDHEIGHT;  //locate tile in map  return TileMap[GRIDWIDTH * actualHeight + width];}

It is probably flashing because once you go past the ends of the buffer, nothing tells it to stop. So then you start accesing invalid parts of memory which will have "random" data values. As your code interprets this it would probably appear to "flicker."

The solution I showed you should fix that, provided that width was passed within bounds, [0, GRIDWIDTH-1].

##### Share on other sites
Quote:
 Original post by bzroomOr you'll have to loop the data when you're accessing the tile map. If the currently requested index is beyond the tile map, it is wrapped around to the begining, so your data will scroll continuously for ever.For example:const int GRIDWIDTH = 20;const int GRIDHEIGHT= 20;const char TileMap [GRIDWIDTH * GRIDHEIGHT] = { . . . };char GetTile(int width, int height){ //the following line will make sure actualHeight is always < GRIDHEIGHT // no matter how large height may get. look up "modulus operator" int actualHeight = height % GRIDHEIGHT; //locate tile in map return TileMap[GRIDWIDTH * actualHeight + width];}It is probably flashing because once you go past the ends of the buffer, nothing tells it to stop. So then you start accesing invalid parts of memory which will have "random" data values. As your code interprets this it would probably appear to "flicker."The solution I showed you should fix that, provided that width was passed within bounds, [0, GRIDWIDTH-1].

Quick question you didnt show exactly what you were passing through to the getTile function I am assuming you are passing in the width and height of the tile itself is this correct?

Thanks

##### Share on other sites
Quote:
 Original post by bzroomIt is probably flashing because once you go past the ends of the buffer, nothing tells it to stop. So then you start accesing invalid parts of memory which will have "random" data values. As your code interprets this it would probably appear to "flicker."

Shouldn't that make it segfault?

##### Share on other sites
Are you double buffering and not clearing buffers before useing them?

##### Share on other sites
Quote:
 Original post by MSWAre you double buffering and not clearing buffers before useing them?

Im not really sure actually if thats whats happening. Is the flickering a problem that could be caused by not clearing the buffers?

Sorry if I sound newbish its because I am , and I am just learning all this stuff.

##### Share on other sites
You really need to describe exactly how the screen is flickering and looking wierd. And for good measure, post your latest version of UpdateScrollPostion(). It's most likely that when you fixed the scroll direction, you broke the bits that check for the edges of the world, or something like that.

It's unlikely that a double buffering issue would crop up just now. It is possible, stranger things have happened to me, but I don't think you need to worry about it.

Quote:
 Quick question you didnt show exactly what you were passing through to the getTile function I am assuming you are passing in the width and height of the tile itself is this correct?

It appears he actually meant the coordinates of the tile. So in DrawTiles() you would do something like "tilenum = GetTile(x, y)".

##### Share on other sites
http://lazyfoo.net/SDL_tutorials/lesson21/index.php

This helped me out tons with 2d scrolling. It's written for SDL but it should at least point you in the right direction for DirectX

##### Share on other sites
Quote:
 Original post by MSWchange this: //draw tiles onto the scroll buffer surface for (y=0; y<=rows; y++) to this: //draw tiles onto the scroll buffer surface for (y=rows; y<=0; y--)

I'm coming in late on this one, but since no one else has pointed it out, it will work better if you make it "y>=0" in the second sample >.<

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628334
• Total Posts
2982147

• 9
• 24
• 9
• 9
• 13