I am currently experimenting with creating a turn based game using Unity and am trying to come up with a sensible architecture for structuring my game systems.
So far I have decided to base everything around Game states. A game state represents a sort of "mode" of the gameplay, for instance "Select unit state" or "Game over state". A global game state manager holds a stack of these game states and can be called with pop (remove top game state), push (push the provided game state onto the stack) and replace all (pop all existing game states and push provided game state). Update is called on the top most element of the stack every logic frame.
I also have Entities which exist on the scene and represent stuff like as game units, terrain, projectiles etc. These are standard gameObjects with behaviors, unity lifecycle etc.
Finally there are a bunch of services which provide methods to handle stuff like menus and camera.
The idea is that the game states handle the "logic" of the game such as player input, transitions between game states, calling the services and controlling/modifying entities.
I also use events to facilitate the communication between state, entities and systems to avoid coupling too much. These events are stuff like "Unit destroyed" and "Objective complete" which are listened to usually by entities, calls to move the camera, create entities and such are still called normally using the relevant service methods.
Whilst this system works in theory I am having some issues with the interactions between events and game state logic. Consider the following:
Update of performActionState:
void update() {
// Do something.
Messanger.fireEvent(EventType.ACTION_COMPLETE);
GameSystemManager.pushState(new NewTurnState());
}
Objectives system listening for action complete:
void handleActionComplete() {
if (checkObjectivesAllComplete()) {
GameSystemManager.pushState(new GameCompleteState());
}
}
What I want to happen is for the objectives system to push a game complete state and finish the game. What actually happens is that the handle action complete pushes the game complete state, the rest of the perform action state update runs and a new turn state is pushed.
The above example might be a bit simplistic but I see this as an underlying issue with my architecture. Most events would be fired due to a change in the state of the scene or completion of something, for instance a unit being destroyed or a unit entering a trigger area. The probability of the game logic needing to change state from the game states is quite high in these cases usually.
I could easily fix the above sample in a number of ways, for instance I could move the checking of objectives into the start of NewGameState and invoke a pushGameState there instead to end the game but all the ways I can think of at the moment seem like hacks which will greatly increase coupling (New game state doesn't need to care about objectives) and code spaghetti.
Any advice for ways I could potentially architecture around issues like this?