↖️ Blog Archive

Pinwheel Game, Part Two

Bradley Gannon

2025-10-09

TL;DR: I finished the game I started in part one. You can play it online here or compile and play locally with the code here. I don’t have a lot of time for writing this week, so I’m going to give a brief brain dump instead of my usual level of detail, which I suppose is better than nothing at all.

I changed up the architecture of the code after just kind of taking a guess at what might work during the first week. It was important to me that the rendering and game state logic be separate, and ideally that would mean they’d live in separate files. My naive first approach was to try to put an impl for a struct in a sibling file, but this doesn’t work because of Rust’s item privacy rules. After some reading, it seems this is only possible if an item is defined in a module and the impls are elsewhere in the same module. I guess this makes sense because otherwise it would be possible to add functionality to structs that are otherwise private. I still don’t fully understand this behavior, but I was able to create a game module that defines all of its structures and other items in mod.rs and extends them with more functionality in files within that module.

Sound was a little challenging to get working on NixOS. On Linux generally, enabling the audio feature in macroquad causes it to link against libasound2, and in a typical distribution it would be sufficient to install the development package for that library so the linker can find it. On NixOS, this is normally solvable by adding the package to the development shell and making sure its path is in LD_LIBRARY_PATH. But, as I learned, this is only really relevant for runtime dylib loading, and Rust’s linker needs extra help to find certain libraries at link time. (As above, I don’t understand this fully.) It turns out that you can pass the path in RUSTFLAGS, which is easy enough to do in shell.nix.

I was especially happy to get sound working because I think the sound effects in Pinwheel completely change the character of the game. It’s the first or second thing that people have mentioned when playing the game for the first time, and it adds a silliness that I enjoy. I made all the sounds myself and recorded them into Audacity with my cheap desk microphone. This was mildly embarrassing but also kind of fun. I’ll have to remember the power of sound when I build more games in the future.

macroquad performed beautifully throughout this project, and one example is its effortless WASM deployment. Just installing the appropriate toolchain and running a slightly altered build command is all that’s required. The option to play in a browser makes it really easy to show people the game and let them try it out if they want to. I was able to add some logic to maintain aspect ratio in most situations, which simplified the canvas CSS a bit, so with three static files I can deploy the game online. If you’re on macOS or iOS, you may have noticed that the web version doesn’t fill the viewport or even run at all. This is a disappointing and confusing issue that I haven’t resolved yet, but it’s not that important to me at the moment. Maybe I’ll address it in future game projects. I don’t see any reason why WASM should be a problem on these platforms with the right configuration. I just have to figure out what that configuration is.

I built a few functions of general usefulness during this project, and by chance I watched this video about building a personal library of components for accelerated game development. This has led me to the realization that I should be building a crate that’s essentially an extension of macroquad that I can grow over time. Utility functions can live there, but more than that I can add implementations of various parts that together approximate a true game engine. Collision, asset loading and management, rendering and lighting tools, pathfinding, opponent AI, and so on. As I encounter new problems and solve them, the solutions can get merged “upstream” so I have them next time. Since nobody else will use this crate, I can make decisions based solely on my own whims.

This was a lot of fun, and I’ll definitely try to do some more gamedev over the winter.