Composable Sprites Part 2
Link to Composable Sprites Part 1
For this week's update, I'm going to talk about the different layers, the order, cosmetic variations (hair/eyes/colors) vs more functional compositions (e.g. showing equipment), and a brief description of how it's implemented in the game.
Layers and order
Player-character sprites will be a composition of the following hierarchical layers:
- Head
- Eyes
- Mouth
- Beard
- Hair
- Helmet
- Body
- Cape
- Main hand
- Off hand
- Feet
To compose a sprite, we start with a blank transparent image and we add layer by layer. The order is slightly different than the one shown above, as for example feet are after the body but before the hands.
Attachments and relative positions
Some elements are not animated (e.g. equipment, beard, eyes) but some others are (body). So how do we compose the layers for an entire animation? By requiring only the root layers to be animated (above: head, body, feet), and animating the rest using "attachment points". When a player is holding a sword, we do not want the sword layer to have to mirror the player's movement. So we specify a 2d pixel coordinate for the sword's sprite, and a 2d pixel coordinate for the sword's parent layer, which is "body". The vector from the sword's coordinate to the parent coordinate is the offset we need to apply to the sword pixels in order for them to be aligned to the parent layer. The 2d pixel coordinate of the body is animated (we need to specify one coordinate per animation frame), so for any animation frame, we match the animated coordinate of the body with the static coordinate of the sword, and the result is a sword that's animated.
Cosmetic variations
While originally the goal of the composable sprite system was to just visualise the equipped items on certain creatures, with a minor extension it can be used for including different hair styles, eye shapes, mouth shapes, beard shapes and so on. Why not? I'm not putting much effort on this as the entire sprite collection might still be replaced later, but the underlying system is there. So, in the next release, some level of customisation will be included.
Special pixel values and palettes
Let's say we want to support multiple hair colours. Instead of creating identical versions, we can create a version with a "special" colour (the unnatural magenta) where, if we detect such a coloured pixel and we have specified a palette color in a configuration somewhere, we can "recolour" a pixel. We can even allow shades of the palette colour if we use shades of magenta. This can be applied to any layer, and will find heavy use, as it allows for obviously different colour hair and (optionally) beard, but also different colour skins, different coloured mouth or eyes, and later on it even could be applied to represent the same weapons/armour made up of different materials, although that might need a bit more flexibility and creativity.
Input and output sprites, and composer system
All the sprites that can be used as composable elements are in one texture array. The composed sprites are generated and placed in a different, dynamic texture array. I've written a system (in the ECS sense) that handles events related to composable sprites, e.g. changing equipment or making cosmetic changes (changing haircut etc) for a creature that supports composable sprites. When such an event is handled, we calculate the new layer configuration (which sprite, what palette, etc), create a unique ID out of this and check if such an ID already exists in our dynamic texture array. If it doesn't, we build the sprite and link it to that ID. If it exists, great, just use that already prebuilt sprite. This would be handy if we are in a settlement area with many guards (or bandits) who wear the same equipment and look the same: they should all use the same sprite. Because this description doesn't include deletion, when we run out of space, we do a cleanup pass where we detect all used sprites and remove/unregister all the rest, freeing up slots in the dynamic texture array.
Plans ahead
- Next step is to prepare the sprites for more equipment, especially armor. There are a lot of lovely weapon collections out there, but my sprite size limitations are very harsh, so I'm extracting the weapons from oryx sprites.
- Another step is to prepare the base sprites for another race. Eyes/mouth should be portable, but hair/beard might need changes to accomodate different head shapes. Weapons/shields should be portable via attachments, armor is not portable so it would need some manual work. This would be to prove that there is the potential of sharing a lot of sprites even among different races.
- Ideally, later on, I should expand this composition functionality to any creatures that support equipment (currently some skeleton types, goblinoids and necromancers).
- Finally, the current sprite size of 24x24 for character sprites with composable elements ends up being quite limiting. I'm tempted to make the composable ones to be at least 32x32, which will allow a little bit of space for tall or wide weapons or shields.