After working on our Godot + Rust game fulltime for one year it is now up on Steam

496 points progfu

I know this isn't a standard /r/rust post, but considering how few games are made in Rust I thought it could be interesting to a larger Rust community to see a non-trivially sized Steam release.

It's almost exactly one year since Ludum Dare 48 where everything started. A little before that I quit my job, and a little after that my second daugher was born, and a little after that my wife (who's also a programmer) joined me in working on our game full time. Or at least as much as having two toddlers allowed. Fast forward one year, and we've just released BITGUN on Steam.

The game started as an extremely simple top down shooter in Godot. We've made some smaller games in Unity before, and this was the first real attempt at trying Godot, as we got fed up with some performance issues in the Unity editor.

For the first few months the development went great using pure GDScript and I actually quite enjoyed using it, but some issues were popping up. The biggest one we couldn't solve in GDScript was pathfinding, as the builtin Navigation2D didn't do what we needed, and a custom solution in GDScript would be far too slow. We also had some aspirations for PCG, and again doing that in GDScript proved to be an issue for multiple reasons.

One innocent tweet quickly lead us down the godot-rust path. We started using the pathfinding crate for our pathfinding, egui for in-game UI, hecs for some architectural stuff, and serde for serialization of game configs & save files.

With the newly gained Rust powers I somehow thought it would be a good idea to also write our own behavior tree library. A bad idea for many reasons, one being that I've seen too many GDC talks about behavior trees, and didn't realize that they were a complete overkill for what we were doing. Despite that, it works reasonably well, and after I also wrote a behavior tree visualizer in egui and integrated puffin in the game we were able to get some reasonable performance out of behavior trees.

A unique part where Rust did help with performance was our blood canvas. I've always disliked when blood decals in games disappeared after a while, so one of the early features in BITGUN was a level-wide texture into which blood and other debris gets written, to allow for "unlimited destruction" without sacrificing performance. Initially this was implemented in GDScript, and despite what literally everyone has been saying online, writing to textures on the CPU side is more than fast enough. Here's a simple bevy demo drawing roughly 2M pixels per frame at 60FPS, and one more that's a little bit less random.

One benefit of drawing into textures on the CPU is that adding little bits of procedural graphics is extremely easy. In our case each blood particle paints a blood trail, and paints on walls on impact. Here's an example of "lots of blood".


At the time of release there's roughly 7500 lines of Rust and 4200 lines of GDScript. We had around 3000 lines of GDScript when we introduced Rust, and rewrote about half of it in Rust initially.

As for our experience with godot-rust I have to say the feelings are mixed. Considering we use both GDScript and Rust quite heavily and call both from GDScript into Rust and from Rust into GDScript and pass objects around we've had quite a few complications. As far as godot-rust as a library is concerned, it works very well. It does however remove quite a few of the safety benefits of Rust (lots of unsafe due to the way GDNative works), and we've had some weird crashes due to bugs in our code. I'd say if you're thinking about using godot-rust it'd be easier to go all-in on Rust and not try to have parts of state in both languages.

I wouldn't say there's any issues on the side of godot-rust itself, but there are problems in Godot that don't exist in Unity/Unreal. One being terrible 2D performance, the number of draw calls with 2D lights and tilemaps (basically no batching), and overall profiling GDScript itself isn't a great experience when compared to Unity's profiler. Adding Rust into the mix takes this to a whole another level, and honestly at some point we just cut down on gameplay features due to being unable to figure out the source of some of our performance issues.

The experience with Godot has also been mixed. I see a huge number of people praise it for how lightweight and fast it is, and initially that's what got me sold as well. But as our game grew and our scenes became larger, it became very difficult to do anything in terms of level design, as operations like duplicating a node would take 5-10 seconds. Rendering performance also quickly became a problem, and while this is something that'll get fixed in Godot 4.0, I still think it should be mentioned.

Similarly the experience with Rust has been changed quite a bit by the integration with GDNative. I've made a few smaller games in pure Rust with Bevy and Macroquad as well where the "engine" is designed around Rust as a language. In the case of GDNative it becomes a different thing depending on the amount of Rust that a particular feature uses. If it's something large enough where a big part of the code is purely in "Rust land" everything is generally okay. But considering the amount of GDScript we had we also had a lot of very thin Rust features that worked close to the GDNative layer.

I know the first thought that probably comes to everyone's mind is that the solution is to just avoid writing GDScript and only use Rust. The problem at least for us was that while Rust is productive for some things, it's definitely not nearly as convenient for simple gameplay logic as GDScript, and in a lot of cases it felt like a huge overhead to take something that would be 5 lines of GDScript written in 1 minute and turn it into a pure Rust alternative. That being said, this was a very new experience for us after years of using Unity, so certainly someone with more experience could design things in a better way.

To keep it short, my conclusion for using Godot + Rust would be that if the game is big enough and/or systems heavy, it could be a great fit. In our case we started systems heavy with lots of dreams of PCG, and in the end cut down a lot of features due to scope creep, and the tradeoff probably stopped being worth it.


As for some marketing data for those that are curious. At the time of release we have ~1400 wishlists, about half of which we gained during the last Steam Next Fest. We've had one playtest with around 130 participants.


The whole process was a life-changing experience for us. BITGUN was by far the biggest game we've ever made, and by far the biggest effort I've personally put into anything. While I've had many software engineering jobs I completely misjudged what building an actual complete game with non-trivial features would be like. One year ago I was confident I could implement a lot of features myself. Rust makes a lot of difficult things possible, which also makes keeping the scope small very difficult.

Compared to other languages, almost every crate we tried worked very well. I don't even remember if we ran into any weird bugs on the side of Rust libraries. This also meant we initially used a lot of things, and ended up throwing away a ton of code and features later for the sake of actually finishing the game. I remember as I discovered tracing and being excited about the detailed instrumentation it provided, only to never actually use it in practice after I integrated it into our codebase. I remember how excited I was when I finally implemented a completely custom GUI debugger for our behavior trees, only to stop using it a few days later because the same person implementing the debugger and the library also implemented most of the AI, which meant the debugger was only used for debugging a few initial bugs, and after that was practically useless.

Rust is amazing at many things, but like Spider-Man taught us, "with great power comes great responsibility", and we definitely didn't use its power well. We never got to write any multithreaded code, we never properly used our ECS, we never fully ported our UI to egui, we never fixed our player controller being a gigantic spaghetti mess of half the state in GDScript and half in Rust, and we never got our PCG to a state where it was good enough, and after almost 2 months of work we removed all of it and just hand-designed levels instead.

That being said, Rust did allow us to try things we would never do otherwise (such as a RPG-style drag & drop inventory system), and we learned a lot from it.


If you have any questions I'm very happy to answer any of them!

Lastly, if you'd like to support us, consider buying BITGUN on Steam :). If you end up trying the game, please consider leaving a Steam review. It would mean a lot to us, being a small 2 person studio, as the Steam reviews directly influence the game visibility, and we don't have a big marketing bugdet, so organic Steam traffic is where almost all of our sales will come from. We just want your honest opinion, we are not asking only for positive reviews! Even the negative ones help.