Back in June, another GDR 4x4x4 challenge started up for the month. I decided to participate and made this silly little game over the course of that month.
And here's a video of the finished game.
The artwork is not by me, instead I used this fantastic asset pack by 0xDB a.k.a. Dennis Busch.
The Game Idea
As with the last 4x4x4 challenge that I participated in, it starts with rolling a random set of 4 different emojis. This time Sirocco from GDR has put together a fancy and mostly complete website for doing this, as well as supporting emoji rolls within some different themes.
On my second roll, I got 🍀 🌘 🌉 🔵.
For some reason when I saw these I kept associating the blue ball with some sort of projectile. And then the two other emojis both relating to the moon made me think of some sort of game where you are shooting at the moon. Possibly for some reason relating to the four leaf clover, which may just be symbolizing luck in some way.
After a few hours of mulling it over I came up with the following:
You're a wizard who has a recipe for crafting "good luck" potions from four-leaf clovers. But where to get four-leaf clovers? You've discovered that moon rocks contain minerals that when struck with living tissue can be used to grow four-leaf clovers in abundance and very quickly through a certain magical process. But how to get moon rocks to strike living tissue in sufficient quantity? You come up with a plan where during the local Lunar Festival (that, coincidentally, is happening very soon) you will anger the Lunar Gods who will surely start hurling moon rocks at you in retaliation. And if you can get people in between you and those moon rocks ... well, that is a recipe for success! A bridge on the way from your local village and the location where the Lunar Festival is celebrated each year would seem to be the perfect place to set up a trap to harvest that valuable moon energy!
One thing that I kept thinking at first was that the player would be shooting at the moon. However, I decided that I didn't like that idea so much. I couldn't think of interesting mechanics to go along with that idea for whatever reason. And then I thought "well, what if instead of you shooting at the moon, the moon shoots at you?" And then instead of the game just being target practice of sorts for the player, you're instead needing to dodge whatever the moon is shooting at you while you are trying to get something else accomplished.
This is what morphed into the idea from above where your goal as the player is to get the poor villagers in the line of fire between the moon and you so that you can essentially turn them into four-leaf clovers.
But for me the silliest part of this which I couldn't stop laughing to myself about was the whole "get the moon angry at you so that it starts shooting you" aspect of the game. For some reason the thought occurred to me that "well, maybe the moon gets angry when you throw animals at it?" Why animals of all things? Why not? I mean it's pretty ridiculous, but for some reason it just made me start laughing. Even now as I'm writing about this over a month later, I am laughing at the idea, heh. Perhaps it's just my under-developed sense of humour.
All of this was facilitated greatly through the use of the art asset pack I was using. I was dabbling with the idea of doing all my own artwork again, but decided against it because one major reason for my participating in this perfectly-timed challenge was to test-drive some code I'd been working on the prior months. I wanted my focus to be on that instead of worrying about art. As a bonus, it meant that I had a lot of existing art to draw on, including a wide variety of people and animal sprites.
The art asset pack I used is all "lo-fi" in nature, being a large set of 1bpp 8x8 pixel tiles. This wasn't specifically the look I was setting out for, but I think it worked out well. The main reasons I choose to use this art asset pack was that it contained a lot of varied artwork and that it was made by another member of the GDR community just in the prior months, so it was very fresh in my mind at the time.
The Technical Bits
As I mentioned briefly above, one major reason for my participation in this particular challenge was that it was mostly timed well coinciding with a bunch of work I was doing on a pet project of mine.
Early 2021, I had decided I wanted to learn the Rust programming language. I really liked the idea of some kind of more modern C/C++ alternative. Several years ago, I wondered if Go was that alternative, but it (in my opinion) isn't. After a short time of using Go at work in in 2017/2018, in my personal opinion, Go is more of a middle-ground or intersection between Java, C/C++ and perhaps Python. Whereas Rust is absolutely low-level exactly like C/C++ can be.
I knew that Rust also had a reputation for being difficult to learn. Specifically the borrow-checker is known to very strongly influence the way in which you architect your Rust code. I am no stranger to static typing, so that part I was fine with, but the idea of a borrow-checker was of course brand new to me and definitely intriguing.
I'm someone who learns new programming languages best by using them to work on personal projects. To scratch my own itches and finding solutions to "real world" problems with the new language along the way. So, after reading through the Rust book and coming away from that a little bit overwhelmed, but feeling that I could definitely do stuff, I started work on my first Rust project which was not this game at all. Which was totally unintentionally probably a good thing, as a game presents a number of different ownership challenges with the borrow-checker that I don't think I would've been quite ready for at that time.
Early this year I started working on libretrogd which is my terrible name (because I hate naming things) for a library that is aimed at doing "retro-like" game development with Rust. But it's very personally focused to scratch some "fun" itches I had, growing out of the retro-computing stuff I've been into the past several years. Specifically, this library takes my usual NIH-syndrome-approach to doing everything, where instead of using something like OpenGL or Vulkan, or something else higher level like SDL or Raylib, I instead do all the graphics via software rendering myself. As well, to continue along with an MS-DOS-like theme, the graphics are all 8bpp (256 colours) and render into a 320x240 framebuffer (that is then up-scaled when shown on the screen of course).
This library wraps SDL via rust-sdl2 providing a set of functionality including:
- Low-level pixel graphics functionality, including higher-level support for drawing primitives like lines, boxes, circles.
- Numerous graphics/sprite blit variants (solid, transparent, flipped, rotated, scaled, palette-offset, etc).
- Palette manipulation (including rotation, interpolation).
- Blend/color map support for highly customized color re-mapping per-draw-call.
- Keyboard and mouse input support.
- Audio playback support (currently limited to 8-bit samples played at 11 khz for completely arbitrary reasons).
- 2D math library, including vector, matrix, rect, and other useful utilities.
- Game state management (stack) abstraction/tools.
- Game event system.
- Entity Component System (ECS).
Of particular note is that this is the first game in quite a long time that I've done which has included any audio support whatsoever! I am using SDL under the hood so this is easier, but even still, I implemented my own audio mixing, supporting 8 concurrent channels of playback. I'd never tried to tackle audio code like this before, so I am quite happy with my (simple) results. It ended up being a fair bit easier than I thought it would be.
The state management, event system, and entity component system stuff is all largely ported over from previous work I'd done in my old "Monster Defense" project as well as some subsequent projects that came out of that (which I previously wrote about here). I found that those older projects had ended up with a very "spaghetti code" feeling at the end, and don't think I've solved that problem yet here in this game's code, but I feel that is largely because I was rushing through it having only a month to get it done for the challenge.
One big improvement with using these same systems but now in Rust, was that I was able to use plain ol' functions for a lot of stuff instead of being forced down the OOP route (sort of) like I was before in Java, so this ended up making things simpler I thought. I always felt like shoving a lot of functionality into objects as I did previously just didn't "feel right" and contributed to extra boilerplate which was tougher to wade through. Though, to be fair, I am now also making that observation after having now also spent many years writing Clojure code and thus, thinking about programming in a much more functional programming mindset, even when I am not using a functional programming language.
Anyway, I suspect I'll do a subsequent post soon about libretrogd and discuss some of the things that I like and don't like about it as I have quite a bit I could write on each of those topics. But for this game project, I think overall I found the library useful and mostly effective for writing small games like this!
I'm also especially happy that it gave me a relatively easy way to produce native statically-linked executables for each of the main desktop operating systems. Well, almost ... there is a gotcha with cross-compiling rust-sdl2 projects for Windows from Linux and I had to leave the Windows build with dynamically linked SDL2 in the end.
I have some other simple ideas for mini games to put together in the near future and am looking forward to working on them.