I realize there are already many Q&As on this site about GameState/GameScreen management, state machines, state stacks, etc. This question is meant as a follow-up:
Suppose hypothetically I decide to go with a simple state machine for the fundamental transitions in the program:
[intro loop] <---> [main menu] <---> [gameplay] <---> [pause menu] \ / \ `-<---[game-over] `---> [ending] of course that is not meant to be a comprehensive diagram by any means. (And I'm aware of the suggestion to not use a game state machine at all, but let's say I would like to go ahead and make one).
I happen to be using the SFML framework in C++, so in my code my main loop happens to look like this:
MainLoop.cpp:
while(app->isRunning()) { while(app->win->pollEvent(event)) { switch(event.type) { case sf::Event::Closed: app->exit(); break; case sf::Event::Resized: app->resize(event.w, event.h); break; case sf::Event::KeyPressed: app->handleKeyPress(event.key)); break; case sf::Event::KeyReleased: app->handleKeyRelease(event.key)); break; // ... etc. This means I have an 'app' object implementing a certain interface:
Application.h
public: virtual bool isRunning() const = 0; virtual bool isPaused() const = 0; virtual void pause() = 0; virtual void resume() = 0; virtual void exit() = 0; virtual void resize(int w, int h) = 0; virtual void render() = 0; virtual void handleKeyPress(int key) = 0; virtual void handleKeyRelease(int key) = 0; virtual void handleMouseClick(int btn) = 0; // ... etc. but following the principle of favouring composition over inheritance, my 'app' object (say MyGame) which implements this interface is not a state machine, but contains a StateMachine as a member, like so:
MyGame.h
class MyGame : public Application { private: StateMachine * myStateMachine; // ... etc. So for things like handleKeyPress() and handleMouseButton() it simply delegates these calls to its member state machine like so:
MyGame.h
public: void handleKeyPress(int k) { myStateMachine->handleKeyPress(k); } void handleMouseClick(int b) { myStateMachine->handleMouseClick(b); } Meanwhile, the StateMachine object DOES THE SAME EXACT THING, i.e. it forwards these same calls to the "current" State:
StateMachine.h
class StateMachine : public Application { private: State * currentState; public: void handleKeyPress(int k) { currentState->handleKeyPress(k); } void handleMouseClick(int b) { currentState->handleMouseClick(b); } which -- to me, looking at the big picture now -- seems a bit crazy because now each time the user moves the mouse, presses a key, or breathes the wrong way I'm going through a chain of THREE virtual method calls?
Every class in the chain seems to implement this same Application interface of handleThis() and handleThat() and enter() and exit() and so forth. StateMachine might as well inherit from Application, and while we're at it, each State could also! But each of them ends up passing the buck down to the next lower layer:
[MainLoop] keyPressed: `-> [MyGame] handleKeyPress() `-> [StateMachine] handleKeyPress() `-> [State] handleKeyPress() `-> actual code that handles keyboard input Should I be trying to "flatten" this architecture, so to speak, to avoid all this virtual method chaining & delegation? If so, how do I do that while still maintaining a reasonable separation of concerns, where Application, MyGame, StateMachine, and each individual State are not all stuffed into the same class? (Or maybe they should be.)
I may be worrying about optimization prematurely, since some of these virtual method calls (esp. from StateMachine --> State) can be inlined. So who knows, maybe this design is not all that unreasonable.
Obviously: first-time game architecture ever, guys... this exercise has been quite humbling.