Sign in to follow this  
xM1k3x

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
		    UpdateScrollPosition();

        //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
void UpdateScrollPosition()
{
	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 this post


Link to post
Share on other sites
Quote:
Original post by I Am Legend
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


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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by daviangel
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.


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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by MSW
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




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

Thanks!

Share this post


Link to post
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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by MSW
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.


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 this post


Link to post
Share on other sites
Quote:
Original post by xM1k3x
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.


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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by bzroom
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].



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 this post


Link to post
Share on other sites
Quote:
Original post by bzroom
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."


Shouldn't that make it segfault?

Share this post


Link to post
Share on other sites
Quote:
Original post by MSW
Are 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 this post


Link to post
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 this post


Link to post
Share on other sites
Quote:
Original post by MSW
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--)


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 >.<

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this