EzSpriteSheet: a free and open source sprite sheet creator
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.
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.
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:
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:
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
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:
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:
- stb
- giflib
- libwebp
- libpcre2
- QtCreator
- RectangleBinPack
- Sprite art courtesy of @DynoPunch