Sign in to follow this  
crzzymatt

2d tile based map and objects

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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).

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
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.

Share this post


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




Would other special cases be say, when I attack I would have a function like: CheckAtkCollision(x,y,width,height,range)
where if range = 0 it's melee, else possible magic/bow attack?

and if I have an Action Button to open chests, open doors, read signs, etc. it would be like:
CheckActionCollision(x,y,width,height)
and as you said run some method for the object that was collided with.

Not to jump too far ahead, but would the same concepts go along with mouse commands such as: onClick pass a location and dimensions to the collision handler for evaluation of what "thing" was clicked on?

Share this post


Link to post
Share on other sites
Quote:
Original post by crzzymatt
Would other special cases be say, when I attack I would have a function like: CheckAtkCollision(x,y,width,height,range)
where if range = 0 it's melee, else possible magic/bow attack?

and if I have an Action Button to open chests, open doors, read signs, etc. it would be like:
CheckActionCollision(x,y,width,height)
and as you said run some method for the object that was collided with.

Not to jump too far ahead, but would the same concepts go along with mouse commands such as: onClick pass a location and dimensions to the collision handler for evaluation of what "thing" was clicked on?

You can reuse the same collision check functionality for all these cases. Only the reaction and the objects you need to take into account differ. You can write different functions for each case, that underneath all use the same collision check routines, just on different objects and with different after-effects.

As for the CheckAtkCollision function, it depends on how you want to handle your combat. When does a melee attack hit? How do you handle ranged attacks? Do you fire a projectile or do you use instant hit within a certain radius?

Share this post


Link to post
Share on other sites
Ok, So in such functions would I do something like

pseudocode:

here the collision is detected and the object the player collided with is passed back to the player, then the player handles the attack sequence.


// function inside of collision handler, which has a pointer to the enemy list
Enemy* CheckAtkCollision(x,y,h,w,etc.)
{
iter = enemys.begin();
while (iter != enemys.end())
{
if (CheckCollision(x,y,h,w,iter.getX(),iter.getY(),iter.getH(),iter.getW()))
{
return iter; //return pointer to enemy that was collided with
}
}
return NULL;
}

//function in the player class, that called for the collision check
if ((Enemy* tmpEnemy = CheckAtkCollision(m_X, m_Y, m_Width, m_Height, range)) != NULL)
{
if (tmpEnemy->isAlive())
{
tmpEnemy->Hit(m_Dmg); //tell the enemy to decrement his life based off of players damage
if (tmpEnemy->isAlive())
{
m_exp += tmpEnemy->getExp(); // increase players exp if enemy is dead.
}
}
}






or

should the collision handler handle the attack it self



// function inside of collision handler, which has a pointer to the enemy list
Enemy* CheckAtkCollision(x,y,h,w,etc.)
{
iter = enemys.begin();
while (iter != enemys.end())
{
if (CheckCollision(x,y,h,w,iter.getX(),iter.getY(),iter.getH(),iter.getW()))
{
if (iter->isAlive())
{
iter->Hit(m_Dmg); //tell the enemy to decrement his life based off of players damage
if (iter->isAlive())
{
m_exp += iter->getExp(); // increase players exp if enemy is dead.
}
}
}
}
return NULL;
}






Or another option being have some event handler that gets passed an "attack" event and say the event object has a pointer to the attacker and a pointer to the object being attacked. The event handler would then do the correct actions when it pops the events out of it's stack.

Another thing I'm iffy about is the constant passing of pointers to each handler.
Meaning: the collision handler has pointers to the map, item list, NPC list(enemies and friendlies), player

the player, the NPCs, the items have a pointer to the event handler so they can throw events themselves (to solve this one I guess I could have an event list attribute and the event handler checks the player,npc,items to see if they have any events)

example of this "self suggested solution" would be:
changing the code from the first snipplet a little (keep the collision code)

//function in the player class, that called for the collision check
if ((Enemy* tmpEnemy = CheckAtkCollision(m_X, m_Y, m_Width, m_Height, range)) != NULL)
{
eventlist.push_back(new AttackEvent(player,tmpEnemy));
}

//function in the player class that the event handler will use to get it's event list
vector<Event>* getEventList()
{
return eventlist;
}

is this normal? or am I just over thinking and trying to make it a problem.

EDIT: Just read some stuff on MVP design and use of Patterns, So hopefully I can figure this out myself.

[Edited by - crzzymatt on February 1, 2008 10:46:24 AM]

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