Unreal Blueprints to C++
➡️

Unreal Blueprints to C++

Tags
UnrealC++BlueprintsDesign
Owner
Justin Nearing

After wasting too much time Suffering: The First Two Weeks Of Zig, I’ve decided to just start working on LETSGOLETSGO 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”:

image

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:

image

And now that blueprint looks much better:

image

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 LETSGOLETSGO 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

Rename SpawnPoolNoteContainer
Update 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.
Probably want to pull this entire thing into code.

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.

Bootstrap a Music Composer and Conductor

During the runtime of the game, I want to build a musical composition with player input.

I need to think about how I’m actually going to do that.

Roughly, here’s what I’m thinking:

image

So I am imagining a Composer that is responsible for managing the musical composition at runtime.

Then I’m thinking of a separate Conductor that acts as the intermediary between player actions and composer.

A composition is built in Phases.

  1. Set tonic/3rd/modes/etc. - have player define the Music Theory bit
  2. Instrument selection - which synth/lead/percussion does the player select?
  3. Song structure - AABA - ABAB - etc.

The Composer holds state, as well as holds the definition for each Phase:

  • Here’s what the Conductor needs to do during the “Set Tonic” Phase
  • Here’s what the Conductor needs to select between two synths

The Composer takes the output of the Conductor, generates what is needed for the next Phase, then sends the next prepared Phase back to the Conductor .

I envision Phases as pure data.

A list of instructions for the Conductor, and the expected output the Conductor needs to return.

The Conductor feels purely functional- Just a bunch of actions it is capable of doing: Taking a list of arbitrary actions, doing those actions in order, emitting the result.

💡
This use of “actions” might be a good candidate for the Command pattern.

Here’s attempt number 2:

image

So a couple things going on here.

I’ve set that the Composer has a ordered list of Phases it wants to run through, and has an Current Phase.

I then have this object containing the details needed by the Composer to resolve the current Phase. Like a “Phase Initialization” object, but I’m not sure about that name.

Point is, it contains the Action expected to resolve the phase (I’m not going to think about supporting multiple Actions per Phase just yet).

It also has the Inputs required for that Action to be successfully invoked, and the expected output.

Now I’m not exactly sure I need the expected output.

The way I’ve structured the Composer is that it will take that Phase Initialization object, somehow invoke the Actions contained, and marshal the result of the Action into a Phase Resolution Object.

This infers an event system, where the Conductor is listening for anything that triggers an Update event.

The nice thing about this kind of Update event is that the Conductor doesn’t really need to know about what the expected output should be. It just sends the output it gets, and the Composer can handle the result- including handling a bad result.

I’ve used Event Systems in Unity with C#.

Not sure if there’s any out-of-the-box patterns with Unreal C++.

Looks like in Unreal there is “Delegates”. I’ll have to do some reading on this.

Hol’ Up, Am I Getting Too Weird With It?

One question I want to ask myself first though is if I’m over-engineering this?

In theory I could just hard code the phase list, making direct calls per phase, etc.

But I feel the above approach is a good separation of state, execution, and data:

  • The Composer is holding the order of operations and the generated runtime data.
  • The Conductor is executing a versatile set of supported operations.
  • Pure “phase data” objects are flowing through the two systems.

It does seem a bit complex, but really what I’ve designed here is the core gameplay loop for the entire game.

It feels like this structure would also allow for Phases containing multiple Actions. Like a bass drop:

  • Reduce volume on lead 2 bars
  • Add 2 bar percussion riser
  • Percussion switch on drop
  • Lead switch on drop
  • Big ol’ inception chunga bass at max volume

Which means there’s Actions that can contain other Actions.

icon
This kind of vibes with the research I’ve done into Hierarchical Task Networks, where you have a concept of an “primitive task,” which contains no sub-tasks, and compound tasks which do:

It’s been a dream to build an HTN implementation, even though there’s off-the-shelf plugins for Unreal. Expensive though.

You need a relatively versatile gameplay loop system manage that set of actions, and keep it in context of a somewhat legitimate sounding musical composition.

So I think I’ll continue with this design.

Obviously it is still a bit rough, and not quite refined enough to put into code, but this does seem to be a legit path forward.

Phase Lifetime

  1. Composer arranges Phases into an ordered list
    1. Phases have Dependencies on other Phase objects
    2. This is done at Composer Init time
    3. Open question if the Phases are set in stone, or dynamic as the composition progresses
      1. No reason why you couldn’t give the Player a choice to add a new Phase
  2. Composer sets currently active Phase(s)
  3. Phase Name
    Phase State
    Eligible to Activate?
    Repeatable?
    Set Tonic
    Complete
    False
    False
    Set Third
    Currently Active
    True
    False
    Set Mode
    Pending
    False
    False
    Bass Drop
    Pending
    False
    True
    BPM Switch
    Pending
    True
    True

This shows a problem with just having an ordered list of Phases.

Really, there is a “bag” of Pending phases eligible to be activated.

While technically you could change the beats per minute at any time, this would be a relatively rare thing to do.

I think the problem is that I’m confusing Phases with Actions.

Another way to think about Phases is the actual structure of a song:

Intro
Verse
Bridge
Chorus
Verse
Chorus
Outro

In the Intro, there are Actions that make sense- Set which musical scale we’re using, start the various instruments.

In those Phases you have a set of eligible Actions:

Action Name
Action State
Eligible
Repeatable?
Eligible Phase
Set Tonic
Complete
False
False
Intro
Set Third
Currently Active
True
False
Intro
Set Mode
Pending
False
False
Intro
Bass Drop
Pending
False
True
Bridge
BPM Switch
Pending
True
True
Bridge, Chorus, Outro

Let’s pseudo some code:

enum ActionState
	PENDING,
	ACTIVE,
	COMPLETE,
};

struct Action {
	FString Name;      // Name
	ActionState State; // Action State
	bool IsRepeatable; 
	bool IsEligible;
};

struct Phase {
	FString Name
	TArray<Action> Actions; // Eligible Phase 

	
};

// Problem Here
struct PhaseResolution {
	Phase Phase;
	
}

struct Composer {
	// Current Composer State
	Note Tonic; 
	Scale Scale; 
	
	// Managing Phases
	TArray<Phases> Phases;
	void InitializePhases();
	void UpdatePhases();
	
	// Interacting with Conductor
	void InvokeEvent_ComposerCommand(Conductor.RecieveCommand, Phase);
	void RecieveEvent_ComposerUpdate(PhaseResolution);
};

struct Conductor {
	
	//event handler
	void RecieveCommand();
}

Ok so above there’s that rough idea.

Here’s the issue I’m running into. To resolve the Phase “SetTonic” you need a Note to be entered into Composer State.

Basically, I don’t want to polymorph a “PhaseResolution object and write one being “SetTonic” and another “SetThird” and another “SetMode”. Yuck.

I guess the actual data for the State should be its own object:

struct Composer {
	// Current Composer State
	ComposerState State;
	
	//...
};

struct ComposerState {
	Note Tonic;
	Scale Scale; 
	
}

And when the Conductor invokes a ComposerUpdate with a State object it thinks it should be.

That doesn’t sound so compelling either.

struct Composer {
	Note Tonic;
	Scale Scale; 
	
	UpdateTonic(Note);
	UpdateScale(Scale);
	
	void RecieveEvent_ComposerUpdate(PhaseResolution);
};

struct PhaseResolution {
	// [  
	//   { SetTonic, Note },
	// ]
	TArray<StateChanges> StateChanges; 
};

Ok the problem I'm having here is I want to send an object that essentially is:

[ “event_name”, “dynamic_object” ]

C++, famous for its casually dynamic everything, makes it unintuitive to implement such a solution.

My original thought was to call it a payload. Some kind of object wrapped with an interface that allows the composer to consume the payload and action accordingly.

I feel what I need here is a Strategy pattern.

class Action {
		virtual void Resolve() = 0;
};

class PhaseCommand {
	// Here's the Phase you need to do
	
};

class Tonic {
	Note Tonic;
	
};

class Composer {
	Tonic Tonic;
};

class Conductor {
	
	// Recieve Composer Commands()
	void RecieveCommand();
};


class SetTonic :