Sign in to follow this  
stitchs_login

Managing and transitioning states in my Game.

Recommended Posts

Hello,

[i]Language: C++, Library: Allegro 4[/i]

So, in continuation of my Tic-Tac-Toe project, I'm starting to refine the states by which my game is managed. I've come up with 4 states so far with about 6 transitions. States include: Menu, In-Play, Paused, End-Game. Transitions include; Start game, Pause, Player wins, both players draw, Reset the Grid, Quit to menu. I want to make it so my states are managed by one method as currently I have a number of methods in my game class and some of these methods check the GameState against an Integer value:
[source lang="cpp"]void GameEngine::f_PauseGame()
{
// ensures that pause game can only occur whilst not in menu's
if(a_GameState == 0)
{
if(/*f_PauseKeyDown() == true && */!a_IsPaused)
{
a_IsPaused = true;
}
else if(/*f_PauseKeyDown() == false && */a_IsPaused)
{
a_IsPaused = false;
}
}
}[/source]
I'm not a big fan of this idea as I don't want random checks dotted all over my code as it might become difficult to manage. However, regarding the above example, Pause can only occur during the In-Play state. I was thinking of giving every method an additional Integer parameter, and then in each method performing an if/switch check on a number. This number will be fed when the method is called in an overall method that manages game states, I feel this makes it a bit neater, but still requires checks all over the place. My CheckGameState method looks something like this (a snippet):
[source lang="cpp"]if(a_GameState == 0){
if(!a_IsPaused)
//

if(/*ROWS*/(a_Grid->f_GetGridSquare(0,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(2,0) == a_LastSymbolPlaced)
||
(a_Grid->f_GetGridSquare(0,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(2,1) == a_LastSymbolPlaced)
||
(a_Grid->f_GetGridSquare(0,2) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,2) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(2,2) == a_LastSymbolPlaced)
||
/*COLS*/(a_Grid->f_GetGridSquare(0,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(0,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(0,2) == a_LastSymbolPlaced)
||
(a_Grid->f_GetGridSquare(1,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,2) == a_LastSymbolPlaced)
||
(a_Grid->f_GetGridSquare(2,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(2,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(2,2) == a_LastSymbolPlaced)
||
/*DIAG*/(a_Grid->f_GetGridSquare(0,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(2,2) == a_LastSymbolPlaced)
||
(a_Grid->f_GetGridSquare(2,0) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(1,1) == a_LastSymbolPlaced) && (a_Grid->f_GetGridSquare(0,2) == a_LastSymbolPlaced))
{
// WE HAVE A WINNER ARTIFACT AT THIS LINE.
if(a_LastSymbolPlaced == a_PlayerOne->f_GetSymbol()){
a_PlayerOneWins = true;
// Game is set to End-Game, allowing for end of game functions to initiate
a_PlayerOne->f_IncreaseScore(1);
//f_SetGameState(1);
}
else if(a_LastSymbolPlaced == a_PlayerTwo->f_GetSymbol()){
a_PlayerTwoWins = true;
a_PlayerTwo->f_IncreaseScore(1);
//f_SetGameState(1);
}
END_GAME_STATUS_STRING = "WINNER!";// + a_LastSymbolPlaced;
f_SetGameState(1);
} else if(a_GridSquareCount == 9 && a_Grid->f_IsBoardFull()){ // We use the GridSquare count here to ensure that IsBoard is not checked on every possible go of the Game Loop.
// Checking for a draw.
textprintf_centre_ex(screen, font, SCREEN_W/2, 256/2, makecol(255,0,255), makecol(0,0,0),"DRAW GAME!");
//a_GameState = (alert("DRAW", NULL, "Would you like to play again..?", "Reset.", "Quit.", 'r', NULL) + 1);
END_GAME_STATUS_STRING = "DRAW!";
f_SetGameState(1);
}
}[/source]
Ideally I want to have something along the lines of, and this is just a basic arbitrary form: if(gameState == 0) { class->method1(), class->method2(); }. I feel that if I am to simplify my methods to not check the state of the game, that my GameState method should be simplified to only call methods, and not have this bloat.

So, from my own writings, I have decided that I will separate non-method code away from the CheckState method into their own functions, where appropriate. My actual question is; how do I simplify the numerous checks to the GameState dotted all over my code, especially where some functions have different responses based on game state. I.e.: I don't want to call Method1() in GameState 0 and GameState 1 as it will be checked twice in either instance because it responds in both cases.

Apologies if my point seems a bit waffle in places.

Regards,

Stitchs.

Share this post


Link to post
Share on other sites
You could make use of virtual functions/polymorphism here, and create your states as objects. Each state should be responsible for itself and can request to change states when appropriate. The GameEngine can be the keeper of the states.

Here is some very rough code (not compiled), but you can get the idea:
[CODE]
class GameEngine
{
public:
void SetState(State* state)
{
m_CurrentState = state;
}
void Update()
{
m_CurrentState->Update();
}
MenuState* GetMenuState()
{
return &m_MenuState;
}
PlayState* GetPlayState()
{
return &m_PlayState;
}
void TogglePause()
{
m_IsPaused = !m_IsPaused;
}
protected:
MenuState m_MenuState;
PlayState m_PlayState;
bool m_IsPaused;
};
class State
{
public:
State(GameEngine* g)
{
m_pGameEngine = g;
}
virtual void Update();
virtual void OnPause();
};
class MenuState : public State
{
public:
virtual void Update()
{
//! update the menu
//! pseudo code
if(ready_to_play){
g->SetState(g->PlayState);
}
}
virtual void OnPause()
{
//! do nothing - pause only works in play state
}
};
class PlayState : public State
{
public:
PlayState(GameEngine* g) : State(g)
{
}
virtual void Update()
{
//! update PlayState
//! pseudo code
if(goto_menu) {
g->SetState(g->MenuState);
}
}
virtual void OnPause()
{
g->TogglePause();
}
};
void main()
{
GameEngine game;
game.SetState(game.GetMenuState());
while(1)
{
game.Update();
}
}
[/CODE]

Share this post


Link to post
Share on other sites
I thought about this, and I do like the idea of encapsulating states, in their own classes/functions. With your suggestion, does it not create an issue of high coupling between the classes whereby the Engine requires the State and vice-versa. Would it not be better to let the Engine have knowledge of the States but not the other way around, so that the states could be re-used elsewhere, potentially?

Regards,

Stitchs.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this