That was the question I had in mind for a while as I heard about Rust but never really had a deep look at it. It turns out that one day Dan Olson, a game developer that I follow on Twitter, started mentioning that programming language as he was writing some tools and experimenting with it.
I still don’t know exactly why, but I wanted to try it out. I’ve been a long time fan of Go as it brings the good of C-like programming and cuts out most of the C-like cons at the same time, but it’s not really tailored for game development due to its garbage collector and a not yet solid mobile deployment system. On the other hand, Rust guarantees memory safety, has no garbage collector, can interoperate with C libraries and isn’t really that hard to deploy on iOS and Android.
Where to start from?
As usual I wanted to start with something not so easy. Let’s see if I can make a small game, make it run on iOS and Android and have some sort of hot reloading of code while developing on my Mac.
That might sound a bit bold as a statement, but I read Michael Fairley’s post about how he made a game in Rust and I thought that if he did that, I could as well. It’s just that I had no previous experience with Rust, so I might need some time to get all the pieces up and running.
I threw together a quick prototype using the SDL2 bindings for Rust. A simple red screen with a white triangle. That took less than an hour to put together (please keep in mind I had zero knowledge of the language syntax). Getting the whole Rust development environment and the build script together was very quick and I love the fact that you can make your build scripts with the same programming language as your game. That takes a lot of the problems I usually have with CMake out of the way.
Next up was hot reloading. I wanted my gameplay code to live in a separate dylib that could be hot reloaded as soon as I changed a line of code, without having to recompile the whole game (ok, at this point I had near to no code, but I already had in mind to port my game engine from C++ so that would make it easy to separate the engine from the gameplay code and be able to quickly iterate the gameplay).
What’s good with Rust is that there are a lot of packages and one of them is dynamic_reload. Dynamic_reload takes care of all the low level code to unload and reload dylibs, so that the code I needed to write to have it up and running was a mere 15 lines of code.
iOS and Android
So I had a simple prototype running on macOS with hot reloading and it was about time to port it to iOS and Android.
Setting up the iOS project is pretty straightforward. Just make a new Objective-C iOS project in Xcode (I am used to Objective-C and I have way less experience with Swift, so that made more sense. You might probably want to start with a Swift project, though), change a few parameters in the project configuration so that it picks up the library of your game and you’re set to go. Well, not really. You still have to setup your code to work in a different way based on the kind of OS. That’s where the #[cfg] attribute comes handy.
You have to make sure that hot reloading is off as you don’t want that in your iOS or Android builds. On top of that, you also want the main() to point to a different main() but we’ll get to that with Android. You would probably want to have the toolchain take your library and place it somewhere in your iOS project. On the other hand, I did the exact opposite. I downloaded the latest stable SDL2 source code, compiled it as iOS library from its own Xcode project and then copied the resulting libraries (both for the device and the simulator as they have different architectures) to my own Rust project in the corresponding target/[platform] folders. That way I can compile my Rust project with the SDL2 dependencies and be sure that they can be linked. Next I opened up my own Xcode project and made it run on the device. Surprisingly, it worked like a charm.
I have my own C++ cross-platform game engine. I know how and when Android gets in the way. So I was prepared to what lied in front of me. Android needs a mix of Java and C code. In this situation the C code is my Rust code. The Java code can be copied and pasted from the SDL2 activity that can be found in the source code repository. But! The SDL2 Android project is still using the old Ant build system and that’s going to be dropped sooner or later. I started up Android Studio and made a new Gradle project. I copied over the SDL2 activity and started hacking the Gradle build script to have it include both the SDL2 C code and my Rust library. It took me some time to get things right, but once again I had my mini game running on Android. Mission accomplished!
I know that a white triangle on a red background isn’t really a game. But I didn’t want to write a small game from scratch at that point. It really made no sense. All the basics are laid out and it’s just a matter of writing the game code. But since I couldn’t resist, I started porting my C++ engine over to Rust. It took me a few days to get my rendering and sprite batcher working with Rust. I can’t say it’s been easy. The borrow checker, together with lifetime management are damn hard to work with. It takes a while to get used to how those work and how to write your code the correct way. I had to ask for help in the newbie Rust IRC channel and I must say that people in there are just awesome!
Now you can see a single wabbit on the red screen.. better than the white triangle, but still not much. I kept porting my code and now I’m stuck with the Entity-Component-System. My C++ implementation of the ECS is pretty simple. I have a Scene object that manages an EntityList that keeps track of the spawned entities. Each Entity has a ComponentList that holds the Components that are attached to that entity. This is an easy way to structure this kind of code with an object-oriented language. But since Rust has no knowledge of OOP whatsoever, things get complicated. I have yet to find a solution and a good design for it. I’m pretty sure I have to change the way I think about it. You can read more about that in the post I opened on the Rust forums.
This is just the beginning and I wrote enough in this post for the time being. I guess I’ll have to follow up as soon as I make progress. Thanks for reading so far. You can find the current code on GitHub.