2d tile based map and objects

Started by
11 comments, last by crzzymatt 16 years, 2 months ago
I'm using C++ and SDL to build a basic 2d tile based game. I only want to be able to do a few basic things. -Move around on the tiled map. -Pick up/drop items to/from the map. -Swing a sword(no projectile/magic) -Read signs/talk with NPC -Have enemies that move on the map, has basic path finding skills (maybe A*). My first problem that has come up is the basic map. I've read several tutorials on tile map design yet I am still having trouble. The idea I have is to have a 2d array of "maptiles" that has pointers to other "maptiles" that are stacked above. This technique allows for basic ground tiles to have other items stacked on top of it such as stumps, signs, characters, items, etc. I've come up with an idea of how to break the objects up, but I am unsure as to how well of a design it maybe. My Design ------------ pass keyboard events to player object player uses events to determine a change(ie. move up) change is sent to map manager map manager checks one tile up from players position to see if he can move if player can move, update player info and move player tile same for enemies -enemy run AI -any change is passed to map manager -if map manager says OK, then update the enemy's info and move/change the tile So there it is my basic design for moving on the map. If this is a bad design please state why, and provide any other design suggestions you may have.
Advertisement
Why do you have to treat the player as if it was a tile?
I don't think that the maptiles do have to know on which tile the player is.
If you need to know where the player is standing then put that information into your player-class, if you have something like that.

If I were to develop a tile based game I would do it like this:
Make a Map class that would load tile maps into an array and would be able to draw itself taking a "view" as argument.
Then I would have a view-class, that is just like a camera that looks onto your map. That way you can use those views to determine which portion of your map needs to be drawn (normally this is where the player is). You can also have several views, if you wanted to have a split screen for 2 player games for example.
Then I would have a class for the player, another for the enemies and then a generic class for items. Those would hold information like place, health etc.
Finally a game class that would own instances of all the other classes and handle them, like when having the player move, the game class would "ask" the map class whether that place is free for moving. Here you can use path finding I guess.
Ok well the problem I come up with that approach is how do I allow the player to be able to walk behind something? Say I have a house and I want the player to walk behind it and you can no longer see the player. With the player as a tile he would be drawn before the house was drawn, therefore putting him behind the house.

edit:

-my guess is to have the tiles have a z-order attribute, and on the draw() function check the tile's z-order with the players z-order and draw inorder from lowest to greatest?
Quote:Original post by crzzymatt
Ok well the problem I come up with that approach is how do I allow the player to be able to walk behind something? Say I have a house and I want the player to walk behind it and you can no longer see the player. With the player as a tile he would be drawn before the house was drawn, therefore putting him behind the house.

edit:

-my guess is to have the tiles have a z-order attribute, and on the draw() function check the tile's z-order with the players z-order and draw inorder from lowest to greatest?


Ever heard about layers? Foreground layer, background layer... rather than sorting objects by z-order, put them into a layer, and draw the layers in a certain order. In the end, it's more or less the same: you're sorting objects on z-order, but in a more explicit way and with less overhead per tile. A sort of batch-sorting, so to say.

As for doing everything tile-based, that limits movement and item placement to a very rough granularity. It certainly won't feel natural, unless you're going for a turn-based gameplay or such. How about storing objects as actual objects, with their own locations (in world coordinates rather than grid coordinates)? If you know their location and dimension, you can determine which tiles they cover relatively easy with a few bounding box collision checks. It'll make things much more flexible in the end.
Create-ivity - a game development blog Mouseover for more information.
I guess I could go with the layered approach. I was just trying to have a way to not have to iterate through a bunch of blank tiles. Unless there is a different way of doing layers that I do not know of. I am thinking about the MAP[X][Y][LAYER] approach. The tile set I originally stated was suppose to be an Ultima (6?) style.

-I am going for a tile based map implementation, but not tile based movement. Trying to get a Zelda: A Link to the Past feeling.
I recently read this which may help your problem a bit:

http://www-cs-students.stanford.edu/~amitp/Articles/Tiletech.html

Basically, you could have MAP[X][Y] be an array of lists, where each element in the array is a list of the layers. So, for example, in one tile, there may just be the ground layer, but in the tile next to it, there may be the ground layer, a wall layer, and a sky layer (list length of 3 in that tile).
Quote:Original post by Jon_Wilson
I recently read this which may help your problem a bit:

http://www-cs-students.stanford.edu/~amitp/Articles/Tiletech.html

Basically, you could have MAP[X][Y] be an array of lists, where each element in the array is a list of the layers. So, for example, in one tile, there may just be the ground layer, but in the tile next to it, there may be the ground layer, a wall layer, and a sky layer (list length of 3 in that tile).


This is the exact article I read and the idea of what I tried to mention in the original post when I said "stacked map tiles".

I guess I could arrange the order in drawing these tiles to have the layer effect as such: draw first item in list() (background) (storing a list of indices of tile layer list that have a size greater than 1, so I wont have to iterate all tiles again.) Then draw the player & enemies, followed by the rest of the tiles in the set.

The problem that keeps coming to my mind is having to iterate an item and enemy list on each frame. This can really slow things down as the list get massive. Should I be worrying about this with current technology? or is this only a problem when the lists exceed sizes of over a 1000 elements (which I should never inexperience)
A few things to keep in mind: arrays limit you to a pre-defined number of tiles. If you're going for a data-driven approach, where you load maps from files, you should consider using std::vectors or std::deques, or you may want to look into boost::multi_array. That will allow you to load various maps from different sizes without changing your code each time.

As for avoiding too much blank tiles, that depends on just how much blank tiles you'll get. For relatively small and simple maps, I'd say avoiding it isn't worth the hassle. It may not be a real bottleneck after all, so why waste time on it? If it really is a bottleneck - and you should test your code to find out if it is - then there's some ways to get around it. For example, by using multiple smaller grid areas per layer, rather than a single large block of tiledata - as if you're pasting a few small images onto a large canvas, rather than using a single image that's the same size as the canvas but contains large transparant parts.

For larger maps or an increasing number of items, higher-level optimizations, such as using quad-trees for fast culling, become interesting.


But that's probably unnecessary for your game, and would only confuse things for now. I'd say, just get a simple tilemap up and running first and add a layer system to it. And be sure to read up on C++' standard library. ;)


EDIT: As for that article, it's 12 years old and deals with C, not C++. They're two different languages, despite the similar syntax. I'd start looking for a more up-to-date tutorial if I were you.

[Edited by - Captain P on January 28, 2008 7:33:27 PM]
Create-ivity - a game development blog Mouseover for more information.
Captain P I am going to take your advice and get something simple up and going. Heres the plan.

have a two layered map (background and foreground)
have a list of items, a list of enemies, and a player

draw background tiles, draw items, enemies, and player, then foreground tiles.

background tiles, items, enemies, and the player will have a RECT like attribute.

On any movement command the player check to see if the new location's RECT collides with a background tile's RECT (which it will) and determine if the tile it collides with is walkable. If it is, then it will check the "static items" (signs and what not that cant be stood on but interacted with) to see if it collides with any of them, if no then move player to the new position.

Does this sound like what you where describing in an earlier post?

EDIT - Quick question, would having the player and enemy classes have a pointer to the map set and item list be bad practice?(higher coupling) I mean they have to know of their existence to check for the collisions.

[Edited by - crzzymatt on January 28, 2008 9:25:32 PM]
Quote:Original post by crzzymatt
background tiles, items, enemies, and the player will have a RECT like attribute.

On any movement command the player check to see if the new location's RECT collides with a background tile's RECT (which it will) and determine if the tile it collides with is walkable. If it is, then it will check the "static items" (signs and what not that cant be stood on but interacted with) to see if it collides with any of them, if no then move player to the new position.

Sounds good. You don't need to store a RECT for every tile though, since it can be derived from a tiles location and dimension: (x * tilewidth, y * tileheight, tilewidth, tileheight).

Quote:Does this sound like what you where describing in an earlier post?

Yep.

Quote:EDIT - Quick question, would having the player and enemy classes have a pointer to the map set and item list be bad practice?(higher coupling) I mean they have to know of their existence to check for the collisions.

Personally I'd decouple it some more. Currently, the player and enemies need to know about the map and about all items that can block it, but what if you add some other special case? You'll need to modify your player and enemies to get it to work. How about hiding this all behind a single interface? A system that knows about all collideable objects and that can tell any object if it can move or not. The player could then call a function, pass it's own RECT and desired movement, and the underlying system would run all the checks. You'll then have a single place where all collision checks are done, which all moving game objects can use to determine if their movement is valid.

There are other cases too, such as the signs, which shouldn't block the player but perform a certain action. The collision system shouldn't check against them when determining valid movement, but it could provide another function that checks for collisions with special objects, and activate those objects when the player collides with them (object.Activate() or such), so a sign can display it's sign and a trigger can perform whatever action it needs to perform.
Create-ivity - a game development blog Mouseover for more information.

This topic is closed to new replies.

Advertisement