Introduction

A little over a year ago, I wrote a post Why I Wrote a Game Rendering Engine (and Why You Probably Shouldn’t). This post is a short update on what I have learned about a couple of options mentioned in that post: Unreal Engine and Rust.

Contents

Unreal Engine

I wrote:

However, Unreal is huge and known to not be particularly well documented, is historically set up for first person shooter games (although that emphasis has become less with later versions), and requires a lot of memory to use. My computer was too underpowered to run the Unreal Engine, and I would have needed a new machine. My reading also suggests that Unreal produces bloated binaries, and I want my software to be lightweight."

I have since tinkered a little with Unreal Engine (still on a massively under-specced machine - a consumer grade laptop that I “borrowed” from my wife, that has a Tiger Lake-U Core(TM) i5-1135G7 @ 2.40GHz, 20 GB of RAM, and the internal Intel TigerLake-LP GT2 GPU). I decided that a simple project would be lots of frictionless balls bouncing in a translucent box (the box using Blueprints, the balls with C++ code). It was an interesting learning experience.

Bouncing Balls in Unreal!

Some observations:

  • Unreal Engine (UE) is very slow to use, and some capabilities aren’t there because they aren’t supprted by my hardware, and it had trouble finding Vulkan - that would probably be better once I have a machine that has the recommended specs.

  • UE is immensely powerful, and has many many options. The flip side of that is that the learning curve is absolutely huge, although that is also the case for Vulkan. However:

    • For Vulkan the barrier is largely up front - to get something working at all, I had to learn how a lot of moving parts go together (drawing a triangle and understanding how it gets there is an achievement), but once I have that, I have complete control, and I only need to learn other features of Vulkan when when I want to do something new.

    • With UE, it’s relatively easy to get something working (you can do quite a bit without writing any code), but the learning to do what I want would be very much ongoing.

  • UE is floating point based - I understand that by default it uses single precision floats, but can be configured for double precision. Double precision is great for just about everything in gaming (often single precision will be enough), but that doesn’t work for the Galaxy Project for two reasons:

    • The Galaxy Project needs to use 64 bit integers (sometimes 128 bit for internal calculations), as at the sort of scales I’m working at, even double precision floats would have jitter caused by rounding issues.

    • I have made a big effort to make all the procedural generation cross platform in case I ever want to do multiplayer, and floating point calculations will not guarantee that - I even wrote my own Pseudo Double library that returns the same results on different processors.

  • UE was originally written for a First Person Shooter game, and although it is now designed for wider use, the FPS orgins show, with options like “Can Character Step Up On” or “Can Take Damage”. It has a huge set of options for dealing with all sorts of physics, but I don’t need any of that, as what I’m doing is specialized enough for me to want to do my own physics.

  • For the Galaxy Project, I need to have very tight control over the graphics pipeline to be able to deal with the throughput of stars. That would certainly be possible in UE, but I would end up in the more obscure and less documented parts to work around the way UE expects things to work. I would probably end up fighting UE to get things done the way that I want, and my previous experience is that tools that I need to fight are often the wrong tools.

  • I didn’t get far enough to get an idea of how UE performs on lower end machines, but my reading suggests that it’s better for heavyweight games on machines with good specifications. I want the Galaxy Project to be able to run on any 64-bit machine that is Linux, Android or Windows and supports Vulkan.

  • Where UE would really help:

    • Cross platform development - a major pain point of rolling my own engine in C++ is that I have quite a lot of #ifdef statements to deal with different platforms, and UE would deal with most of that.

    • It would offer a lot of useful infrastructure - controls that are more mature and stable than Dear ImGui, handling of game loops and events, and game saving would all be great to have.

    • Multiplayer. Hand rolling this is possibly beyond my resources as a solo developer. UE would definitely offer a leg up if the project ever progresses to that stage.

In summary, Unreal Engine is very powerful and has a lot to offer, but probably wouldn’t have been the right tool for this project - I think to do what I want it would have been an even bigger learning curve than Vulkan, and I would end up wanting to do some things my own way and having to work around the engine.

Rust

I wrote:

I really, really wanted to use Rust, as I find Rust a very appealing language. I like speed and safety and Rust is one of the few languages that realistically offers both, so it is a top contender for any new projects. However, at the time that I was considering it, Rust Android development didn’t look baked enough to use. Also, vector intrinsics were supported for x86-64, but were not stable for ARM Neon. It just didn’t seem ready.

This has now changed, and I’m considering porting the project to Rust, using vulkano for the graphics driver and egui for the UI. There are some issues that suggest that the Rust ecosystem is still maturing, but I’ll learn more as I investigate.

That port is now going ahead, and it is slow (partly because I’ve been busy with things like moving to New Zealand, partly because I’ve been using it as an opportunity to learn Rust). I haven’t got as far as I’d like - I have a Rust version of PseudoDouble and I’ve ported the Galaxy setup over and some of the controls, and am currently porting the rendering pipeline to vulkano. I’ve already found some Pros and Cons.

Pros:

  • Rust is a really nice language to use. My experience is the safer the language and the more fussy the compiler is at compile time, the less time I spend debugging at runtime (that’s why I like things like Haskell and Mercury). Rust does this while also being performant, because the safety features operate at compile time.

    • It is strongly typed - in C++ I’ve been using int32_t, uint8_t, size_t etc. rather than the int that is often used, but Rust forces you to be that specific (i32, u8, usize), and all the libraries use those types too.

    • The aliasing safety is a really big feature. For example, I’ve been coding in C++ for a while, and I’m used to say reading an element of an array and adding it to another element of the same array, knowing that there isn’t an aliasing problem. Rust makes me either specifically set it up so there isn’t an aliasing problem or mark it as unsafe which is good because it draws my attention to making sure that it is actually safe to do.

  • I really like the cargo build system. Just tell the Cargo.toml system what packages I’m using, and it goes and gets them. In particular Android packages can be built on the command line, avoiding the whole bloated mess that is Android Studio (for which I’ll admit that part of the problem is the low end hardware that I’m using).

  • I think Rust is going to be really helpful cross-platform. Although it has multiple compilers available, rustc is the standard one on all the main platforms, so I won’t be facing weird issues like Visual C++ doesn’t support 128 bit integers or _aligned_malloc in Visual C++ having a different argument order than posix_memalign in Linux. I won’t be completely be able to avoid conditional compilation (for instance the vector instrinsics are very different on x86-64 and ARM), but I expect a lot less of it, so less platform specific bugs.

Cons:

  • It’s a slow process doing the port, and redoing what I’ve already written in C++ is way less fun than adding new features would be. I’ve tried ChatGPT 4 to do some of the porting with mixed success, and maybe ChatGPT 5 is worth looking at for that, although even then I want to be careful - I want idiomatic Rust, not a C++ port.

  • Some of the tools are less baked:

    • I’m using vulkano which isn’t as well developed as the C++ Vulkan binding.

    • I’ve read that egui is not as mature as Dear ImGui, particularly on Android.

  • I also had to port some of the libraries that I’m using: Pseudo Double has been ported to Rust, but random-variate-poisson has yet to be.

  • I haven’t got far enough with the port to know if there are any show-stoppers, such as performance or platform issues. There is a risk that I could find some issues that cause me to abandon the whole Rust port and go back to C++.

Unless I run into something that I can’t get around, I expect the Rust version will be more stable and maintainable than the C++ one.

Conclusion

It is occasionally good to re-examine decisions that I’ve made. As this is a solo project in my own timetable, and there hasn’t been any 1.0 release yet that I have to support, I’m free to make different choices as I learn more.

Having looked a very little bit at Unreal Engine, I still think that I made the right decision writing my own, but more knowledge could change my opinion.

I’m hopeful that Rust is going to be a better language for the Galaxy Project than C++ was, the cost of course being that I’m spending time porting instead of adding new features.

Any comments, corrections or observations? Please feel free to contact me