Another entry in the Learning Kyra series. The series to date:
The first three entries in the series documented my attempts at learning about the engine. I had a goal to become more familiar with the engine by the end of the month, so those articles were more focused. While that month has since passed, I am still learning plenty about the engine.
Recently I learned how to create tiles. Originally I was creating sprites, and they are sufficient for making games. However, I can also use tiles, but the documentation warns:
A Tile — in contrast to a sprite — is always square (width == height.) It can be rotated and flipped, however. You should always use Sprites unless you need to rotate and flip the image.
Making sprite images and making tile images are similar activities. When I use the Kyra Sprite Editor, I need to specify tile
instead of sprite
. Tiles are simpler since I do not need to mark hotspots and verify that they are aligned correctly. I was able to get three tiles very quickly, and saving created the .xml file to use. Then I used the Kyra Encoder and created the appropriate .dat and .h files. I copied those over to my source code directory.
The code to load a tile is different from the code to load a sprite, and the tutorial doesn’t cover it. I had to look at the Bug-Eyed Monster (BEM) demo code to find an example that I could use. To load a sprite, you have to do a few steps:
- Load the .dat file
- Get the KrSpriteResource as specified in the header file you created
- Create the KrSprite from the KrSpriteResource
Loading a tile is a little more complex:
- Load the .dat file
- Get the KrResource
- Get the KrTileResource by using KrResource::ToTileResource()
- Create the KrSprite from the KrTileResource
Ok, so there is only one more step, but it took me some time to find how to retrieve a KrTileResource. For instance, to get the KrSpriteResource, there is a function: GetSpriteResource(name_of_resource). To get a KrTileResource, however, you first need a generic resource by using GetResource(ALLCAPS_TAG, name_of_resource), and I couldn’t find the documentation on what that tag should be. It turned out that they were defined in kyraresource.h, and the one I needed was KYRATAG_TILE. Again, it was not in the tutorial so it took some work for me to learn what was needed. Here is the relevant code, keeping in mind that when I used the encoder, I prefaced everything with Tiles2_ (I already had a previous attempt with Tiles_) :
KrResource* res = engine->Vault()->GetResource( KYRATAG_TILE, Tiles2_tile2 );
GLASSERT( res );
KrTileResource* tileRes = res->ToTileResource();
GLASSERT( tileRes );
KrTile* tile = new KrTile( tileRes );
GLASSERT( tile );
tile->SetPos( 130, 98 );
tile->SetRotation(1);
engine->Tree()->AddNode(0, tile);
Anyway, I imagined that the tiles I created would be pretty cool to work with since they have alpha transparency. I could make clouds or chain-link fences, for instance. As I moved a sprite to the tile, I expected that the sprite would show through the tile’s transparent spots; however, when adding an image to the engine’s tree, the order matters. Since I added the sprite after the tile, the sprite image overlapped the tile image. I switched the order in the code, and it worked perfectly fine.
But then I was concerned. In a game, elements will get added dynamically. I wouldn’t want to find that a new enemy can walk over tunnels, or that the hero walks under floor tiles! Then I learned how the BEM demo handled Z-Ordering.
Everything that can be in the engine’s tree inherents from the base class KrImNode. KrSprite and KrTile are both KrImage classes, and KrImage is a KrImNode. KrImNode isn’t an abstract class, and it is the key to handling Z-Ordering.
Usually when I add a sprite or tile to the engine, I use the following code:
engine->Tree()->AddNode(0, tile);
AddNode()’s first parameter in this case is 0, which means that in the tree, it is a child of the root. If I add a second child, it will be drawn over the first child when the following code gets run:
engine->Draw()
BEM handles Z-Ordering by creating subtrees. Background images should be added to the backgroundTree while foreground images should be added to the foregroundTree. You create those trees in the following way:
KrImNode* backgroundTree = new KrImNode;
KrImNode* foregroundTree = new KrImNode;
engine->Tree()->AddNode(0, backgroundTree);
engine->Tree()->AddNode(0, foregroundTree);
Now, when I create a tile that goes in the background, I use AddNode(backgroundTree, tile1). Naturally I would have a separate subtree or two for entities I want to always be between the background and the foreground. If I need to create tiles for the foreground, I can add them to the foregroundTree:
engine->Tree()->AddNode(foregroundTree, tile2);
And tile2 will always be drawn on top of any images that are siblings to tile1, even if I add an image to backgroundTree after adding tile2.
Now that tiles and the Z-Ordering methods are added to my toolbox, I’m becoming much more dangerous with the Kyra Sprite Engine. Using just what I know now, I can probably make a fairly simple game without struggling with an unfamiliar library. There are still a few features to learn, however, including:
- Fonts
- Alpha blending and color transformations
- Scaling
- Canvases
- Sub-window views
I may tackle one or two of these in the next Learning Kyra post.
2 replies on “Learning Kyra: Tiles and Z-Ordering”
Hi. I’m also learning to use the kyra engine and I noticed something… In your post, you can actually use
KrTileResource *tileres = engine->Vault()->GetTileResource(TESTfirsttile);
instead of
KrResource* res = engine->Vault()->GetResource( KYRATAG_TILE, Tiles2_tile2 );
GLASSERT( res );
KrTileResource* tileRes = res->ToTileResource();
just wanted to point that out 🙂
Thanks, David! I’ll keep that in mind next time. It sure seems simpler. B-)