Coding, the last thing a software engineer should do
Ok we’ve gone down the esoteric road of what music is, thought about music in terms of data structures, and figured out how to get Unreal to compile.
By doing this we now have something that the majority of programmers out there are sorely lacking: A Clue.
So let’s write some damn code:
Here we have the
ionian scale (A fancy word for the Major scale). You will notice we have established the step pattern for the scale.
The steps is an enum where Whole = 2 and Half = 1.
The other main enum we have here is the list of notes:
I’ve agonized on the naming of the keys, because everything we define here is a fundamental building block for the rest of our music theory engine. A bad name or structure at this point gives you a compounding problem over time.
There does already exist music notes
enum in Unreal in the
AudioMixer module, but it just flattens all the black keys. Ableton does the opposite, sharpening all the black keys.
Given two competing standards, the obvious choice is to create a third standard!
Really it doesn’t matter what the standard is, what matters is the consistency. For instance, making the decision to structure the note names based on the circle of fifths means when I export sound files from Ableton, I should match the name: no A#, name it Bb.
Hell I could represent the notes as do-re-me.
Tonic key from
ELetsGoMusicNotes and our step pattern found in the
Ionian scale, we can generate the correct scale.
- Its a struct.
- This feels like more of a data container than whatever a class should be, so I’m thinking of it more as a struct.
- It’s hard to tell when something should be a
- Structs appear to be reflected in Blueprints easier though?
- Don’t see
- It is Unreals version of Python’s
self(It’s fancy-work Unreal uses to reflect this struct into Blueprints, etc.)
- You can start to grok how Unreal is interacting with C++ here
- Macro the things you need to surface in the game.
We’ll generate scales into the
FLetsGoGeneratedScale struct- a data object that contains the
Notes of that scale.
We need a function that generates the
Notes contained in the generated scale:
- This is similar to the Jippity output I referenced in , but I actually wrote this myself.Programming Music: Constant Data
- Can you spot the bug in Next?
- I had some trouble figuring out where to put this.
- I originally wrote it as the constructor for
FLetsGoGeneratedScale, but there was some unexpected behavior when I brought this into Unreal land.
I ended up defining this as a static function in
UFUNCTION(BlueprintCallable, Category= LetsGoBlueprintCategory)
static FLetsGoGeneratedScale GenerateScale(const FLetsGoMusicScale& Scale, const FLetsGoMusicNotes& Tonic);
ULetsGoMusicEngine. This is probably not great, I should probably have a bit more care being put into what classes are what.
But as an initial starting point, Im absolutely fine with it. What I really really definitely want to avoid is premature optimization by making this code “cleaner”, which is almost guaranteed to make actual extensions to this code more difficult.
When I have a task that makes refactoring this code necessary, then I will refactor it.
Clean code ≠ effective code
In theory, we can now find a list of notes in
FLetsGoGeneratedScale - a thing we should be able to find Blueprints.
Corrupt Ass Blueprints Brought To You By PAIN
Loki, God of tricks and pranks and stuff turned its attention to my project at this stage.
I did… something.
I had Blueprints working one night, now they are acting very unreliably.
When I start my project, it’s not able to find any of the
LetsGoMusicCategory items. If I compile while the editor is running, then my LetsGo stuff appears correctly. The problem is Unreal freaks out every time its missing the LetsGo stuff, and any blueprint Node related to my custom stuff built here goes missing or ERROR or simply ends up in a uncompilable state.
It makes me think there’s different kinds of compiling? There is:
Compiling Game Projects
Compiling individual game projects using Visual Studio on Windows, or Xcode on Mac.
But I still want to build for Editor.
I dunno, the whole thing is finicky in a way that’s hard to describe. Worse still I’m not totally confident that source control is picking up all the changes- super hard to confirm without another computer to build on. That matters because I kind of want to nuke from orbit, reinstall and clone from main.
This is what I’m talking about:
I create a variable using one of my data structures:
Compiles green, I save all, all good.
With the helpful compiler message:
So much for actually writing some damn code.
Clean up Aisle LETSGO
Ok I think if we can do some kind of clean rebuild, we might get this working.
Stumbled on this:
I closed everything, and renamed the derived folders mentioned above to have a
_ in front of it:
Launch Unreal, get the Module rebuild window, pressed rebuild.
It looks like that this worked. The variables I complain about before appear to be working correctly.
So now I can delete those
_ directories and move on with the damn task.
At this point I just kind of got it working
Blueprints were recognizing my code, and I built a
The intent for it is to kind of be like an Object Pool.
Object Pooling is a game dev design pattern useful for things like spawning bullet objects. If you only have 12 bullets in a clip, you create an object pool of 12 bullets one time at the beginning, instead of spending resources on creating a bullet every time to fire the weapon.
I imagined that instead of bullets, we have Notes.
Basically I imagined a fancy array to stick the Notes in, then have the
MusicPlatformSpawner ”pop” the first note from the array:
SpawnPool is a Blueprint Component of the
MusicPlatformSpawner and has the BP function
Pop Next Note
Pop Next Note grabs the first item (the 0th) from the
Note Pool and removes it from the array.
Shout out to the Unreal Devs for dynamic arrays. This relatively simple/common action smells like it would be a #wholething in native C++
NotePool is generated at the
BeginPlay event. It is a
SpawnPool Variable that is an array of
Notes. Here we set a tonic and for each supported scale we defined in
FLetsGoMusicScales, we generate the notes and add them to the
I did have a problem here where every music platform being spawned was choosing the note
C. Turns out I had a bug in my
generateScale function where I was missing some parenthesis.
But once I fixed it things started working.
I immediately blasted social media with how damn smart I am:
Justin Nearing on LinkedIn: #unreal #gamedev #audioproduction #indiedev #indiegame #devupdate
More progress on my #Unreal project LETSGO: Jumped headfirst into C++ to build out a music theory engine for the game! Lot's of shenanigans related setting…
And so that’s it. We did it: A rambling 4 part series on how to make a single function in Unreal.
Gamedev ain’t shit.