Scrolling Map (non tile-based)

Started by
6 comments, last by leiavoia 19 years, 2 months ago
I am making a game that is comparable in style to Soldat or Leiro (basically Worms in realtime instead of turnbased), and so a level will have a main map, some parallax backgrounds, some worms, projectiles, particles, bonuses, etc. I want the player to be able to scroll around the whole map by moving the mouse to the edge of the screen, but i have some questions about scrolling. (i am using SDL, if that makes any difference) 1. For scrolling, the only idea i could come up with was to make a pair of global 'xoffset' and 'yoffset' variables, and just apply them to every single x/y coordinates. Is this the best way? 2. With a big map, how much of it do i have to be calculating and blitting? The way i assume it should be done is that the code for everything would always run, but only the sprites onscreen would be blitted? Also, does anyone know how to do destroyable landscape(like Worms)? I vaguely remember hearing once that you could do it with a 2d boolean array that had a 'destroyable value' (1 or 0 obviously) for every pixel of the map. Though im not sure how to impliment that. Thanks in advance for your help!
Advertisement
Quote:Original post by djalias1. For scrolling, the only idea i could come up with was to make a pair of global 'xoffset' and 'yoffset' variables, and just apply them to every single x/y coordinates. Is this the best way?


That's pretty much the basic concept. You could go with the opengl-esque method, which has what could be described as a 3d cursor (misdominer) which you place things with. OpenGL uses a similar concept, only it uses matricies to store the "compiled" effect for faster math, to reduce the high number for cos/sin math required.

Quote:2. With a big map, how much of it do i have to be calculating and blitting? The way i assume it should be done is that the code for everything would always run, but only the sprites onscreen would be blitted?


Well, you could use a quadtree or related concept, or even a simplified version of that general idea: partition space into a certain grid layout, and only render the grid cells that could potentially be onscreen.

Quote:Also, does anyone know how to do destroyable landscape(like Worms)? I vaguely remember hearing once that you could do it with a 2d boolean array that had a 'destroyable value' (1 or 0 obviously) for every pixel of the map. Though im not sure how to impliment that.

Thanks in advance for your help!


Well, here's how I did it for a basic tank game in BASIC oh so many years ago (psuedoish coding in C++ because it's been so long since I've touched basic, very sloppy design, but it should get the idea accross):

char pixelcolor[width][height];//X direction goes from left to right//Y direction goes from top to bottomvoid explode( int x , int y , int radius ){   //FIXME: Code can access beyond map, which in the best case will SEGFAULT.   //(hint: implement get/set pixel which return black/set nothing if out of bounds)   for ( int i_x = x - radius ; i_x < x + radius ; ++i_x )   {      for ( int i_y = y - radius ; i_y < y + radius ; ++i_y )      {          int dx = i_x - x;          int dy = i_y - y;          if (( dx * dx + dy * dy ) < (radius * radius)) //if within radius          {              char color = pixelcolor[i_x][i_y];              switch( color )              {                  case PLAYER1_COLOR:                      player1.armor--; //NOTE: damages based on how many pixels of the player get covered in the blast                      break;                  case PLAYER2_COLOR:                      player2.armor--;                      break;                  default: //terrain                      pixelcolor[i_x][i_y] = black;              }          }      }   }}void apply_tank_physics( ctank & tank ){    //Note: we're making the tank rest only on the pixel directly bellow it's center    while ( pixelcolor[ tank.x ][ tank.y + 1 ] == black ) //FIXME: also not bounds checked, see previous FIXME    {       //Note: we dont even attempt to restore the terrain.       //This may eat up some terrain.       tank.erase();       ++tank.y; //REMEMBER: +Y is down in our coordinates.       tank.render();    }}


Remember, this was done in BASIC using 16 colors (I think, maybe 256) in which the only "global" variables were the tank positions, health, last shot angle/power, and the pixel data which was also the storage of the terrain. If it was brown it was ground, black signified nothingness, and... well yeah.
Quote:Also, does anyone know how to do destroyable landscape(like Worms)? I vaguely remember hearing once that you could do it with a 2d boolean array that had a 'destroyable value' (1 or 0 obviously) for every pixel of the map. Though im not sure how to impliment that.


I would say that it could be a good way. You could hold an bitmap of the land area, where you would define one color as 'air' or 'destroyed' (transparent colour would be best). Then you would modify this bitmap on each explosion.
Drawing would be relatively easy, because you would allways just draw the land bitmap on the screen, (you would substract the camera_origin_x and camera_origin_y coordinates from it).
Quote:1. For scrolling, the only idea i could come up with was to make a pair of global 'xoffset' and 'yoffset' variables, and just apply them to every single x/y coordinates. Is this the best way?


That's the basic idea. HOWEVER i would not make them global. I'd create some basic blitting function that wrap those variables. Have

BlitGraphic( world_x, world_y, SDL_Surface* graphic ) {   SDL_BlitSurface( graphic, world_x - x_offset, world_y - y_offset )   }


or something like that. That way, no one has to know or understand how to use all that screen positioning hoopla.

Quote:2. With a big map, how much of it do i have to be calculating and blitting? The way i assume it should be done is that the code for everything would always run, but only the sprites onscreen would be blitted?


For a worms clone, i think it might actually be a good idea to load the entire map image into memory. You would only blit the screen portion of course. That's a very easy thing to do in SDL, fortunately. Alternatively, you could load the image and break it up into tiles, then only blit the appropriate tiles. Another idea would be to break up the world into major sections if your world is truly huge and memory is an issue.

Quote:Also, does anyone know how to do destroyable landscape(like Worms)? I vaguely remember hearing once that you could do it with a 2d boolean array that had a 'destroyable value' (1 or 0 obviously) for every pixel of the map. Though im not sure how to impliment that.


For 2D i would think you are better off using the actual pixel data rather than some other structure. That's easy in SDL too :-)
I have just recently wrote a simple project that utilizes worms style damageable geometry - using openGL.
Its not a complete game, but I think you may find it usefull gimme some moments I'll upload the everything for it and link it here.
Ok heres the the zip, it contains src code, plus an exe you can tryout.


<DamageableSceneTest.zip



Controls are arrow pad rotate ship/ use booster. Z/ numpad0 = shoot.
Simply fly around and blast the scenery.
I thought I would mention this, my technique doesnt require storing bitmap data, and thus can have quite large maps, currently this is 8*8 screen in size.
wow thanks everyone for the input.


great idea with the wrapper for blitting, lol i never would have thought of that, i figured i would just put the offset into every blit. but should i offset all the code that controls movement of objects, or should i have it so in the code, there is no 'scrolling' and the offset is nothing more than a graphics thing?

And thanks for the code X-out, but im noob and cant really understand it too well.

The main map will be very graphic with lots of color and stuff, so i cant use cirtain colors to represent data. so should i have a map bitmap, and a 'data' bitmap (using a few specific colors); or should i use a map bitmap, and store the data in an array?


When you move objects, it should be as simple as object.Move(x,y) . When you blit it should be as simple as image.Blit(x,y) , where x and y are always world coordinates, not screen coords and not offset coords. Let the blitting objects/functions worry about exactly how to do their job. For my project i recently reconfigured the whole blitting arrangement. I have a Texture class with a Blit(x,y) function. However, the Texture class also needs the screen offset data but this is the job of another object. So Texture has a static set of data members and a static function called SetScreenOffset(x,y) which it uses to factor in the blitting routines.

So each frame, my process is like:

1) camera object refocuses/recenters

2) camera object notifies Texture::SetScreenOffset()

3) objects draw themselves with texture.Blit(x,y)

This topic is closed to new replies.

Advertisement