Efficient game maps and Scrolling

Game Maps

I started working on my newest project today and it got me wondering. When games with huge tile maps like the olden day Legends of zelda were created, were the maps drawn manually and the programmers mainly added collision to it, or did the programmers take the tiles and make huge a** for loops for the map? I thought, maybe just load the maps and just create special classes for each object, containing its’ own separate collision and methods. I had some intense and creative ideas I wanted to apply.

Scrolling

How does scrolling effect work. would I draw the map and then have the screen move according the players’ coordinates?

There’s about 50 zillion ways to do it. But I use a huge for loop that while looping, also detects the edges of the screen so it only renders tiles actually on the screen. For collision/object detection, I search the entire entire map’s tiles for a blocked/collision property and build my collision system based on that … and as for map movement, I made the camera detect where the player is and follow him around.

Really you’re asking for many different systems, that all have to work together and are all dependent on how you build the others. So it’s kinda complicated to give a single unified answer. :smiley:

Tile collision from the guys at Metanet, of N+ fame: http://www.metanetsoftware.com/technique/tutorialB.html

Awwww. I know this is programming but I thought this could be a lot simpler. I planned on just designing the giant tile maps and using a camera to follow the players’ position. The thing is, I don’t know how the coordinates would work for this :/.

That’s basically how it is done, in the case of “player is camera” you render everything from player.x - screenWidth/2 to player.x + screenWidth/2. To figure out where that lies on your tilemap, you use a screenCoord to worldCoord transformation function, which is usually just a division by tile size.

It is really simple stuff.

Do I have to make all the collision boxes move downward every time the player presses the up key?

No. I’ll stop right here and recommend you invest yourself at least a little bit in some design philosophy called MVC. It stands for Model-View-Controller and is a system in which you separate the functions of various aspects of your application from each other.

For instance, your tilemap is a Model (of the tile data). It is rendered to the screen in a particular way, the particular way is described by the View, in the case of 2D, top-down tilemaps mostly the transformation function I mentioned earlier. The Model and View are manipulated via user-input by the Controller.

The tilemap contains the collision data, which is used to determine collisions etc. It has no effect on rendering, it is separate. Nothing “moves down” when the player presses the up key, the player object moves up in the Model, and the View renders the scene with the player in the center (if that is what your are doing), creating the illusion that everything is moving around the player. Why move all but one thing when you can move just the one thing?

MVC, being as abstract as it is, can be applied in endless ways, so I’ll stop here (I think you can get the basic idea) and let you go look at some of the numerous tutorials if you need more.

makes sense. I was thinking about how to apply this and drew a visual.

Since the player (view I think) moves up, the other entities (models) keep their location? The weird thing is, I would have to draw the items starting from 0x, 0y which would be the upper left corner of the map. So then, the map would have to start at the upper left corner? (Please say yes)

Yes. At least in the case where the player avatar is the only MOB. For simplicity sake, think of the player as your cameraman moving through the world (model) as opposed to the world moving around the cameraman.

Indeed, although when you’re rendering (as previously mentioned), you’d render player position +/- minus the appropriate x and y distances.

Glad to hear that! So, if I just drew a tile map with a tile creator and drew the image to the screen, could i work from there? I don’t think loading maps from a file would be easy. I also don’t see how a for loop would be necessary to draw detailed parts of the map.

When prototyping, I usually just load up 2 tile images and use a loop to fill a 2D array with alternating instances of the image. As for loading maps from a file it can be as easy or complex as you choose to make it. Don’t be afraid to try new things though just because they could be complex. The best way to build your programming muscles is to bite off slightly more than you think you can chew.

You’ll always need at least one outer loop for rendering the map no matter how simple or complex it is. Inner loops are usually used for rendering multiple layers of tiles on a map. As long as you’re sensible in how you handle sparse data sets, you can still get a nice balance of detail and efficiency. :slight_smile:

AAAAAAAHHHHHH! I really want to just draw it and get on, but the array of maps sounds more professional even though I have no idea how to do it.

You don’t “have” to do anything a particular way. The tiles and objects residing on them can be represented any way you want.

In this example, tiles are simply in an array, [icode]Tile[][] tiles;[/icode]

Each tile can contain a [icode]List entities;[/icode]

These entities have their own collision bounds, AABBs in this case. Collision can be easily determined and resolved.

Rendering tiles can be done something like this: (in top-left coordinate system, can be easily converted to others)


// just to go along with the image
final int screenWidthInTiles = 7;
final int screenHeightInTiles = 5;


int startY = Math.floor(player.y) - screenHeightInTiles / 2;
int startX = Math.floor(player.x) - screenWidthInTiles / 2;
for(int y = startY; y < startY + screenHeightInTiles; y++) {
    for(int x = startX; x < startX + screenWidthInTiles; x++) {
        Tile tile = tiles[y][x];
        setColor(tile.color);
        int tileX = (x - startX) * TILE_WIDTH; // transform to screen coords
        int tileY = (y - startY) * TILE_HEIGHT;
        fillRect(tileX, tileY, TILE_WIDTH, TILE_HEIGHT);
        
        // ok, so I'm cheating here, I'm lazy.
        for(Entity e : tile.entities)
            e.render();
    }
}

EDIT: it should be noted that everything is stored and done in world coordinates, screen coords are calculated as needed when rendering, and only then.

This is my first time seeing somthing like this so I’m still a bit lost… But I see how rendering only what is on the camera keeps the fps higher

Indeed, and when you have lots of complex entities, lighting, particles systems, etc etc, it actually becomes quite necessary to not render all the things going on in the universe all the time.

Yea, mind giving me a completely unrelated tutorial for that loading only in view? xD

It looks so hard. Couldn’t I just make the graphics render whenever the camera touches its’ camera collision bound or somthing?

You could, but I was under the assumption that you were looking for efficient methods; iterating over the whole map each frame is definitely not efficient unless your map takes up 1 screen or less. BP’s method is pretty simple. Walk through it in your mind a few times and it will click. :wink:

I concentrated on reading it and understand it exxcept for this line.

int tileX = (x - startX) * TILE_WIDTH; // transform to screen coords
        int tileY = (y - startY) * TILE_HEIGHT;

So startX is the players’ current position at all times right? If your subtracting the tiles’ x/y by the players’ x/y, wouldn’t it move the player further? What would a keypress look like ?

An alternative is doing something like this, but this DOES iterating over the entire map, only rendering the tiles it detects as in the viewport.

BP’s way is technically a faster way of going about it because you don’t iterate the entire map, but this is how I do it. Although looking at BP’s I may try his way and see how much faster it could be.

EDIT: fixed some stuff.


for (int x = 0; x < mapWidth; x++) {
	for (int y = 0; y < mapHeight; y++) {
		//Where you're seeing if X/Y is even in the viewport, if it's not, don't bother rendering.
		if (((x*tileWidth)+mapX > tileWidth*-1) && ((x*tileWidth)+mapX < displayWidth+tileWidth) && ((y*tileHeight)+mapY > tileHeight*-1) && ((y*tileHeight)+mapY < displayHeight+tileHeight)){ 
			//Assuming you store tiles by a tileID and tileID 0 is "no tile", you just skip attempting to render it.
			if (!(mapArray[x][y] == 0){ 
				//render tile X/Y
			}
		}
	}
}