Early in 2023, I wanted to learn a new skill just to have something else to do. As I previously participated in some game jams a few years ago, naturally I went searching for another one. Unfortunately, I'd already missed the very start of the year, so I missed out on quite a few of the bigger ones. But then I stumbled upon the HaxeJam 2023.
Game Development With Haxe: Lessons Learned From Making a Submission to HaxeJam 2023
Early in 2023, I wanted to learn a new skill just to have something else to do. As I previously participated in some game jams a few years ago, naturally I went searching for another one. Unfortunately, I'd already missed the very start of the year, so I missed out on quite a few of the bigger ones. But then I stumbled upon the HaxeJam 2023.
A bit of an explanation might be in order before we move on – a game jam is a competition of sorts in which each of the participants (or teams) set out to make a game in the limited amount of time available to them. This alone makes it a good programming exercise because you have to manage your time correctly to end up with a working game at the end.
Some jams are extremely short – just one or two days – but most are more relaxed with the deadline set at a week or two after starting. At the end of the time limit, the games are submitted and played by other participants, judges, and random players who submit their scores and feedback. A common requirement is to also limit the reuse of code and assets, within reason of course – they’re usually supposed to be new games, not extensions or continuations of previous projects.
To help focus the development (because "make a cool game" isn't very helpful if you only have 48 hours to do so from scratch), game jams typically also have one or more additional requirements. For example, to make the game in a specific engine or language, have it run in a specific environment (like a GameBoy emulator or certain browser), or to include a specific theme in the game.
HaxeJam had all of these, making it a perfect candidate for "something else to do". We were given an engine requirement, obviously, and a whole week to make the game. It had to be playable in a browser, and the theme was "again and again". I liked the theme and I was really curious about Haxe – I'd heard about it before, but never really tried it out. Well, since I had the opportunity, why not give it a shot?
Preparation
I came up with the idea of exploring an endless dungeon. The player would have limited health and could choose over one of the four rooms. Only one room would lead the player forward with the rest just containing other things – be they traps, treasure to find, or monsters to fight. Since you don't know which room of the four is the exit, you have to risk losing health in order to proceed. Health could be replenished by using items found in the dungeon. If (or rather, when) the player eventually loses, they would be returned to the base, have their health replenished, and could start exploring again. In the base, the player can also increase their health, allowing them to go further and further.
With the general game idea more or less finished, I started looking for and preparing the game assets. Most of them were free assets from OpenGameArt, but I quickly found an issue with that approach – I needed room images, and quite a lot of them. Each type would be presented in a few different ways so as to not completely bore the player or let them know what to expect too easily. I also needed some kind of title screen and some staircases to serve as entrance images.
I could, of course, have drawn all of this myself and probably get them looking kind of okay, at least given enough time – and, looking back, I probably would have chosen that route if I wasn't also learning the language along the way – but I really felt I wouldn't be able to make all of it fast enough. What I decided to do instead was to use Stable Diffusion to generate all of those images. After some experimentation with the prompts, I managed to generate three images for each room type that looked good enough and had a more-or-less consistent art style.
The language question was already sorted out by the game jam itself – it was a HaxeJam, so quite obviously you had to use Haxe. The language is just one part, though – you don't usually want to rely on the language itself but rather want the affordances provided by a game engine so you don't have to invent things from scratch. The choice of game engine sorts out things like game update loops, rendering 2D or 3D elements, handling keyboard input and mouse interactions, and so on. There are a few engines available for Haxe, with the most popular being HaxeFlixel. I ended up using Heaps instead, mostly because from browsing the tutorial and code samples it looked more interesting and less restrictive to me.
So, for most of the week, with Stable Diffusion steadily but slowly plugging away in the background, I began to familiarize myself with Haxe. It turned out to be really unsurprising for me as a programmer. It's an object oriented language, so it has all the brackets, for loops, switch statements, classes, and methods that one might expect from a programming language. If you can code in Javascript, you can pretty much code in Haxe. It needs some getting used to if you’ve never programmed a game before – you will need to learn a few things like update loops and so on – but nothing is as hard as it may sound.
Developing the game
I managed to end the week having a working title screen and a main game screen with the ability to switch between them. From Friday evening through Sunday I worked pretty hard on finishing the game. Of course its design ended up slightly different to what I described above due to time constraints – there aren't any actual items to find, you just lose or gain health automatically. Similarly, the base had to be simplified, and you just automatically convert any gold you find in your run into more health rather than having a proper upgrade shop or similar.
I'm not going to describe the whole process or walk you through the whole code because the game is mechanically pretty simple and by design has mostly UI elements, so there aren't any surprises there. You can go through my github repository for the game and see how it works if you like. Instead I'd like to highlight a small thing here that I had to dig around random repositories quite a bit for, which is saving and loading data.
Let's start with the code for the whole class and then go through it step by step.
class App extends hxd.App {
public static var instance:Scene;
public static var playMusic:Bool = true;
public static var playSound:Bool = true;
#if hl
static var SETTINGSPATH:String = Path.join([Sys.getEnv("APPDATA"), "Save/", "/settings"]);
#else
static var SETTINGSPATH:String = "Quaashangma-jam/settings";
#end
public static function saveSettings() {
#if hl
sys.FileSystem.createDirectory(Path.directory(SETTINGSPATH));
#end
hxd.Save.save({
music: playMusic,
sound: playSound
}, SETTINGSPATH);
}
public static function loadSettings() {
var data = hxd.Save.load(null, SETTINGSPATH);
if (data != null) {
playMusic = data.music;
playSound = data.sound;
}
}
static function main() {
loadSettings();
App.instance = new game.Title();
}
}
Firstly, there's our main class App
inheriting from hxd.
App
, which is where the player will end up after loading the game. We're declaring two public variables here for the sound settings so that we can refer to them from other places in the code.
class App extends hxd.App {
public static var instance:Scene;
public static var playMusic:Bool = true;
public static var playSound:Bool = true;
Below is the only tricky bit in this code – obviously the saving process has to be different when running the game in a browser versus natively. The browser just saves an entry in its Local Storage, whereas the native application has to create a file somewhere on the disk. Using the conditional compilation directive helps us here. If the compiler detects the Hashlink library (which is present only in native exports), it will compile the block and define an actual filesystem path, otherwise – since it's a Javascript output if not using Hashlink – it defines just the name for the Local Storage entry.
#if hl
static var SETTINGSPATH:String = Path.join([Sys.getEnv("APPDATA"), "Save/", "/settings"]);
#else
static var SETTINGSPATH:String = "Quaashangma-jam/settings";
#end
The next bit is the actual saving of our data. Once again we have a conditional here that gets included if the game is run natively through Hashlink. For some reason, file systems don't really like it if you try to save a file into a non-existing directory, so we have to create one first.
Saving the data itself is pretty straightforward and abstracted well by the hxd.
Save
class, which allows us to just call the save
method and not worry about the underlying mechanics of opening files, writing to them, or saving them in a browser.
public static function saveSettings() {
#if hl
sys.FileSystem.createDirectory(Path.directory(SETTINGSPATH));
#end
hxd.Save.save({
music: playMusic,
sound: playSound
}, SETTINGSPATH);
}
Loading the saved data is even more straightforward – once again we use a method from the hxd.
Save
class to load the data, then assign it back to our variables if not null.
public static function loadSettings() {
var data = hxd.Save.load(null, SETTINGSPATH);
if (data != null) {
playMusic = data.music;
playSound = data.sound;
}
}
Lastly, the main()
function of our class. This is what gets executed once the game has finished starting, so we need to make sure we load the saved state here. After this is done, we can begin loading the actual title scene with the App.instance = new game.Title();
line.
static function main() {
loadSettings();
App.instance = new game.Title();
}
Publishing the game
On Sunday evening, close to the deadline, I started packaging and getting ready for the final game jam submission. I finished preparing the game's itch.io page with some screenshots, tagged the page appropriately, made sure all the code was on github like it should be, and eventually pressed the submit button on the game jam page with about half an hour to spare.
Now it was all in the hands of the other participants and all I could do was wait for the results.
The results are in…
…and ultimately, my game was rated by other jam participants as seventh out of 14 games.
Not that great, but also not that bad considering I made most of it in two days and spent the other five learning Haxe and Heaps. The few comments I received were that it was a pretty promising idea, but should be expanded into something more. I completely agree with this, and plan to do so in the future. There wasn't much feedback, but that’s not surprising at all since this also was a pretty small game jam.
Even though I placed right in the middle of the pack, I'm really satisfied with this result and view it as a real success. After all, I made – and what's more important, finished – a whole game from scratch, all while learning quite a few new things.
The pros and cons of making a game in Haxe with Heaps
Let's sum it all up a bit:
Pros
- It felt really good to finish a project and have people play it.
- The Haxe language was really easy to pick up and felt really logical to use.
- The Heaps engine had just enough features to help with game development and not force things to be done in specific ways.
- Exporting the game to JS to run in browsers worked flawlessly.
Cons
- Documentation and tutorials, especially for Heaps, were a mixed bag. On one hand, the API docs for both were pretty great. On the other hand, the samples were pretty limited due to HaxeFlixel being much more popular, so often I had to search Github for other game jam submissions to see how I should do something on a higher level.
- Mac exports kind of worked locally, but packaging and distributing them as an application just didn’t work correctly despite me spending far too long trying different ways to get them to. Fortunately, native desktop releases weren't a requirement in this game jam.
- If you're limited by time, learning a language while making a game isn't the best idea. Fortunately, Haxe proved easy enough to make it possible.
- I really wish I had more time during the jam to create proper graphics based on the AI-generated ones. Having a more consistent art style would be much better.
There are definitely two aspects here: the game jam side and the programming side.
From the programming side, there appear to have been more problems than it was worth. However, most of these can be attributed to me being new both to the language and the Heaps engine rather than any difficulties with them specifically.
The only real issue here is the lack of a good Mac export, which might improve with time since it almost worked except for one library that refused to load. There are a few open issues on Github about exactly these kinds of problems, so before next the HaxeJam it might already be a non-issue or at least some workaround may be found. It's not necessarily a deal breaker if all you care about is the browser version, but it’s still a little disappointing.
Conversely, from the game jam side, it was a pretty good result. I got a game made from zero to a playable state, it works in a browser like it should, people can play it, and a few of them even liked it. That really feels good, even if we're talking about a simple game like mine, and I would recommend every programmer to try participating in a game jam at least once.
On-demand webinar: Moving Forward From Legacy Systems
We’ll walk you through how to think about an upgrade, refactor, or migration project to your codebase. By the end of this webinar, you’ll have a step-by-step plan to move away from the legacy system.
Latest blog posts
Ready to talk about your project?
Tell us more
Fill out a quick form describing your needs. You can always add details later on and we’ll reply within a day!
Strategic Planning
We go through recommended tools, technologies and frameworks that best fit the challenges you face.
Workshop Kickoff
Once we arrange the formalities, you can meet your Polcode team members and we’ll begin developing your next project.