tile based engine design

Started by
1 comment, last by chare 21 years ago
Say I wanted to make a game like final fantasy tactics or in other words a turn based tile game. How should I design my data structures. Its obvious that i should have something like
  
struct tile {
    unit *u;
    ...
};

struct map {
    struct tile[blah][blah];
    ...
};

struct unit {
    int hp;
    ...
};
  
But now thinking ahead about the implementing the battle engine I see that each turn there is an active unit who does the usual attack/move/otherstuff and then the next unit gets to go. So then maybe at the top of the battle engine code there is
  
/* battle.c */

/* the dude whos turn is right now */
unit *activeunit;

...
  
So then when the active character attacks or moves there is selecting of squares and in the end there needs to be some way of accessing the x,y coordinates of squares. So to move I need to somehow grab the square the guy is moving to so that I can change its unit pointer to the guy. Back to all those structures declared the question is should the tile and unit structures contain x y integers that keep track of its location. In other words, should the tile "know" its own x y location, should the unit "know" its x y location or say rely on the x y location being stored in the tile its on, or maybe have none of them have x y stored and rely on figuring that out through maps array of tiles. Choosing where x y coordinates should be stored will obviously effect the code and certainly some ways will give more nasty code.
Advertisement
Yeah that''s a tricky one. When I had to make it I initially went for the more elegant solution - having the tile just know about the unit, and the map knowing the position of the tile.

But, it made things a right pain in the ass, so I gave the tile and unit knowledge of their own position. For instance, say I wanted to iterate through a player''s units and center the screen on the current unit - if the units know where they are I can simply use a list of units to do that. If they don''t know their position then we have a problem (or at least I did).

Not sure if it''d be easy to do, but I guess you could always optimise the code later. Try changing the mutator method for changing a unit''s position (setPosition(int , int) or whatever), to also change the position''s unit to null. Stuff like that helps to prevent bad things like the tile thinking it still has a unit when the unit thinks it''s on a different tile (although that example probably breaks the unit''s responsibility there are ways to do the same thing without).
The way I'm doing it is, the x/y is simply the way the map is stored. I've got an array of tiles, like MapTile[x][y]. Each tile then contains certain information, such as the following:

1) pointer to terrain (e.g., MapTile[x][y].Terrain)
2) height (e.g., MapTile[x][y].Z)
3) passability info (bitwise flags) (e.g., MapTile[x][y].Flags)
4) pointer to first object in tile (bottom-most) (e.g., MapTile[x][y].Object)

#1 points to another structure, a terrain type, which contains info about the particular terrain that tile is composed of. For example: grass, stone, snow, dirt, etc. Basically just to further compartmentalize the data, since right now I think I'm just storing a name and bitmap for each texture. (e.g., MapTile[x][y].Terrain.Name and MapTile[x][y].Terrain.Texture)

#2 is just the height in units that I use when actually rendering the map

#3 contains bits for passability. Say the rightmost bit controls ground passability, the next bit air passability, etc. (e.g., if (MapTile[x][y].Flags & FLAG_GROUND_PASSABLE) then AllowMovement())

#4 points to the bottom-most object in the master list of objects that is currently residing in that tile, or to NULL if there aren't any objects there. (e.g., if (MapTile[x][y].Object) then DrawObject(MapTile[x][y].Object) else DoNothing())

MapTile[0][0] is the top-left tile in the map, and increases to the right and down.

MapTile[x][y].Object.Next would be a pointer to the 2nd object in the tile, if any. For example, you could move a trooper on top of a pile of gold, or into the same tile as a weapon (sword lying on the ground). The first object is the bottom-most object. The last object is the one on top. (see below for example)

A note on my objects. Each object knows where it's at. Object.X and Object.Y. MapTile[x][y].Object.X would be equal to x. Redundant? Uhh, yeah. Useful? Yep. Say I want to know how far away from something an object is, like a Leader. If an Object has a Leader, which would simply be a pointer to another Object, it would be easier to reference the distance by Object.Leader.X and Object.Leader.Y than to search the entire map looking for that Leader's location. And what if the Leader is located inside another object, like a building? Then we wouldn't be able to find it by searching the map. So yeah, having each object know it's own location is sometimes useful, even necessary.

The most work is done when an object moves. I have to update the tile it is leaving as well as the tile it is entering.

The above setup is nice I think for speedy rendering. After I render the ground I can make another pass for any objects, testing each tile like this:


      for (y=0;y<MAP_HEIGHT;y++){ for (x=0;x<MAP_WIDTH;x++) {  Object *Obj = MapTile[x][y].Object;  while (Obj != NULL)  {   DrawObject(Obj);   Obj=Obj.Next;  } }}      


That way it draws the bottom-most object first, and the top-most object last. If a sword and an arrow and a rock are all in the same tile with a trooper, my code will make sure the trooper is the last object in the list when he moves into that tile. It will draw the other smaller objects first and then the trooper last, who is on top of the others.

I hope that makes sense, and I hope the above is useful in some way.

Take care.

Florida, USA
RTS Engine in Development
http://www.knology.net/~heaven
Jesus is LORD!

[edited by - Heaven on March 26, 2003 1:11:56 PM]

[edited by - Heaven on March 26, 2003 1:12:25 PM]
Florida, USA
Current Project
Jesus is LORD!

This topic is closed to new replies.

Advertisement