It documents the process of me smashing my head against the keyboard to build a game called LETSGO
It’s gotten long enough to break into several sections:
After wasting too much time Suffering: The First Two Weeks Of Zig, I’ve decided to just start working on LETSGO again.
My initial goal has been to do some light refactor work, moving some of the more painful-to-look-at blueprint spaghetti into C++ spaghetti.
Here’s an instance of “blueprint spaghetti”:
The details don’t really matter, but it basically boils down to adding a bunch of stuff into some arrays before using those arrays.
While I was making this, I felt bad.
It looks bad.
It is bad.
But, it had the lowest mental overhead to get to the goal.
The goal was to make thing work, not make thing beautiful.
But now it is working, and now I’m willing to commit it to code:
And now that blueprint looks much better:
Least Path of Resistance
Optimizing for the least mental overhead is incredibly effective.
Like most humans, I am easily confused, distracted, and overwhelmed.
Doing something you know is stupid, but will work, is the optimal path during the prototype stage of a project.
Game jams, first time using a set of tools, not having a totally clear idea of how to accomplish a goal- a “stupid but works” approach can give you much more real progress than waiting for an optimal solution.
Of course, the result is garbage. Garbage that works, but garbage.
Which is why moving into a second phase of refactor/cleanup/etc. is necessary as well.
But even in the case of refactoring and clean-up, having a clear goal is important.
I’ve gotten myself into trouble doing some massive refactor that resulted in different, but equally complicated code, that ended up just having me solve the exact same problem over again in a different way.
The goal here is pretty simple though:
- Get back into the codebase with some light refactor work.
- Strengthen my Unreal C++ muscle by moving logic from blueprints to code.
- Lay the groundwork for the next feature.
Specifically the task is to implement Multiple Spawned Music Platforms
Currently in LETSGO a AudioPlatform
is triggered every X seconds in front of the player. You step on it, it plays the associated sound.
This work was captured in Music Theory Engine
What I want now is to present 3 platforms in a line in front of the player, each with a different note, and wait for the player to step on one of them.
I want to store that note, then build musical scales based off that initial choice.
That requires a bunch of refactor work, starting with the blueprint refactor shown above.
A Thing Called SpawnPool
The AudioPlatform
spawner contains a component called SpawnPool
SpawnPool manages which musical Note
the AudioPlatform will use.
The initial implementation had this SpawnPool thing generating all the notes of all the scales, then add it to a Notes array- hence the “Generate All Scales” blueprint / code.
The platform spawner would call a Pop Next Note
function that will remove/return the first index of the Notes array.
That pop function is used when a new AudioPlatform is spawned. (Hence the name SpawnPool - a collection of notes to use when spawning an AudioPlatform.)
Required Changes
- Modify “Generate All Notes” functionality just be one way to initialize a SpawnPool.
- Add a “Select n random notes” functionality
- The
AudioPlatformSpawner
needs functionality to spawn multiple platforms as a logical set.
Additionally, I need something to manage the state holding the order of events:
- Create a set of platforms to set the Tonic (what musical key is this song going to be in?)
- Create a set of platforms to set the nature of the 3rd ( major/minor - dependent on a Tonic)
- Create a set of platforms to set the mode of the scale (suspended 2nd/4th/6th/etc - dependent on Tonic/3rd)
This makes me think that SpawnPool
has overloaded functionality and is poorly named.
The idea I had was it to be a NoteContainer
, and at the time it made sense to put the construction of the Notes in that NoteContainer
.
I think what I want is to pull note construction out of the NoteContainer
altogether
Tasks
SpawnPool
→ NoteContainer
NoteContainer
- Should be purely functional- It is set with an array of Notes at init time, it does not set anything itself.
- It should be only concerned with the functionality of setting and getting Notes at runtime.
This is all complete as of:
That only took a few hours, which was a nice win from the hours of fruitless Zig development I smashed my head against.
Now I just have to draw the rest of the owl.