Gnorp News #10 — Hats And Rust

(the) Gnorp Apologue

(the) Gnorp Apologue is the journey of the gnorps as you guide them towards their goal of delightfully excessive wealth accumulation.

I'm back from my break, and I am currently working on quite a few things. One of those things are hats. I have also written a lengthy piece about my experience with Rust. You can read this post here, or on [url=https://gnorp.dev/news/10-Hats-And-Rust/]gnorp.dev[/url] [h2]Submit a Hat![/h2] With hats being planned to be implemented, I have asked the [url=https://discord.gg/Kf3ZWGVSDe]Gnorp Discord[/url] to come up with some hat ideas. The top voted hat(s) will be implemented into the game, with the creator credited. If you would like to participate in submission or voting, go ahead and join the Discord. You can also post your hat on the [url=https://steamcommunity.com/app/1473350/discussions/]Steam Discussions forum[/url] - just tag it with [Hat]. Here are the rules: — Hats can also be expressions, or both — Hats need to be pixel-perfect, so no sub-pixel shenanigans — Hats need to be reasonably sized — Hats can be general, or intended for specific gnorps — Hats should fit with the game, which is a nebulous requirement, but also an important one — I will compile the gnorps with hats, and you will vote for them (On Discord) — Top voted hat(s) will be implemented with the hat update, and the creator(s) will be credited Below you can find a couple of hats that I made, to give you an idea of what you can do. You can use something like Paint, Aseprite, or an online editor (search "pixel editor") to make your hats. [img]{STEAM_CLAN_IMAGE}/44722791/8a0855a5a6cfa4fc2faf343e139d11582b169ba1.png[/img] [h2]Writing a game in Rust[/h2] For those that don't know, (the) Gnorp Apologue is written in Rust. I have been asked a few times about my experience with Rust, and so I thought I would write a little bit about it. [h3]Inception[/h3] I love games, and I had toyed with the idea of making a proper one for a long time. There's a graveyard in my filesystem of unfinished projects, concept art, ideas, and half-written prototypes. During this time, I tooled around with a lot of different frameworks, engines, and languages. Unity, Godot & Unreal did not appeal to me, due to their editor-centric nature. My preference is to live inside a text-editor, and that preference led me to LÖVE, a 2D game engine. I made a prototype with LÖVE, and LÖVE did not disappoint. My interest in the prototype faded, however, partially due to the game concept, but also because of a voice in my head saying "Lua is not fast enough". Time moved on, but I still wanted to make games. One day I decided to try out Rust for fun, which was a programming language I'd heard about but never looked into. Reading about it, I was drawn to the idea of writing fast code, comfortably. I'd heard about Rust from the yearly Stack Overflow poll, and also through an AMA on reddit about a project written in C++. The question was "What do you think about Rust". In the response, the developer stated that he didn't see the point of using another language if C++ can do the same and more. There was also the following line "[...] and I have very few issues with [C++]". That piqued my interest at the time, as I thought it impossible to have very few issues with C++. It could only mean his opinion was wrong. It turned out, even with my silly, flawed logic, that trying Rust out was a terrific idea. I loved the language, the documentation, and the tooling. I clicked with the language within the first few hours of playing around, and I decided firmly that I wanted to make a game with it. And so I did, by reading the Rust book. After reading the Rust book for 12 minutes, it was time to put all the nothing I knew into practice. I researched frameworks and game engines I could build something with. Now, with Rust being a fairly new programming language the selection is not nearly as wide as more mature languages. But that was okay — I was willing to deal with that in my excitement to work with Rust. I found GGEZ, Coffee, Amethyst, Bevy (which was just published at the time), and many others. In the end I chose Tetra, created by Joe Clay. It is a fantastic, well-documented framework built on top of SDL2. It draws fast, and had support for everything I needed. Now it was time for Test Project 1. It would serve to help me familarise myself with building software in Rust. Test Project 1 was based on a small game I had made for my girlfriend (now wife) in the span of 12 hours. It features creatures dubbed Gnorps, and a rock that output smaller rocks when struck by the gnorps. I finished a prototype and it was surprisingly fun. It was enjoyable to watch the gnorps go about their business, and it was satisfying to see the a huge number of smaller rocks, "shards", on the screen. My girlfriend (now wife), played it and enjoyed it, and so did my friends. That's when I decided to make a real game out of it of Test Project 1, and it turned into (the) Gnorp Apologue. [h3]What did I like about Rust[/h3] My absolute favorite feature of Rust is enumerators, and by extension the pattern matching that comes with it. It's an incredibly powerful system, which has allowed me to write reliable code that can be easily extended when new behavior needs to be introduced. The compiler has also been a fantastic tool. When designing a game, refactoring is a given, and a part of the process. You protype something, you extend, now there are modifiers, now this unit needs to be able to do this, etc. You rip out code, and replace it with something else, introduce new systems, smash modules together, split them apart — it doesn't matter — when the compiler is happy, it. just. works. I have found that being able to focus on *just* writing the game has been tremendously productive. The cost of context switching is high, and there have only been a few occasions where I have been distracted by the language or mystifying compiler errors. [h3]Gameplay behavior[/h3] The game loop is a fixed timestep loop. This means that the game runs at discrete steps, with no considerations of a delta time between updates. I have found this to be the best way to write games, as it allows you to write physics-based behavior with very few bugs. The drawing is interpolated between the steps, so that the game looks smooth regardless of framerate. There are many different units within the game, and they all have different behaviors. Writing this behavior in Rust was a surprisingly smooth ride, partially thanks to the power of enumerators. When creating a new unit, I would generally follow a compositional approach: [img]{STEAM_CLAN_IMAGE}/44722791/e12d7eb3682a69715877ee95e9f02891e0d79f9e.png[/img] I've never been a fan of inheritance, and found composition in Rust to be a much more flexible approach. [h3]User Interface[/h3] The worst experience of writing (the) Gnorp Apologue was no doubt the user interface. I decided against using a UI library, and instead rolled my own to have full control of the behavior and appearance. This might have been a mistake, and perhaps I should have reached for something like Egui or some of the other UI libraries available. Some parts of the UI follow the immediate mode paradigm, and some parts are only redrawn on demand. Drawing text can be expensive, and I have tried to minimize this, but that is an ongoing process as I update and refine the game. In terms of Rust, it's hard to say if the language was a help or a hindrance. Depending on the architecture of the software, the borrow checker can be a bit of a pain when dealing with a user interface, but I managed to navigate the issue after reapproaching a few times. [h3]Performance[/h3] As an avid player of games, I have always been interested in performance. When I was a kid, I didn't have the most powerful computer, so the performance of the games I played was always a concern. It's always impressive when a game will just run on anything, and that was one of the goals I had with (the) Gnorp Apologue. Here are some of the things I did to achieve that goal, some of which may have been overkill: — I tried to write solid code, with few hacks — I did not use dynamic dispatch — I did not use reference counting — I tried to keep units light in terms of memory usage — I tried to keep the number of draw calls when rendering the UI low (it's still too high) The game logic runs in a single thread, and I have no reason to make the game multi-threaded, even when there are thousands of units and objects active. I have also not used any unsafe in my own code, with the exception of one, entirely safe but unnecessary, unchecked unwrap. The game runs well. Not much more to say than that. SDL2 allows the game to draw fast, and Rust makes the logic fast and reliable. [h3]Compilation[/h3] Just a small section here. Rust compile times are relatively slow, but at the same time I find that constantly compiling to be unproductive behavior, so I strive to compile only when testing is required to proceed. [h3]Summary[/h3] My Rust journey has been a good one. I have learned a lot about the language, but there are still plenty of features that I haven't taken full advantage of yet, mostly because I didn't need to. Would I recommend Rust for game development? Yes, absolutely. When it comes to game design, the hardest part is not writing code — it's coming up with the right ideas. Rust allows you to implement those ideas with confidence that things will work as intended, and that the code will be fast. [h3]Aftermath[/h3] On the 14th of December, I released my first ever game, with the release build being finalized 20 minutes before release. The game has to date received over 1500 reviews. It has been a way bigger success than I could ever have expected, with tens of thousands of copies sold (I try not to check). A big part of that success I attribute to Rust, and the community around it, and to Tetra, made by Joe Clay. Thank you for reading!