Game States
Most game code is implemented by writing custom components. However, components always work in the context of an object, be it a single game object or an entire prefab. The most that a single component can be responsible for, is to do high level logic for a level, by acting as a global message handler.
However, for a full game you need a layer of control that is outside the world, where you can do logic like what level to load, what to do when the player dies or reaches their goal, how to display a main menu for the game settings and level selection, and so on. Most of these things would be possible with world components alone, but it would be cumbersome. Especially switching from one level to another is difficult, if some of your overall game logic has to be transitioned as well.
Game States are this extra layer. A game state sits between the application (TODO) layer and the world.
A game state is in so far optional, in that the engine will create an instance of plFallbackGameState
, if no custom game state is available. The diagram above shows, that the editor skips the game state in simulate mode. In practice that means that the editor will not allocate any game state when the scene is being edited or only simulated. Only when you enter play-the-game mode, will it create a game state, which can then take over full control for the windows, input and the main camera.
Game State Responsibilities
The typical things that a game state controls are:
Spawning one or multiple windows
Setting up the main render pipeline
Creating a world and loading a scene into it
Unless it's run in the editor, where it is handed an existing world
Setting up input devices and bindings
Processing main input (not component specific input)
Setting up and controlling the main camera
Spawning the player prefab
Displaying game UI
Providing a main menu
Saving and restoring global state (progression, high-scores, etc)
For example when you have a player start point component in a scene, the component itself doesn't do anything, it just holds some data. Instead, when you enter play-the-game mode, the active game state can (but is not required to) use the information from these components to spawn a player prefab.
Similarly, most scenes have a camera component whose usage hint is set to 'Main Camera' (this may be part of the player prefab). This camera defines what part of the scene will be shown on screen. At least that's how it appears. In reality it is the game state that controls the camera for the main render target. It's simply a feature of the plFallbackGameState
, that it searches the world for an appropriate camera component and applies that to the main camera. If it doesn't find any such camera component, it provides simple WASD fly-camera controls. You can even cycle through the different camera components in a scene using Page Up
and Page Down
.
As you can see, by implementing a custom game state, you can gain control over many things that otherwise appear to be built-in.
Game State Instantiation
It is the responsibility of the plGameApplication
to instantiate a game state. By default this is done right at application startup, but if you write your own application (TODO) you could handle this differently. For example the editor only instantiates the game state for play-the-game mode.
The application knows what game states are available through the reflection information. When a game state is needed, all available ones are instantiated and 'asked' (via plGameStateBase::DeterminePriority()
) whether it is the right one to handle the situation. The game state that is the best fit will be kept and gets control.
The idea is, that there are typically only few game states available anyway. Usually you have the built-in plFallbackGameState
and then you have one other game state implementation from your custom game plugin. The latter one will take precedence. You could have multiple game states, for example when you have multiple game plugins loaded simultaneously, but then they would need to somehow figure out which one should get activated (e.g. through command line arguments).
See Also
Custom Code