Now that the menu system is nearly complete, the next step is to get a tile map going. This process has involved quite a few iterations, and I believe there are still more to come. Ultimately, we'll end up with a map or background that is redrawn every frame.
Common tile formats include orthogonal (square), isometric (diamond) and hex. A few examples, and a intro to tile based game can be found on wikipedia. I've decided to go with isometric tiles for two reasons: I found a nice tile pack that happened to be isometric, and depth is easier to create.
First Attempt: Hardcoded Sprites
To get my feet wet without having to add more infrastructure code, I loaded up 100 sprites in a loop.
for(int x = 0; x < 100; x++) { ImageObject *bg = new ImageObject(); bg->LoadFromTextureSheet(texture, sf::IntRect(0, 0, 64, 32)); bg->SetPosition(x*64, 0); std::stringstream key; key << x << "bg"; _OM.Add(key.str(), bg); }
Then, back in the main loop, a single call to _OM.Draw() would successfully display 100 tiles to the screen. It's great that it sticks with the central Object Manager concept, but there are two significant problems.
Not easy for designers to work with.
Second Attempt: Only One Sprite
The first problem to tackle is speed. While 50 sprites may load fine, 2000 sprites made my computer chug. The logical fix is to start with one sprite, and change its position multiple times per frame.
for(int x = 0; x < 10; x++) { sprite->SetPosition(x*64, 0); sprite->Draw(); }
That sample code would draw 10 tiles across the top of the screen. This instantly fixed performance issues, but introduces another problem.
Our concept of an Object Manager that is responsible for drawing every frame is now broken. sprite->Draw() needs to be called outside of _OM->Draw().
Still not easy for designers or level editors to work with.Â
Third Attempt: Using an Array
Because solving the Object Manager concerns would take a large refactor, I moved on to trying to make things easier for designers. Using an array would present a basic visualization of what the map is supposed to look like. Sticking with one sprite, we could make a map like this:
char worldmap[5][5] = { {'A','A','A','A','A',}, {'A','A','A','A','A',}, {'A','A','A','A','A',}, {'A','A','A','A','A',}, {'A','A','A','A','A',}, };
Iterating over that array will draw a square of our one tile. This is progress, but we're still left with two issues.
The 'A' is a hardcoded reference to our one tile. When we have more than one, how do we make dynamic aliases?
We're still drawing outside of the Object Manager.
Fourth Attempt: Using Aliases
Let's head back to how we load our tiles. The basic approach is to iterate over the sprite sheet and load images in by increasing x*64 each iteration. That works, but how do we know the name of the tile once it is loaded into the Object Manager -- we have no way to reference it. Some sort of collection is needed so that we know exactly what to load from the sprite sheet.
struct Tile { public: int x; int y; int width; int height; sf::String alias; }; std::list _mapTiles; Tile desertA; desertA.alias = "A"; desertA.x = 0; desertA.y = 0; desertA.width = 64; desertA.height = 32; _mapTiles.push_back(desertA);
In the sample code, a list of map tiles is created. Each tile is a struct that contains coordinates, size and an alias. The alias is used as the key in the Object Manager.
std::list::iterator it; for ( it = _mapTiles.begin(); it != _mapTiles.end(); it++) { ImageObject *bg = new ImageObject(); bg->LoadFromTextureSheet(texture, sf::IntRect(it->x, it->y, it->width, it->height)); _OM.Add(it->alias, bg); }
Using this approach, we can now create a second sprite with an alias of 'B' and use 'B' in the array. But, as always, we still have two concerns.
This becomes quite verbose. If we have 100 sprites, we need a list of 100 variables crated. The creation of tiles structs should be dynamic somehow.
Still manually drawing outside of the Object Manager.
Fifth Attempt: External Files, Software Tools and Classes
I'm done for tonight, but there is still plenty more to research about drawing tiles. The list for now is:
Load Tile info from a JSON array.
Load map array and Tile info from an external JSON or XML file.
Learn to use Tiled, and import from one of the supported exports.
Make a class that extends or uses its own Object Manager to completely handle the drawing of tile objects in an isolated format.