A while back, I got an ominous and semi-threatening email from... my sprite packing tool. They had "detected" that I was using a Single User license on a machine also running Jenkins, a Continuous Integration (CI) tool. Indeed, I bought another license for this tool and set it up on my living room PC to build my game as I push new commits. I ignored this threatening email because the CI was broken anyway. Until yesterday, when I tried to revive the CI setup, and I saw that the stupid sprite packing tool broke the whole fucking build pipeline! 😡
If you look on the website for TexturePacker CI licenses, it will cheerfully inform you that you can buy a "TexturePacker Floating License Subscription" for €120/year (with tax). Every year! This price point is ridiculous if you're a bootstrapped indie like me. I could buy at least two sprites for that kinda money. But if you're working for a big corp (also me), that price point is way too low! The amount of paperwork involved in getting a purchase approved is the same whether the license is €120/year or in the low five figures.
But the problem this package solves is really not that complicated. It grabs images from a folder and puzzles them into larger images. Sure, bin-packing is an NP-hard problem, but there are plenty of algorithms that are "good enough." That's the difference between Computer Science and Computer Plumbing, after all. So I looked for an open-source alternative and found Free Texture Packer. Hilarious.
Feature-wise, Free Texture Packer is almost exactly what I need. It outputs a JSON file with the same keys and values as the other tool. The difference is that it can only output PNG images and not WebP, but that's not a huge loss because I can always hook up another step to the asset-building pipeline. 🔨
One wrinkle is that it stores folders and images as absolute paths in the project files:
{
"meta": {
"version": "0.6.7"
},
"savePath": "D:\\Projects\\SSSG\\intermediate\\resources\\sprites",
"images": [],
"folders": [
"D:/Projects/SSSG/resources/sprites/actors/bobby",
"D:/Projects/SSSG/resources/sprites/actors/chichi",
"D:/Projects/SSSG/resources/sprites/actors/riya"
],
"packOptions": {
...
}
}
I tried using relative paths anyway, but the package just chokes on that. I should really submit a patch, but in the meantime, I resolved this issue by creating a temporary project file with a bit of Python code that has the absolute paths set up correctly:
def fixProjectFile(self, project_path):
self.log.info(f'Fixing project file "{project_path}"')
with open(project_path, 'r') as input:
project = json.loads(input.read())
parent = str(Path(project_path).parent)
new_images = []
for it in project['images']:
stripped = str.replace(it, self.project_root, '')
it = str.replace(parent + stripped, '\\', '/')
self.log.debug(f'image "{it}"')
new_images.append(it)
project['images'] = new_images
new_folders = []
for it in project['folders']:
stripped = str.replace(it, self.project_root, '')
it = str.replace(parent + stripped, '\\', '/')
self.log.debug(f'folder "{it}"')
new_folders.append(it)
project['folders'] = new_folders
self.log.debug(str(project))
with tempfile.NamedTemporaryFile('w', encoding='utf-8', delete=False) as temp:
json.dump(project, temp, indent='\t', sort_keys=True)
temp.flush()
return temp.name
Conclusion
TexturePacker fucked me over for a measly €120/year. As a result, I looked for a free alternative, and I would be hard-pressed to recommend the tool now.
