There are a lot of tutorials out there about how to make flashy features for a game like a crafting system or a grappling hook, but very few about how to take all these disparate pieces make a whole game work. In this post I wanted to talk about what I affectionately refer to as “Glue Code” these are things that manage your gameplay flow, let systems talk to one another, and just generally allow you to make a real game instead of just a youtube-friendly tech demo.
I want to preface all of this by first saying that the structure of a game that feels right is inextricably tied to your own personal aesthetic. One programmers spaghetti code can be another’s simple and effective depending on their background and what kind of problems they’ve encountered in the past. For this reason I’m not going to be too prescriptive here. I’m just going to lay out various patterns and talk a bit about how and when they’re useful. I’m also going to try to keep this as engine-agnostic as I can, but I will talk about some applications in Unreal, Godot, and Unity.
With all that out of the way, let’s start moving through various patterns from least to most complex!
A Game Manager is simply a top-level object that controls the overall flow of your game. They will almost always be a Singleton meaning that they will have one instance that is globally accessible.
They may spawn initial actors for a level, emit events for other actors, or manage a global state machine if your gameplay is structured into distinct phases.
Game Managers can go by many names like Game Controller, Root State, or even just Game (As is the case in this famously silly example from the source code for VVVVVV). Oftentimes programmers will naturally stumble onto this pattern making their first game, something needs to be in charge of how the game works after all.
Here’s an example of how my Game Manager for Rogue Hike looked:
class GameManager {
public:
int gameProgress = 0;
int biomeProgress = 0;
StateMachine levelStateMachine;
PlayerCharacter* playerCharacter;
static GameManager* Instance;
void OnGameStart() {
Instance = this;
gameProgress = 0;
biomeProgress = 0;
levelStateMachine.ResetStateMachine();
activePlayerCharacter = Instantiate<PlayerCharacter>();
levelStateMachine.ChangeState("NewDay");
}
void OnNewDay() {
biomeProgress = 0;
gameProgress += 1;
if (gameProgress == 1) {
LoadForestBiome()
} else if (gameProgress == 2) {
LoadRockyBiome();
} else if (gameProgress == 3) {
LoadNewBiomeScene(snowyBiomeScene);
}
levelStateMachine.ChangeState("SelectLevel");
}
void OnPlayerEntrance() { ... }
void OnAnimalsTurn() { ... }
void OnTimePasses() { ... }
void OnEndTurn() { ... }
void LoadForestBiome() { ... }
void LoadRockyBiome() { ... }
void LoadSnowBiome() { ... }
};
If your game is small/simple enough, this structure alone can often take from prototyping to ship without much issue. I want to emphasize that last part again, this is a perfectly fine way to make a game and if it’s working for your project, keep on rockin it!
It’s not however without pitfalls, those usually being:
In Unity: Attach a C# script on a game object with references to all the things it needs to touch that is created on load and never destroyed.
In Unreal: You should use the built-in GameMode & GameState classes in-place of you own implementation since it works better with the rest of the Unreal ecosystem. If you really want/need to roll your own thing, you can do it in a custom Subsystem.
In Godot: This would just a script on your top-level Node that is accessed via a Group (of 1) and emits events via Signals.
Event systems are another common tool to reach for as the size of your game grows. Events are pretty simple at their core, an object in your game generates an event of a certain type (optionally with some data), and other objects listen for those those events and respond accordingly.
A good example of where an event might be useful in a game is defeating a boss. After the player fells the boss, you probably want to:
I like to reserve events for cases where I have 2 or more mostly unconnected systems/objects that need to talk to one another for a few specific cases. That said, if you already have a reference to something, method calls are preferable since they are cheaper and easier to trace. It also goes without saying that you should avoid situations where you are generating events every tick, as this can add large overheads to the performance of your game. Events are also tricky to reason about as they (by-design) decouple the parts of your gameplay code, which is another good reason not to over-use them.
Publish & Subscribe systems take the concept of events and make it a bit more granular. Instead of all events going out into the ether, you can make dedicated streams for events to fall into. This can make your event system much easier to reason about, but also less flexible.
In Unity: I actually recommend using something similar to the Scriptable-Objects-Based method Schell Games developed for their games. You have to do more wiring in the editor, but the overall result is very flexible and designer-friendly.
In Unreal: Unreal already has a very solid system for Delegates/Events built-in, though it’s not global by default.
In Godot: Signals provide a natural starting point for building out events. You can get a pretty decent one together by creating signals on a top-level EventManager Node.
The Observer pattern is kind of ironically named because in most implementations an observer is not actively watching what is being observed, but is being pinged when it updates. When your level starts, observers will subscribe to the observable values that are relevant to them, then when the owner of that value changes it, it will go through each active subscriber and notify them that the value has changed. Values can be simple values like ints, bools, or floats or they can be larger objects like the entire game state.
Observers are good to use when you have a value that rarely updates and affects multiple things. UI elements like a health-bar work really well because it only changes when the player is hurt or heals and as a bonus, you can easily wire in visual effects that accentuate the change.
In general, you should try to be as granular as possible in terms of what data is being observed to avoid notifying too many things at once. That being said, you can reach a point where you have so many little bits of data that it becomes hard to reason about. I usually recommend grouping things together that make sense logically, and then breaking them out later as you start to have performance issues.
In Unity: Once again, I recommend the Scriptable-Objects-Based method Schell Games developed for their games. They are easy to use and allow you to re-use and test different components in isolation.
In Unreal: A Delegate with a single argument can act as an observable, you just have to remember to update it when you update the value tied to it.
In Godot: Signals work great for this and you can easily make one for any value, you will just have to remember to invoke it when you update the underlying value (or make your own wrapper)
States are your go-to tool when you need to logically separate different parts of gameplay or behavior. To understand states it’s useful to think about 2 different examples of state at different scales.
Starting with the small scale, imagine you are creating an enemy in a platformer that runs at the player when they are close, but otherwise patrols. You would define 2 states: Patrol
and Chase
. Patrol
would follow a pre-defined path and if the player is in range transition to Chase
. Chase
would move towards the player and if the player moves out of range transition to Patrol
. This is a pretty simple example, but it leads to behavior that is much easier to reason about and extend than a giant if-else statement.
In Unity:
In Unreal:
In Godot:
When you’re working at a small scale, it’s very easy to cleanly encapsulate the behavior in a few scripts, but what if we had not only states for characters, but for our overall gameplay? This is where a state machine comes in to play.
State machines are a pattern where you have an active state, a set of possible states, and transitions between them. They’re very useful for high level gameplay flow where you have a lot of systems that need to be coordinated.
For example, in my game ArrowBall I set up a state machine to control a round of play with the following States:
I don’t opt for strict transitions for the sake of flexibility, but it ends up looking something like this:
The real magic starts when you realize can hook a state machine up to your events system to broadcast events when you enter and exit certain states. Doing this allows you to trigger animations, particle effects, sounds, UI, or whatever else you need in a way that’s flexible and extensible.
For instance in ArrowBall when the Score state is entered it triggers:
Celebration
Lots of different systems, all working in harmony, doesn’t get any better than that!
In Unity:
In Unreal:
In Godot:
Flat State machines and States work really well when states are used in a predictable way, but it becomes difficult to manage when you have a state that you want to re-use in multiple contexts like one that manages a menu or a confirm dialog. State Stacks to the rescue!
A State Stack is simply an organization of different states onto a stack with only the top-most state being active. This means you can pop into a new state and once that state is done, you can just pop it and return to the previous state.
The clearest example of where this is useful is in UI Menus. Games often have dozens if not hundreds of menus and they’re often used in different contexts. For instance you might have an options menu that is accessed from both the title screen, as well as in-game from the pause menu. With a State Stack you just push the Options Menu State onto the stack when a button is pressed, and then when “back” is pressed you pop it off and return to either the title screen or pause menu. If you have controller bindings or a back button like Android this makes your life even easier since you can just map that button to pop the top-most State in the stack without having to explicitly code out the behavior for each State.
In Unity:
In Unreal: The CommonUI plugin maintained by Epic implements Widget Stacks which are functionally State Stacks but for UI.
In Godot:
Services are (usually singleton) classes that are available to any part of the codebase to perform a service of some kind. That may sound like an overly broad definition but it’s because services are meant to cover a wide range of capabilities, doing everything from playing sound effects to coordinating AI behavior.
A lot of mature codebases rely on this pattern because it’s an easy way to segment different systems and allow them to be created, worked on, and tested in isolation from one another when you have a large and specialized team. For instance you can have Audio Engineers owning services that control music and sound effects, Tech Artists owning Services that spawn particle effects, and Gameplay Programmers owning Services relevant to specific features they’re working on.
My rules of thumb for creating services:
Services are a powerful tool and with great power comes great responsibility (to avoid spaghetti-fying yor codebase).
Regardless of your chosen tool, services are usually built with static singletons, dependency injection, or a service locator. This section will assume you are starting with a singleton (we’ll explore the other solutions for wiring in the next section)
In Unity: Services can be a simple static Singleton or they can be set up with more advanced methods like a service locator or dependency injection.
In Unreal: Custom Subsystems are a great fit for singleton services. They are tied to specific lifetimes (World, Level, Game Instance) based on their parent class and Unreal will automatically instantiate and destroy them for you. I would NOT recommend rolling a custom C++ singleton for many reasons.
In Godot: Similar to the game manager, you can make a collection of script on top-level Nodes that are accessed via a Group (of 1).
As your project gets larger and the number of services grows, it can become difficult to connect dependencies to one another in a sane way. There are two common solutions to this problem that I’ll explore here.
Dependency Injection frameworks work by building out a graph of all your dependencies and automatically injecting references to them into each object.
Dependency Injection is very common in the world of web development, but not so much in most game projects, likely because game developers usually prefer control over abstraction and DI is a very heavy abstraction.
It’s important to note that while DI can help with a lot of your dependency wiring,it’s not a silver bullet, and you can still end up in situations where you have to spend a lot of time untangling dependencies that have circular references.
DI also can’t really solve a mess of dependencies all it really does is hide the mess so you don’t have to see it as often. The rabbit hole can go very deep with DI frameworks and their is a risk that engineers can turn them into code-golf challenges that make things even harder to understand. Which is why I recommend trying to use them sparingly.
I would not recommend rolling your own solution for Dependency Injection if possible as they can be a huge time-sink for engineers, so instead I will link to projects that already exist that you can utilize.
In Unity: Zenject is the solution I’ve worked with the most as it’s the foundation of Pokemon Go’s codebase, though it’s not as well-supported as it used to be and is a bit on the heavy-side in terms of complexity. VContainer is another option that seems to be popular.
In Unreal: Unreal has Type Containers which provide a simple DI solution. it’s important to note that they are C++ only so your designers wont easily be able to access them in blueprint land. They also aren’t used by a lot of games and the only place Epic uses them is in the Unreal Launcher so YMMV.
In Godot: For dependency injection in gdscript the godot-di project is the best option I’ve found, though doesn’t see much use currently. If you’re using C# you can leverage any available DI framework or use the one built into dotnet.
Dependency Injection is a little heavy for most games, but there is a middle ground that exists between static singletons and a big DI framework and that is the trusty Service Locator. A Service Locator is a global singleton that wires up services and then allows other parts of your codebase to access those services via their interface. This is often what people want when they reach for dependency injection and it’s what I recommend instead.
Unlike Dependency Injection, a basic service locator is usually dead-simple to implement and then expand as your needs change.
In Unity: You can leverage a static singleton that wires up concrete service implementations with interfaces accessed by other parts of your code-base. If you want this to happen at runtime you’ll need to make a game object for it to live in
In Unreal: Type Containers also work here, but for an even simpler (and more commonly seen) solution, you can create a Service Locator Subsystem that wires everything up when your game starts and as a bonus you can make different locators for different scopes (World, Level, Etc.) and they can potentially tick.
In Godot: