Writing a game engine from scratch in a language you’ve never used before is certainly not the most common way to go for novice game developers, but that’s what Alex Butler did when he quit his software development job in the fintech industry in 2017 to create a puzzle game using Rust.
Games are software programs, and just like other software programs, most games are written with the help of development frameworks — standardized coding tools and libraries that others have already written. Frameworks are like prebuilt foundations and frames of houses — they get a lot of the dirty work done in advance and make it possible for developers to more easily build a variety of different structures. But because frameworks try to accommodate a wide range of possible projects, they can also feel cumbersome, and they’re not without limitations.
“They can get you going on an example very fast, which is great,” Butler said. “But then once you’re six months down the line of the project, you find out the framework just can’t do a certain thing, it has a certain limitation. And resolving that is often very difficult.”
One of the most popular development frameworks for creating games — also known as “game engines” — is the Unity engine. It’s used to write a large portion of mobile app games, and can also create games that run on a variety of platforms. Butler said developers add their own logic by writing code in a language like C#, which calls functions from the Unity engine.
But ultimately, Butler just wanted to write his own game engine for the experience of tinkering in the lowest levels of game development.
It’s not the best route for everyone — Butler said it helps to be familiar with useful libraries that already exist, so developers can enjoy the flexibility of creating a custom game engine without getting bogged down in the details of creating every software capability from scratch.
‘Gaming Is High-Performance Computing’
Butler had originally intended to use Rust only for his custom game engine, and Python for the rest of the game, but he enjoyed Rust so much he ended up writing the entire game with it.
He picked the language for writing the low-level game engine because it leaves memory management up to the developer. That’s different from higher-level languages like Java and C#, which manage memory behind the scenes by automatically deallocating unused memory, in a process called garbage collection.
Garbage collection is generally desirable — it means the developer doesn’t have to spend time allocating and deallocating memory every time, or worry about forgetting to do it and accidentally causing a memory leak in the program. Butler said automatic garbage collection can also be more efficient than doing it manually, because the heap gets cleaned out all at once after enough debris has accumulated, instead of a little at a time.
But for programs that are memory intensive, there is also a latency cost to the automatic process.
“If you’re writing the game loop itself, garbage collection invariably introduces a pause into the execution,” Butler said. “And game loops need to run relatively fast and without stutters.”
“If you’re writing the game loop itself, garbage collection invariably introduces a pause into the execution.”
He said for a language like Java, garbage collection can take one-tenth of a second, which is enough to cause a frame drop. Languages with manual memory management may require more work upfront, but they give developers control over when memory deallocation occurs and reduce software latency by allowing them to clean up as they go. Game engines are generally written in lower-level languages with manual memory management, like C or C++, that offer this flexibility.
Rust is a good compromise because it allows manual memory management while providing more features that guard against its pitfalls than other languages, Butler said. The Rust language itself reinforces memory safety by catching memory errors at compile time, instead of having developers find out about a memory leak after the program is already running and can cause a bigger problem.
“Gaming is high-performance computing,” Butler said. “I think that’s what attracts game developers to Rust. It’s an alternative to C++, where Python and Java just aren’t in the same way.”
Puzzle Game Design Is About Working Backward
Robo Instructus, Butler’s coding puzzler, is a thoroughly nerdy game. Players learn to use a simple in-game scripting language to maneuver a little robot through a series of coding puzzles until it completes its mission. The player ends up getting a decent primer on programming, but Butler said it wasn’t actually built to be an educational tool.
“It’s a game about programming itself,” he said. “I wanted to capture why I loved programming.... I wanted it to be a game about running algorithms and feeling cool about solving stuff.”
Since the game is designed for anyone to play regardless of programming experience, Butler had to create a gentle ramp-up during early levels that introduced basic programming concepts, and build up to the point where players could solve algorithmic puzzles. Butler struggled with setting the appropriate pace of learning. What helped was play testing, where he gave friends an early version of the game and watched them play.
“It was really valuable whenever I saw people being stuck on something,” Butler said. “It’s amazingly valuable watching someone play your game, really watching them — head over their shoulder, or seeing them sharing their screen. Because they get stuck on things that they never tell you about.”
Initially, he introduced new programming concepts like
if and immediately expected players to solve tricky challenges using them. But after some play testing sessions, he added more early levels. He realized that splitting difficult concepts into several different levels, instead of combining them into a single puzzle, reinforced learning and helped players avoid getting stuck on fundamentals.
“It’s amazingly valuable watching someone play your game, really watching them.”
Butler said the way he created puzzle challenges was by working backwards from ideas of what sorts of game play would be interesting to solve.
“You have to sort of think in reverse of your playing,” he said. “Trying to think of a problem, and then trying the problem, of course — you have to design the problem before the solution. So you have to think, ‘What would be fun to solve about this problem?’”
One challenge he enjoyed coming up with involves orientation, where players have to figure out how to continuously track the direction they are facing in order to reach their destination. Up until that point in the game, players never had to worry about cardinal directions. Initially, Butler dismissed the idea as too difficult, but realized it was doable once he created a proof of concept for himself.
“When players get to it they’re like: ‘Well, how the hell do I know which way I’m facing? Impossible!’” he said. “So it’s still a place where people get stuck, but it’s an example of something where I thought initially it would be too difficult, but then when I actually solved it, I was like, that’s kind of fun.”
Rust Game Development Has a Young but Thriving Community
In addition to play testing with friends, he also appreciated the support from Rust’s small but growing group of game developers.
“I think one of the strengths of Rust is the current developer community,” Butler said. “Maybe that’s just because it was a younger language, where you have more enthusiastic people — and they’re quite low-level [coding] people.”
Despite the language being newer, Butler found plenty of helpful libraries in the Rust ecosystem that others had already made, which he was able to use in his game. One open-source library he used a lot was gfx-rs, a Rust wrapper around the OpenGL library, which helps developers render graphics. He found interactions with library maintainers to be friendly and helpful, on the occasions he contributed to libraries with pull requests to add functionality he needed.
Although he no longer works in game development, Butler enjoyed coding in Rust so much that he and a few other employees at his current company made the case for using it at work, and he’s now a full-time Rust developer.
“I personally find Rust a really productive language,” Butler said. “Even though it’s a lower level language, I find it just as productive.”