2
votes

First off to spare anyone not very interested in reading this whole novel, there is no code in this question it is purely theoretical. I am on my phone and this is a question about which choice is best fitting and easiest to work with in the situation.

So to the question, I am working on what I believe should be a basic Tilemap engine* and I'm in doubt about my approach. I have already rewritten it several times. Not that it have taken too long, the project is still in its diaper to say the least.

Basically, I have a Sprite class with this constructor:string name, int Id, texture2d texture.

And then there is my Tile class that derives from the Sprite class, with these added to the constructor : Int X, int Y, bool walkable.

At my first attempt I used a 2D integer array and assigned the ID value of the desired tile to the position on the array. And, then when drawing the tilemap I would double loop** the entire array and find the tile with the Same ID as the array position currently at in the loop with a Tile function, which would return a new Tile, that I would then continue to draw with the loops current X and Y(multiplied with texture size, for obvious reasons). Because at this point, my Tile class didn't have X and Y values. Which seemed like an awful lot of throwing forth and back, and for some reason I felt it was a sort of primitive approach.

But then I went with another approach, I made a 2D Tile array and instead of having to assign each of the arrays positions an ID value, and then matching it, making a new instance of it, and then drawing it I could just loop the array and make the array position a new instance of the desired tile with the current X and Y in the constructor, and then draw it with the Tile.X and Tile.Y. Seemed more fitting.

At least for about a second before I ran the code, it did. See, now I needed to fully populate the whole array with tiles before I tried to draw it, or else it would return a null object reference( of course ), because previously I had a tile with the ID of 0, which it would just draw as the default if nothing else had been assigned. Somewhere in all the mess I had before, I also had a simple little "if" that just told it not to draw if the ID was 0, but it was later on I added that. I realize now that I could do the same with the Tile array, just instead of checking if 0, then checking if null I guess. But, I don't care much for checking if null. Again, that seems primitive in some way to me, as If the code is not fully thought through and doing something it's not supposed to. Also, I would now be in sort of a pickle if I tried to parse a simple xml file as tilemap, because I wouldn't know how to match the xml with a tile, where I previously just could've used the ID's. Btw, I've never worked with xml before, so I can only guess if that would be a problem.

*although if anyone would clarify exactly when or why a "project" could be considered an engine, however basic it may be, then that'd be cool too. Because I don't think I understand that fully yet.

** I realized that it would be more fitting to use foreach in this situation, but that would only work with the Tile array, afaik.

TL;DR So to end my question.. what is most fitting? The int[,] or the Tile[,] for a tilemap? And, should I instead of using 2 unrelated X and Y ints use vector2 in the Tile class for positioning?

Note - I am a computer science student, and we have only had the basic "hello world" training in so far, the rest I've learned is from personal interest. Also, please do correct me if there is any flaws, or terms used wrongly. I'm asking here because I want help, and I'll take any help offered, whether it's on- or off-topic. However, I would appreciate if it's given in a properly and constructive manner, please.

Edit: holy sht, I didn't even realize I had written that much. To whoever reads all of this, you're the real MVP!

4
I enjoyed reading this but I don't have an answer for you.. Awesome post though about Monogame and tile theory!jhhoff02
My map structure for a well bounded, relatively small map is usually a 2d array of TileStack. With TileStack possessing some metadata about the coords, and a List of Tile. Tile will then contain the Tile's id, and additional information about the tile instance, such as color, offsets, rotation, etc.Bradley Uffner

4 Answers

1
votes

It's possible some of the regions in your game won't have just one tile, they may have multiple tiles on top of each other.

For example, a table that stands on the floor so you can see both floor tile and semi-transparent table tile. This means a simple 2d array of tiles won't be enough in that case.
Perhaps a list of tile instances each having it's draw coordinates? Then you just loop through the list and draw tiles on the screen in whatever order they go.

0
votes

I don't know if it still relevant but anyway. From my experience, definitely use Tile[,] and not just int[,], think about the future (especially when your are building engines, which have to be very generic), maybe someone will want the tile to be animated or enter some logic to the tile.

What you can do is build the tilemap using int[,] but it is way better to use XML. tip: instead of looping on TileID or using a long switch-case (or a lot of ifs ofc), you can store them in a dictionary (which is c# implementation of HashMap) and then you'll get them with complexity of O(1) instead O(n) (Which might be crucial and might not, depends on how much tiles you got). You can store in a dictionary the delegate the the creating function or so.

The main issue with not using Tile[,] or int[,] is the collision detection. You will have to loop on the entire collection of tiles, instead of just calculate the correct indexes (or range of indexes). The calculation should look like this j = x / tilewidth; (Again O(1) instead of O(n), here it is very crucial).

If you want to have more than one layer on your tilmap (for tables, chairs, plants, etc..) you can create Layer class (which should contain Tile[,]) or use Tile[,,,] where the first index is the layer. I think the first is better because the last is more complex to work with and less generic.

0
votes

You could do both !

Store your "general tile data" somewhere, and store map-tile specific data (data related to 1 tile in the map, could be called MapTile or something) in a list.

So, your Tile class would hold the sprite, the "walkable" state and such, and your MapTile would hold a reference to it's tile model, it's position in the map, it's layer, and maybe it's state, in short, any data unique to this specific tile in the map.

When you want to draw your tiles, you just scroll through the list of MapTile, and draw the model related at the position of the tile.

This way, you reduce memory usage and still have the functionality of the Tile[,] method.

0
votes

I would suggest to use ints. If your different tiles are less than 65.000 I'd suggest you use ushorts.

Let's assume you want 8000x2000 Tiles. Thats equals to 16.000.000.

If you choose to store References (64 bit) then you'd occupy 16.000.000*64 = 1024000000 bits (~ 1000 MB RAM)

If you choose to store ints(32bit) then you'd occupy 16.000.000*32 = 512000000 bits (~ 500 MB RAM)

If you choose to store uShorts(16bit) then you'd occupy 16.000.000*16 = 256000000 bits (~ 250 MB RAM)

In my oppion it's worth to invest the extra CPU time to do a lookup in your Tile array but this depends on your situation.