PDA

View Full Version : Objects on a tilemap



T-Bear
05-05-2014, 12:36 PM
Hello.


I would like to know what the “best”, most efficient way is to handle objects on a tile map. So far I have mostly done it with one huge array which was easy to implement but feels inefficient. This way I have to loop through each item on each frame to see whether it is on screen and should be rendered. Is there a nicer way of handling these kinds of objects?


My previous code:
Tmap = class
public
Tiles: array of Ttile;
Objects: array of Tobject;
end;



Mostly I'm just curious what the "proper" way is of doing this. Maybe I am doing it completely wrong from the start. :)

AthenaOfDelphi
05-05-2014, 12:49 PM
Hi T-Bear,

If you are storing an X,Y pair for each item in the Tiles and Objects array, then you can improve things quite a bit by moving to a two dimensional array.



Tiles : array[0..200,0..200] of TTile;
Objects : array[0..200,0..200] of TObject;


Which obviously has the limitation of hard coded sizes. If you're using a dynamic array where you work out an index like index:=x+(y*width); then providing you only iterate through the cells that appear in the visible area, you're not going to gain too much I wouldn't have thought by changing it.

When I wrote a tile based game for one of our competitions, I used pointer based datastorage which is in fact much like the dynamic array with index calculations.

Can you post some of the code where you are actually using the map?

SilverWarior
05-05-2014, 01:19 PM
Usually peopole use two dimensional arrays (array of array of TObect) instead of one dimensional one. This alows them to acces grid cell data by row and column numbers.

If you have tilemap with fixed tile size (all tiles are of the same size) you can easily calculate which tiles are in your viewport from your camera position. For instance:
Camera position is on X row and your viewport would render 21 rows. This would mean that you would need to check tile information in rows from X-10 to row X+10.
Similary you calculate which columns would be rendered.
So you end up having all necessary information to use for lops for reading information

Another approach would be dividing your map into sections (chunks).

Anyway if you provide more information about your game and general map structure we would be able to offer you better advice particulary more suitable to your game.

Super Vegeta
05-05-2014, 03:05 PM
Like said above, make the object array two-dimensional. Or, if you need to have multiple objects on a tile, then I'd add an array/list to TTile, which would hold pointers/array-indexes of objects standing on given tile.

T-Bear
05-05-2014, 07:58 PM
Hi T-Bear,

If you are storing an X,Y pair for each item in the Tiles and Objects array, then you can improve things quite a bit by moving to a two dimensional array.

Code:
Tiles : array[0..200,0..200] of TTile;
Objects : array[0..200,0..200] of TObject;




How would this work if the objects are larger than a single tile though? Basically on the map there are buildings which can span many tiles, and trees which are on a single tile.
As you said this probably won't work with dynamic size of the objects.


When I wrote a tile based game for one of our competitions, I used pointer based datastorage which is in fact much like the dynamic array with index calculations.

How exactly would this work? Pointers from each tile object to the object on the tile? How would this work with objects larger than single tiles?


Can you post some of the code where you are actually using the map?

Anyway if you provide more information about your game and general map structure we would be able to offer you better advice particulary more suitable to your game.

I am mostly just doing some information gathering for my new project, reading about stuff and so on, so I don't have any code sample for how the map structure is going to work exactly. I tried finding some old code illustrating how I did this, but couldn't find any.

AthenaOfDelphi
05-05-2014, 08:41 PM
The objects that I had that were bigger than a single tile were broken up into multiple tiles. By doing this I was able to create the illusion of depth by drawing them on different layers relative to the player so the 'front' were rendered behind the player while the 'back' was rendered in front of the player.

But, back to your question... how could this work if the objects are bigger than a tile? Good question :)

I'm not sure and it's not a solution I would pursue personally because of the overheads involved.

User137
06-05-2014, 12:36 AM
Your original solution was almost perfect, but you are right, on larger maps it would get lower performance. Thus you can divide your map into chunks.


const CHUNKSIZE = 32;

TChunk = class
public
posX, posY: integer;
Tiles: array[0..CHUNKSIZE-1, 0..CHUNKSIZE] of TTile;
Objects: array of TObject;
end;

TMap = class
public
Chunks: array of array of TChunk;
function GetTile(x, y: integer): TTile;
end;

function TMap.GetTile(x, y: integer): TTile;
var cx, cy: integer;
procedure
cx:=x div CHUNKSIZE;
cy:=y div CHUNKSIZE;
result:=Chunks[cx, cy].Tiles[x-cx, y-cy]
end;
Or something along these lines... Of course you could do range checking in GetTile() to avoid critical errors on negative values or out of boundaries.

Carver413
06-05-2014, 01:37 AM
if I were trying to do someting like this I would use an 8bit index map and use index 0 as a null. the background would be for normal size tiles while the forground would be for larger tiles. then I would add a clip frame that reperesents the view able portion of the map. now all you have to do is move the clipframe around within the map to change the view. on the forground the indexed position would be the top/left and all indexes underneath it would be 0's. your clip frame would have to be larger then the actual view so if a larger tile were half on it would still be displayed.