user2990037
user2990037

Reputation: 397

Communication between state machine states

I'm stumbling abit on the implementation of a state machine in my application.

So I have my state machine:

class StateMachine
{
public:
enum State { MENU, GAME };
    StateMachine();
    void Update();
    void Render();

private:
    Menu* mMenu;
    Game* mGame;
    State mState;
};

StateMachine::StateMachine()
{
    mState = MENU;
}

void StateMachine::Update()
{
    switch(mState)
    {
        case MENU:
        mMenu -> Update(); break;
        case GAME:
        mGame -> Update(); break;
    }
}
void StateMachine::Render()
{
    switch(mState)
    {
        case MENU:
        mMenu -> Render(); break;
        case GAME:
        mGame -> Render(); break;
    }
}

My menu and game classes encapsulate updating, rendering, handling of keyboard inputs etc.

So my question is, if the state is in MENU, how do I communicate between the object mMenu with the state machine to tell it to go to the game state? Do I need a variable in menu and game which the state machine picks up on to then switch states?

Upvotes: 2

Views: 975

Answers (2)

Homero C. de Almeida
Homero C. de Almeida

Reputation: 199

Both Menu and Game classes need to be aware of the StateMachine if they are suposed to update it.

The best way to do that is to keep a reference to the state machine in Game and Menu classes which updates the current state. You can further encapsulate this in an interface that both implement to make things further clearer.

// State.hpp
class State {
    public:
        virtual Handle() = 0;
};

// Cotext.hpp
#include "State.hpp"

class Context {
private:
    State* currentState;

public:
    void Update() {
        currentState->handle():
    }

    void setState(State* state) {
        currentState = state;
    }
};

class ContextSwitcher {
    virtual void SetContext(Context* context) = 0;
};

// Game.hpp
#include "Context.hpp"

class Game : public State, ContextSwitcher {
    Context* context_;
    State* menu_;

public:
    virtual void SetContext(Context* context) {
        context_ = context;
    }

    virtual void SetMenu(State* menu) {
        menu_ = menu;
    }

    virtual void Handle() {
        // Do update stuff
        if (shouldGoToMenu) {
            context_->setState(menu_);
        }
    }
}

// Menu.hpp
class Menu : public State, ContextSwitcher {
    Context* context_;
    State* game_;

public:
    virtual void SetContext(Context* context) {
        context_ = context;
    }

    void SetGame(State* game) {
        game_ = game;
    }

    virtual void Handle() {
        // Do update stuff
        if (shouldGoToGame) {
            context_->setState(game_);
        }
    }
}

// GameContext.hpp
#include "Context.hpp"
#include "Menu.hpp"
#include "Game.hpp"

class GameContext : public Context {
private:
    Menu menu;
    Game game;

public:
    void Init() {
        menu->SetContext(this);
        menu->SetGame(&game);
        game->SetContext(this);
        game->SetMenu(&menu);

    }
    // ...
};

Upvotes: 1

Eric Fortin
Eric Fortin

Reputation: 7603

Normally you try to avoid having coupling both ways. That is having a reference of StateMachine inside Menu class.

There is multiple ways to go around this however.

A C++ idiom would be an interface with virtual functions that your StateMachine implements and then passes this in Menu and Game constructors. This reduces coupling but has other drawbacks like it limits handling to a single object.

But you could also use simple function pointers(callbacks), events, signals, etc.

Upvotes: 1

Related Questions