• Advertisement
Sign in to follow this  

Scrolling in a 2D RTS game - How to?

This topic is 4740 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Guys, I've been wondering. I'm creating a 2d rts game, and i have all the basic stuff: I have all the tiles i need and the map array ready and all that, but I've been wondering.. How do I program scrolling? I mean, the idea I had (For example, when I'm scrolling right: ) was to 'chop' a 'pixel-size line' of the next tile to be drawn while moving all the drawn map a pixel to the left, and then draw the next 'line' until I finally draw all that tile, and so on and on. The thing is that I don't really know how to 'chop' a picture I have in memory. Can someone please help me on how to do that? Or if you have a better idea for scrolling, I'll thank you even more. I'm using SDL, btw. Oh yeah, and one small thing - I want to enter a 'quote' of a piece of code here in the forum, how do I do that (prolly [thing] and [/thing], but don't know what is that 'thing'). Thanx :) [Edited by - Twiggy on January 4, 2005 7:24:35 AM]

Share this post


Link to post
Share on other sites
Advertisement
Are you using the 2D SDL API or OpenGL through SDL?

If you are using the 2D SDL api: Basically what i did a long time ago using directDraw

Create a ClipBlit function that takes in: Image - Surface, DestRect - Rectangle, SourceRect - Rectangle.

Ok now i'm assuming you are blitting onto the backbuffer. Get the Rectangle associated with the backbuffer and test the DestRectangle against it as such:


//check if the destination rectangle is going off the top of the screen
if (DestRect.y < BackRect.y)
{
float difference = BackRect.y - DestRect.y;
DestRect.y += difference;
SourceRect.y += difference;
}


The code will adjust the destination and source rectangles so that only the visible parts are copied from the source surface and the destionation rectnagle is fully on the screen. Check it out on paper and work out the tests for the other three sides of the screen.

they are [.code.] and [./code.] tags, remove the dots.

Share this post


Link to post
Share on other sites
In addition to what Ilici said, if you use tiles you basically do the same (e.g. figure out which is the first visible tile and render the portion of the map from there). For smooth (pixel-wise) scrolling a little more effort is required, but it's the same principle.
BTW you can also use [.source] [./source.] tags (without the dots again) to post syntax-highlighted code.

HTH,
Pat.

Share this post


Link to post
Share on other sites
Thank you very much, but I did not understand. I am not such an experienced proggrammer, and a very unexperienced SDL progger.

Darookie, what did you mean by...
Quote:
figure out which is the first visible tile and render the portion of the map from there

And Ilici, the thing I needed to know was not how to know how many pixels to draw, but was how to take the portion of the tile i need and draw it.

I basically need instructions on how the system works.

Thanx again.

Share this post


Link to post
Share on other sites
So you use tiles, right? Then you surely have an array of some sort that tells you where tiles are located on the map.
Say you have a map of 64x64 tiles and each tile is 32x32 pixels in size.
Your whole map is therefore 64*32 x 64*32 = 2048x2048 pixels in size and this doesn't fit on your average screen.

Now let's say your output window is 640x480 pixels in size. This leaves you with a max of 640/32 x 480/32 = 20x15 visible tiles.

If you set your initial upper-left tile to be tile (0,0) in your map, you just draw the first 20 tiles for each of the first 15 rows of you map.
Now if you scroll tile-by-tile (which I'd recommend to start with), you just change this first upper-left tile you draw:



const int MAP_SIZE_X = 64;
const int MAP_SIZE_Y = 64;

// let's suppose this array holds the Ids of your tiles in the map
int tileMap[MAP_SIZE_Y][MAP_SIZE_X];

// these constants hold the number of visible tiles of the map
const int visibileTilesX = WINDOW_WIDTH / TILE_SIZE_X;
const int visibileTilesY = WINDOW_WIDTH / TILE_SIZE_X;

// these are your start positions for simple tile-by-tile scrolling
int startTileX = 0;
int startTileY = 0;

void drawTile(int tileNumber, int screenX, int screenY) {
// blit tile image here
}

void drawMap() {

// tile screen position in pixels
int screenY = 0;

for (int mapY = startTileY; mapY < visibleTilesY; ++mapY) {

// tile screen position in pixels
int screenX = 0;

for (int mapY = startTileX; mapX < visibleTilesY; ++mapY) {

drawTile(map[mapY][mapX], screenX, screenY);

// set position to next tile
screenX += TILE_SIZE_X;
}

// set position to next row on screen
screenY += TILE_SIZE_Y;
}
}

// scroll the map by the given amount of tiles
void scrollMap(int tilesX, int tilesY) {

// update start positions
startTileX += tilesX;
startTileY += tilesY;

// make sure you don't get out of the map boundaries
if (startTileX < 0)
startTileX = 0;

else if (startTileX > MAP_SIZE_X - visibleTilesX)
startTileX = MAP_SIZE_X - visibleTilesX;

if (startTileY < 0)
startTileY = 0;

else if (startTileY > MAP_SIZE_Y - visibleTilesY)
startTileY = MAP_SIZE_Y - visibleTilesY;
}



Hope that helps,
Pat.

Share this post


Link to post
Share on other sites
It's not too difficult if you keep your wits about you. Once you've made it scroll a whole tile at a time at first, add a second pair of coordinates to keep track of the position of the top left corner of the viewscreen in the top left tile, and when you draw the tiles shift the whole lot up and to the left by that amount.

Share this post


Link to post
Share on other sites
Basically, you change startX and startY to where the upperleft corner of the screen is on your map. So, you can move these around to where you want them.

My method of clipping tiles was simply to draw one row off screen, sort of as a buffer. It keeps the tile in memory and displays part of it if the screen doesn't show a whole tile.


int startCol = (startX/tileSize) - 1;
int endCol = (screenWidth/tileSize) + 1;
int startRow = (startY/tileSize) - 1;
int endRow = (screenHeight/tileSize) + 1;

for(int i = startCol; i <= endCol; i++)
{
for(int j = startRow; j <= endRow; j++)
{
drawImage(startX + (i * tileSize), startY + (j * tileSize), tileMap[j].getImage());
}
}

//cleanup code here





NOTE: This isn't actual working code. Just typed it up as a demonstration of the row/col buffer. Probably doesn't even work. If so, let me know.

Share this post


Link to post
Share on other sites
Thanx!

But dudes, don't forget that I have to draw sprites on the tiles (Soldiers and such), that is another problem.

Share this post


Link to post
Share on other sites
SDL does all the clipping for you. If you have a 32x32 tile and draw it at a destination rectangle of -20,-10,32,32 then it will clip the image for you (cannot place a clipper on a primary/flipped surface in directdraw). This makes it very simple to smooth scroll. Here is a working tilemap program in SDL that has smooth scrolling and some simple collision detection. If the player appears not to move then your computer is too fast! :) Just increase the sprites speed in move_sprite by increments of 50 until it works.

Hope that helps.

Share this post


Link to post
Share on other sites
You simply do a Z-Sort for the soldiers on tiles. You draw all the tiles(bottom level), draw ground units second(second level), then aireal units(third level) if you have them. You can hardcode the tile rendering as the bottom level(I would imagine) because it's your background. As far as individual units, when you process their animations and get their current image, send an instance(or pointer) to a linked list. If they're to be drawn first, add them to the front. If they're to be drawn last, add them to the back. Then you simply iterate through and remove units from the list as you draw them.

Share this post


Link to post
Share on other sites
Quote:
Original post by evillive2
SDL does all the clipping for you. If you have a 32x32 tile and draw it at a destination rectangle of -20,-10,32,32 then it will clip the image for you (cannot place a clipper on a primary/flipped surface in directdraw). This makes it very simple to smooth scroll. Here is a working tilemap program in SDL that has smooth scrolling and some simple collision detection. If the player appears not to move then your computer is too fast! :) Just increase the sprites speed in move_sprite by increments of 50 until it works.

Hope that helps.


Love how you made it all in one file to make it easy to understand. love it. anyway I was just wondering on your web site, do you have the source files of the RPG map?

Share this post


Link to post
Share on other sites
Quote:
Original post by Thanhda
Quote:
Original post by evillive2
SDL does all the clipping for you. If you have a 32x32 tile and draw it at a destination rectangle of -20,-10,32,32 then it will clip the image for you (cannot place a clipper on a primary/flipped surface in directdraw). This makes it very simple to smooth scroll. Here is a working tilemap program in SDL that has smooth scrolling and some simple collision detection. If the player appears not to move then your computer is too fast! :) Just increase the sprites speed in move_sprite by increments of 50 until it works.

Hope that helps.


Love how you made it all in one file to make it easy to understand. love it. anyway I was just wondering on your web site, do you have the source files of the RPG map?


Sorry, I think that went away with the last reformat of my hard drive. It was written in directx anyway and the initialization code in that was almost as long as the entire SDL demo anyway. The only useful bits in that would have been the map file stuff which was just parsing a few lines of text and tile index numbers into the tilemap array. I have since ditched my directdraw code for SDL for a few reasons:
1. The initialization of DirectX code is just a pain in the butt.
2. SDL is cross platform. Not usre when I will want to use this but it can never hurt.
3. I eventually want to move onto 2d in 3d via OpenGL when I get a better computer and can see the difference (3d is sloooooooow on my end).

I am glad that I tried DirectDraw before SDL because it makes me appreciate some things on both sides. While SDL is much simpler to get something on the screen, DirectDraw IMHO is actually a more powerful 2d graphics API because it is platform specific and doesn't have to "meet in the middle" to stay cross platform. On the other hand, SDL with OpenGL is much more powerful with todays video cards than any 2d API out there offering hardware acceleration for things such as blending, rotation and stretching which are dog slow in software and hardware acceleration is just not in the cards for 2d anymore.

At any rate, I am glad the demo helped a little. If you have any questions about it, just e-mail me or post here, I usually try to get back to people when I can :)

Share this post


Link to post
Share on other sites
yeah, thats the main reason i wanted the source, because of the map files. but heh i'm sure i can find a way of doing it. (being a bit lazy =P). anyway yeah i feel ya, i started out with DX8.1 myself(such a pain in the ass with them getting rid of directdraw). but i too moved from DX to SDL to one day get good at OGL. also the fact that its cross platform. but yeah that code help quite a bit. Anyway i wanted to ask you if you know how to make images or fade in or out? or even play mpgs in sdl?

BTW, just wondering, have you build any games in SDL yet? something with more then just a tile base system.

Share this post


Link to post
Share on other sites
Quote:
Original post by evillive2
If you have any questions about it, just e-mail me or post here, I usually try to get back to people when I can :)


Hey, I have a question about your example. I was having problems with my attempt crashing when (i think) the map drawing function was trying to draw an extra tile to the right. It happened when I went right quite far. I looked at your example and the only real difference was the get_tile function which basicaly seemed to do this:


if ( tile_x >= MAPWIDTH )
tile_x = tile_x % MAPWIDTH;
if ( tile_x >= MAPHEIGHT )
tile_x = tile_x % MAPHEIGHT;



So I tried adding that, and it has fixed it, the program no longer crashed. The thing is I don't understand why it works. I don't see how using the % thing helps because surely that gets the remainder from "tile_x / MAPWIDTH", and say if tile_x is 30 and MAPWIDTH is 30 then it would set it to 0?

Am I right in saying it draws a tile off the screen to the right and if its at the end, it tries to draw a tile that doesn't exist so your function just overwrites it with somthing to prevent it from crashing? If so why don't you just do "if ( tile_x >= MAPWIDTH ) tile_x = 0" or somthing similar?

Thanks

Share this post


Link to post
Share on other sites
I used the mod (%) operator in order to simulate the continuous map. Try outputing the values that go into it an then come out of it to a log file or to the console and you might understand how it works a little better. Basicly the mod operator does 2 things for me here:

1) it acts as a bounds checker by constraining the result to a value in a certain range.
2) it also happens to give me the propper value of the tile I want to draw.

So the mod operator kills 2 birds with one stone here. However, on to why your version crashed. I am assuming you did not want the map to contiue to wrap around. The trick here is to never allow the camera to move less than 0 or greater than the map width in pixels MINUS the screen width in pixels. Otherwise you will go out of bounds on your tilemap array. You will also need to add a check in there for if the get_tile function returns NULL since in my version it was impossible for it to go out of bounds. Another option is to keep that code the way it is and just limit the camera bounds. That way if you do go too far it won't crash on you.

Share this post


Link to post
Share on other sites
Quote:
Original post by evillive2
I used the mod (%) operator in order to simulate the continuous map. Try outputing the values that go into it an then come out of it to a log file or to the console and you might understand how it works a little better. Basicly the mod operator does 2 things for me here:

1) it acts as a bounds checker by constraining the result to a value in a certain range.
2) it also happens to give me the propper value of the tile I want to draw.

So the mod operator kills 2 birds with one stone here. However, on to why your version crashed. I am assuming you did not want the map to contiue to wrap around. The trick here is to never allow the camera to move less than 0 or greater than the map width in pixels MINUS the screen width in pixels. Otherwise you will go out of bounds on your tilemap array. You will also need to add a check in there for if the get_tile function returns NULL since in my version it was impossible for it to go out of bounds. Another option is to keep that code the way it is and just limit the camera bounds. That way if you do go too far it won't crash on you.


Ahh I see , thanks for the reply. I am already using a limit for the camera like you said, but I think the problem is that I am drawing say 1 or 2 tiles extra incase the tile isn't totaly to the right hand side because of the offset and everything. It works fine normaly but at the edge maybe it tries to draw tiles which don't exist. I'l have to experiment anyway, thanks.

edit: Yep, just fixed it! The problem was I tried to draw a tile or two too much all the time. Thanks though, trying to fix the bug and your reply made me understand it a lot more.

edit2: I just relised that fix wasn't perfect. Withought the extra tile it ment that the little bit of offset wasn't being drawn. So now I have sorted it out by adding 2 to the number of tiles to draw, but to stop the crash if the tile is bigger than it possibly could be I just set it to the biggest possible value. Although I think what I set to is irrelevent as long as it is a real tile.

[Edited by - kzar on February 1, 2005 5:39:47 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement