Here’s a weird crossover that should have probably happened far sooner. Despite years dabbling in game development as a hobby, and over a decade of building UI’s professionally, you might be surprised to learn that those two passions have rarely crossed. Sure, I may have had a “Score Indicator” here, or a button there, but I’ve never actually spent much time building UI’s for my game projects.
Queue the recent Holiday break where I really wanted to spend some time learning more of Godot Engine. I’ve mainly been a Unity boy up until this point, and my last foray looking at this engine was a rather sad attempt at trying to convert an existing project over that I had already been working on for a while. Despite seeming like a good idea at the time, that was definitely the wrong approach to learning something new. My brain was too stuck in the Unity mindset and whenever things didn’t translate over perfectly, I got frustrated and discouraged.
This time around I wanted to approach it differently. I wanted to start with something I didn’t already know how to do easily in Unity, and use that as the launching off point for learning how Godot does things. At the same time, I didn’t want it to be something so unfamiliar to me that it would take weeks to feel like I was actually making any progress. Enter the crossover of my professional and personal life.
The goal: Make a simple menu system that could navigate the player around, collect and pass around various inputs, and try to do it in a way that is modular, extensible, and follows similar design patterns that I would use in Production quality UI code.
Godot’s UI Nodes
I don’t want to spend too much time on this section, as there are better people than me who can explain it better and I would rather focus on what I know best, which is the code. Suffice to say though, Godot has a rather robust suite of built-in Nodes to handle UI functionality. To put it in web development terms, you can think of them as base components that combine with a theming system that’s somewhat akin CSS to create the look and feel of your UI. It’s HTML (also just a tree of element nodes), if instead of having a text document that defines your structure, you instead do it through an interface like this:

One thing I quickly found out though, this gets really annoying to manage once you start trying to create even a slightly complex UI. In HTML, you have the glorious, all-purpose, <div>
that handles any box you want to make. Here we have a PanelContainer
, MarginContainer
, HBoxContainer
, and VBoxContainer
, all different Node types in Godot that do different box-type things before I can finally get my game title text in the position I need it to be.
I still wanted another series of menus from here to navigate through as well. The proverbial VBoxContainerRight
we call it. This is where

Container/Presenter Pattern
There is a design pattern in UI development that I swear by that I know as the Container/Presenter pattern, sometimes also known as the Smart/Dumb Component pattern or the Stateful/Stateless Component pattern, and probably a few other names that I haven’t heard about yet. The idea is pretty simple: you have a high-level “Container” component that handles all the actual logic and state changes of your application, while lower in the tree you have many “Presenter” components (or, importantly, other Container components) who’s job is to look pretty and output events that allow the Container components to respond.
The advantages of this pattern is that it allows you to keep all of your “hard” logic grouped together, the stuff that’s actually driving your application, while the interface to interact with that logic can change or be swapped out easily. It can also just be a nice way to break down something more complex into simpler components that are significantly more easy to reason about. The disadvantages are that you have to actually design generic-enough APIs for the “Presenter” components to be easily swapped out, and that it can often feel like more boilerplate code to set up properly.
So, here I have a main UI setup. I have several sub-views I want to make for learning purposes, I don’t know exactly what they are yet or what I want to do with them exactly, I just know where I want them to render and that I want them affect the overall game state in some way. This seems like a great opportunity to implement the Container/Presenter pattern.
UI State Machine
OK, let’s get into some actual code. If you pull back the curtain, UI’s are just state machines under the hood. You have the “Presenter” components that look pretty and bubble up events to the “Container” components, and then “Container” components respond to the events to change the state, then the new state informs which new “Presenter” components need to be rendered. Rinse and repeat.

Boom. We create a StateMachine
base-class that defines some constants. We extend Node
because everything in Godot is a Node. The constants start to shape how we want this state machine to work. Every state machine starts somewhere, so we define a string value for that of “init”. Every state change comes from and goes to somewhere, we want a catch-all for if we don’t know the exact state value we’re working with, so we define a string value for that of “any”. State changes we wish to define need to call some method on our implementing class, so let’s give that an intuitive format that will look like from_<currentState>_to_<newState>
.

Next, we initialize the class members vars. We’re keeping track of states here, so of course we want to know what the possible states
are so we don’t find ourselves in a place we aren’t expecting. These will be further defined on the implementing class, but we know for sure we will always have an INIT_STATE
. Other things we want to keep track of is our previous state, any arbitrary data associated with the previous state, and same of each for the current state.

So here we have our actual “initialization” function for the State Machine. The implementing class will call this to set the possible state values, give its own initial state value, and pass any initial state data it needs. So what’s going on here?
The first thing I needed to do here is get a combination of all possible states. So, you’ll recall from back on line 7 of this file that we are defining some default states. And by some, I mean just the one, currently, of INIT_STATE
. Suppose I wanted to add more though? It’s very feasible within a UI context that we might also want an END_STATE
or a MIDDLE_STATE
or a WHATEVER_THE_FUCK_I_WANT_STATE
. The important aspect is that I can define those as I desire on this base class, then within the registerStates
method, combine those with the implementing class’s state values. Something like this is most certainly not required for such a simple project, but since my goals in this are to attempt to make things modular and extensible, it was important to me.
HelperFuncs.concat_arays_immutable()
is a helper function I wrote myself. I primarily work in JavaScript… mutable arrays have burned me more times than I care to recall. I wanted to be able to combine arrays immutably without having to worry about that non-sense. It just does what it says.

The rest of this method is mostly validation. We check if all the combined states are unique, and if not, we throw a warning. This is just a sanity check for the developer who may be trying to implement this class. It shouldn’t cause bugs, but it’s also not ideal. Each defined state should be unique.
Finally, we write the full states list to states
member and, if an initial state was provided, we call the change_state()
method to transition from the global INIT_STATE
to the implementing classes initial state. call_deferred()
should ensure that the rest of the scene’s initialization and code execution happens before the initial state change. It’s yet to be seen from me whether this is the right or wrong way to call this, but it felt right in the moment.

So, we’re all initialized. How do we actually change states? Not without some input validation, that’s for sure. Remember, this is base class and I’m approaching this as though I, for sure, or somebody else will be implementing this and make a mistake. I certainly won’t write any documentation around it, so it’s best to be careful. We throw an error if the state they are trying to change to has not been registered.
The rest here is pretty straightforward. We copy the current state to the previous state, then set the new state.

Now things get a little interesting. We have all these possible states. We don’t know how the implementing class will want to transition between them. Still, we need to define a hierarchy for how these state change methods will be called. I figured it would be best to go from most specific to least specific. It could be more advantageous to go the other way around though, or even switch about some of the interchangeable middle ones. This feels like a very problem-centric decision.
A potential improvement I think I could make here to better fit my extensibility goals, is to let the implementing class define this order. But, I didn’t do that here. It’s hardcoded.

All that’s left to do, is check if the state change method exists on the implementing class, and call if it does. These are intentionally only if
statements, rather than if else
statements, to leave room for the possibility of combined methods. Consider you have something you want to always happen upon any state change to StateA
, but you also have something specific you want to happen from a change from StateB
to StateA
. Under this design, both will be executed, with the more specific StateB => StateA
being executed first, and the more generic any => StateA
being executed second. Both will execute under this design.
State Machine Implementation
Great, we have State Machine now. How does it actually work in practice?

Let’s cut this a little short. We extend the StateMachine
class we just built with our implementing class. The exported target_container
Node lets us set in Godot the container we want our sub-views to load in. We preload all our sub-menu “Presenter” views. We create some variables to track the instances thereof.
We define the Godot _ready()
lifecycle method that happens when our Container view loads into the scene. This is where we set our possible states and our initial state, then we call the registerStates()
method from our parent class.

My implementing class can now define the from_any_to_startButtons()
method. This will be called when we transition from ANY state to the startButtons
state. True to our Container/Presenter pattern, this state change method is listening for button press events from a child instance of start_button_instance
rather than letting that component Node decide how to handle it’s own button presses.

Here’s another example of a concrete state change to another concrete state change, also hooking into the Presenter components to register input and coordinate further State changes.
So What Did You Build With It?
So What?
Is this the best way to approach UI development in Godot? I have no idea. I’m still just a beginner. The point of this was for me to learn something and I think I accomplished it. Let’s revisit my intentional goal here.
The goal: Make a simple menu system that could navigate the player around, collect and pass around various inputs, and try to do it in a way that is modular, extensible, and follows similar design patterns that I would use in Production quality UI code.
Do I have a simple menu system that can navigate the player around? Check.
Does it collect and pass around various input data? I glossed a bit over this part in the post, but it’s just a Godot Singleton that holds some data. Check.
Is it modular and extensible? I think I could build on this and be happy with it. Even without the consideration of UI, a generic state machine is something that I know is pretty useful in game development and I think I could do some cool things with it.
Does it follow similar design patterns that I would use in my professional Production UI code? I use, love, and swear by the Container/Presenter pattern and continuously seek to encourage my team to use this in our every day development. I’d not be disappointed shipping this code to Prod. So check here.
Did I learn a lot along the way? So much more than I could express in a single blog post. For everything I’ve been able to enunciate here, there was about 3 million other things that I couldn’t even begin to describe. While all the code I presented here was mine and mine alone, I watched and read a lot human-made tutorials along the way that helped me get here.
The final conclusion is: Keep on learning, keep on following your passions, keep on writing code (and not letting the AI do it for you. =P)
Soucie AI License
Use of this work for generative AI research or models comes at cost of $1,000,000,000, paid in full to the author Brandon Soucie upon use or upon discovery of use. For all other human uses, this work is licensed as follows:
Learning Godot: State Driven UI © 20204 by Brandon Soucie is licensed under CC BY-NC-ND 4.0