in Part 9 of this challenge, Scarfy was happily jumping and running around in his 2D world, and even had background music. That's a great achievement, but rather boring. All he can do is run and jump. Today I'm going to add coins for him to collect. He's going to have fun running around collecting large rotating gold coins...

NOTE: Apologies for the long delay in this episode. Life has been extra crazy for us since part 9, making it hard to get things done.

Data Files vs Hard-Coding

I could do what I did for Scarfy, and create a new coin class. However, that would quickly become unbearable. Imagine having to write a new class for every object, and then having to coordinate with your artist for every change made to the artwork (e.g., new animations, more frames). Not my idea of fun.

Instead, let's use texture atlases. A texture atlas is basically where you pack multiple images and/or animation frames into one large image. Scarfy was already a texture atlas of sort, because all frames were stored in one image. However, it was missing the all important data describing where to put the frames. That information was hard-coded into the Scarfy code.

Ray (the creator of RayLib) has also created a texture packer for this task, which you can find here (link). You can even use it online. In brief, here's how to use it:

  • Drag all your images into the rTexPacker window. The texture packer will try to pack them into a square texture
  • Adjust the settings if needed in the right-hand column (e.g., choose the right texture size for the imagery, and maybe enable trimming
  • Choose an origin point for each of the individual images. This is the reference point for your sprite (a.k.a., object) when drawing it in the world. Useful origins could be the centre, or the bottom of your character's feet
    NOTE: rTexPacker currently doesn't have a way to automatically set the origin. I found it easier to edit the descriptor file manually instead of drawing each origin
  • Click the "Export Atlas" button (lower right corner), and save the atlas image and *.rtp descriptor file

All going well, you'll end up with a nice looking atlas texture, and a descriptor file:

coin

#
# rTexPacker atlas descriptor file (v2.0)
#
# Number of packed sprites:     6
#
# Atlas info:    a <imagePath> <width> <height> <spriteCount> <isFont> <fontSize>
# Sprite info:   s <nameId> <originX> <originY> <positionX> <positionY> <sourceWidth> <sourceHeight> <padding> <trimmed> <trimRecX> <trimRecY> <trimRecWidth> <trimRecHeight>
#
a coin.png 128 128 6 0 32
s coin_default_005 28 32 0 0 56 64 0 1 0 0 56 64
s coin_default_000 88 32 56 0 64 64 0 1 0 0 64 64
s coin_default_001 32 96 0 64 56 64 0 1 0 0 56 64
s coin_default_002 71 96 56 64 30 64 0 1 0 0 30 64
s coin_default_003 91 96 86 64 10 64 0 1 0 0 10 64
s coin_default_004 111 96 96 64 30 64 0 1 0 0 30 64

Coin credit: Flixberry Entertainment (link).

The descriptor file describes exactly where every frame of our rotating coin is, along with its origin and any other needed information.

Storing Multiple Sprites & Animations in One Atlas - Naming Conventions

There's no reason why a texture atlas should be for one object/sprite or animation. However, you do need a way of identifying which frame belongs to what. I've used a very simple naming convention:

<sprite-name>_<animation-name>_<frame-number>

You can see the naming convention at work in the texture atlas descriptor above. There are 6 numbered frames, belonging to the "default" animation for "coin." This texture atlas loading code parses the name into its components, and then organizes the individual frames into animations and sprites.

NOTE: Yes, I don't need this for the rotating coin, but I'm thinking ahead...

Parsing the Texture Atlas Descriptor

The descriptor is a text file with a very specific structure. The file itself has a comment section describing exactly what's stored (although you may need to guess what the individual bits mean unless you're an experienced graphics/game developer). The basic algorithm is as follows:

  • Read the first character in a line. If it's an 'a', then:
    • This is an atlas info line. Read the texture filename, and other details (easily done with fscanf())
  • Else, if the first character is an 's', then:
    • This is a sprite frame line. Read the sprite's name, origin, position, and all other details (also easily done with fscanf())
  • Else:
    • Ignore the line (e.g., the comments)

My TextureAtlas class loads in the texture, and then creates sprites, sprite-animations, and frames as described. These are stored in TASprite, TASpriteAnimation, & TAFrame classes. There's also a TASpriteAnimationPlayer class to handle animation playback. I don't want to go into the details. Feel free to check out the source code below (look for TextureAtlas.cpp|h).

Using the Texture Atlas

With the texture atlas loaded into an appropriate data structure, the next step is to use it. For that, I created a Collectables class. As I said earlier, creating a new class/module for every object type is tedious. Bronze coins, gold coins, diamonds, emeralds and gems all behave the exact same way. So, we can use a single class for all of them, and simply load different sprites from our texture atlas.

The Collectables class has code to load a texture atlas, and get the "coin" sprite. It also creates a physics object for the coin, so it's part of Scarfy's virtual world.

Adding Coins to the World

I'd probably add coin "spawning" points to the tile-map for a real game, but that would mean even more code to write. So, I hard-coded a loop to add 200 coins to the scene:

for(unsigned i = 0; i < 200; ++i) {
	float xPos = (float)(((double)rand() / RAND_MAX) * sceneSize.x * worldScale);
	std::shared_ptr<Collectable> coin = std::make_shared<Collectable>(
"coin", DATADIR "Collectables/coin.rtpa"); addActor(coin, b2Vec2(xPos, 0.0f)); }

With all of that done, Scarfy now has shiny gold coins to collect:

 RayLib 2D Challenge Part 10 Screenshot - Scarfy and coins

What's Next?

There's just one problem: Scarfy can't collect the coins. He can just push them around. That's because the coin interaction code hasn't been written yet. I didn't want to delay this episode of the RayLib 2D Challenge any longer, so that's for next time...

Click here for part 11.

Download the Source Code

Click here to download the source code.