Thumbnail image

EzSpriteSheet: a free and open source sprite sheet creator

Sat, Jan 15, 2022 6-minute read

Making sprite sheets has never been easier: toss a folder at my new program and watch magic happen! This blogpost details both why and how I created a new tool to solve a common obstacle in low-level game development. EzSpriteSheet was a month-long commitment.

image

Motivation

I enjoy developing games from scratch. Managing sprite assets is usually a nightmare, though. The common approach involves “squashing” all the sprite animations into individual sprite strips, but this is unideal to me because information like frame timing is lost in the process. Additionally, duplicate animation frames are not able to be optimized away, there may be a lot of wasted padding pixels around each sprite, and you still have to worry about pivot points.

To me, the ideal structure for organizing sprites is a directory of nested folders containing animated GIF’s/WEBP’s, with the desired timing and pivot information already baked into each one. This way, assets delivered by the artists can easily be dropped in and used immediately. All the other sprite sheet software I tried was lacking in these departments, so I decided to create my own.

image

Multiple user interfaces

I enjoy using the command line. One of its biggest strengths is in automating processes. Once I decide on the parameters I want for generating a sprite sheet, I can add that command to my game’s build script and have it rebuild the sprite sheets automatically anytime the directory contents change. You don’t get that kind of flexibility with a GUI application! And retrofitting a GUI onto a CLI application is easier than vice versa, so I wanted to program the CLI first.

As the word implies, an interface gives the user a method of interfacing with the program. The same core functionality is exposed to the user by both interfaces. It’s only the way the user experiences the program that’s different. Think of it like this:

image

My command line interface looks like this. If you run the program without providing any arguments, it tells you how to use it:

/*********************************
 * EzSpriteSheet v1.0.0 <z64.me> *
 *********************************/
--Necessary Arguments--------------------------------------------------
  -i, --input     specify input directory where assets are organized
  -o, --output    specify output image database file
  -s, --scheme    select which export scheme to use
                    supported export schemes:
                      xml
                      json
                      c99
  -m, --method    select packing method
                    supported packing methods:
                      guillotine  (fastest, worst)
                      maxrects    (slowest, best)
  -a, --area      specify area of generated sprite sheet
                  width by height, e.g. --area 1024x1024
--Optional Arguments----------------------------------------------------
  -h, --help      prints this usage information
  -e, --exhaust   exhaustive packing (doesn't consider a sprite
                  sheet ready until it has tried fitting every
                  remaining unpacked sprite into it)
  -r, --rotate    rotate sprites when doing so saves space
  -t, --trim      trim excess pixels from around sprites
  -d, --doubles   detect and omit duplicate sprites (doubles)
  -b, --border    add padding around each packed sprite
                  e.g. --border 8 (for 8 pixels)
  -c, --color     treat pixels matching hex color as animation pivots
                  e.g. --color 00ff00
                  (complains if multiple possible matches are found)
  -f, --formats   include only images of formats in comma-delimited
                  list, e.g. --formats "gif,webp,png"
  -p, --prefix    append a prefix to each sprite's name when exporting
                  e.g. --prefix "extras/"
  -z, --long      use each sprite's long name during export
                  e.g. write 'player/walk.gif' instead of 'player/walk'
  -x, --regex     include only images with a file path matching
                  an optional POSIX regular expression
  -n, --negate    include only images with a file path NOT matching
                  the provided --regex pattern
  -v, --visual    visualize sprite boundaries (debug feature)
                  (makes each sprite's background a random color)
  -l, --log       specify log file (stderr is used otherwise)
  -w, --warnings  log only errors and warnings
  -q, --quiet     don't log anything

As for the GUI, I used Qt. The packaged Qt Creator has a really nice WYSIWYG editor for creating GUI’s. I used it to design the main window, pictured below:

image

External libraries are a life saver

When I program something, I want to make the thing that I’m making, not the components necessary to make the thing that I’m making. And that’s where external libraries come into the picture. They already exist, the work has already been done, and they are already battle-tested. Use them! The developers want you to!

For example, Google’s libwebp includes a helper function for loading both animated GIF’s (using giflib) and animated WEBP’s. Copying that into my project saved me a lot of time.

I also used the public domain RectangleBinPack for the rectangle packing. A full list of libraries I used can be found in the Attribution section of this blogpost.

Reference implementations

I wanted to make it easy for people to adopt EzSpriteSheet, so I provided reference implementations. I have written two so far: one in C using SDL2 for rendering, and another in JavaScript using an HTML5 canvas. Both display animated sprites on the screen, and demonstrate how to use EzSpriteSheet’s output in a meaningful way.

Try the JavaScript demo live in your web browser: https://z64.me/bin/ezspritesheet.js

image

Quality assurance

It’s important that programs work consistently and reliably. It should never crash. And the same input should always produce the same output. Undefined behavior should never occur, and if it does, don’t take it lightly.

For example: even one variable/allocation left uninitialized can make or break a program for some users but not others, because different CPUs handle stack variable initialization differently. Some will zero-initialize them while advancing the stack pointer, some will not. Then you get unexpected values, hence undefined behavior.

The process will vary across programming languages and across operating systems, but my favorite software for this when writing C/C++ code on Linux—which is the case for this project—is Valgrind. It helps avoid surprises like the scenario described above by checking for all of the following:

  • Uninitialized variable/memory usage
  • Invalid memory access
  • Memory leaks

Running Valgrind on Linux is easy once you have it installed: you run the same command you always run when testing your program, with the same arguments etc, but you prefix it with valgrind, like so:

echo "Hello, world!"

becomes

valgrind echo "Hello, world!"

When I run it on my program:

==993667== 
==993667== HEAP SUMMARY:
==993667==     in use at exit: 0 bytes in 0 blocks
==993667==   total heap usage: 43,399 allocs, 43,399 frees, 57,924,516 bytes allocated
==993667== 
==993667== All heap blocks were freed -- no leaks are possible
==993667== 
==993667== For lists of detected and suppressed errors, rerun with: -s
==993667== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

This is what you want: Valgrind is happy! Publish that code with confidence!

It’s already being used in a production environment

This racing game (Rogue Racer on Steam) uses EzSpriteSheet to produce a texture atlas for its HUD elements:

image

Features

As of this writing, EzSpriteSheet is more feature-rich than its competition:

Feature TexturePacker EzSpriteSheet
Rotate sprites to save space
Add padding around packed sprites
Detect and omit duplicate sprites
Trim excess pixels from around sprites
Users can write their own export modules
Automatic pivot point detection
Supports animated GIF’s
Supports animated WEBP’s
Open source
Price $39.99 per 2 computers Free

You read that right: it’s open source

And you can find it on GitHub.com/z64me/EzSpriteSheet

Attribution

The following libraries and software made this project possible: