Added FSM and Game State Manager
This commit is contained in:
parent
78629be4d8
commit
e4de32f9f7
Binary file not shown.
@ -87,7 +87,7 @@ public:
|
|||||||
sprite->Play("Idle");
|
sprite->Play("Idle");
|
||||||
if (!Game::gravityOnPlayer){
|
if (!Game::gravityOnPlayer){
|
||||||
Game::gravityOnPlayer = true;
|
Game::gravityOnPlayer = true;
|
||||||
sprite->Play("Fall");
|
// sprite->Play("Fall");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
@ -108,6 +108,13 @@ public:
|
|||||||
transform->velocity.y = 0;
|
transform->velocity.y = 0;
|
||||||
sprite->Play("Fall");
|
sprite->Play("Fall");
|
||||||
Game::gravityOnPlayer = true;
|
Game::gravityOnPlayer = true;
|
||||||
|
break;
|
||||||
|
case SDLK_u:
|
||||||
|
// Advance the GSM state via the u key
|
||||||
|
Game::gsm->AdvanceState();
|
||||||
|
break;
|
||||||
|
case SDLK_i:
|
||||||
|
Game::gsm->GameOver();
|
||||||
break;
|
break;
|
||||||
case SDLK_ESCAPE: // exit the game when Escape pressed
|
case SDLK_ESCAPE: // exit the game when Escape pressed
|
||||||
Game::isRunning = false;
|
Game::isRunning = false;
|
||||||
|
@ -28,6 +28,7 @@ UIText* text;
|
|||||||
UINineSlice* my9Slice;
|
UINineSlice* my9Slice;
|
||||||
UIText* scoreboardText;
|
UIText* scoreboardText;
|
||||||
UINineSlice* scoreboard9Slice;
|
UINineSlice* scoreboard9Slice;
|
||||||
|
GameStateManager* Game::gsm = new GameStateManager();
|
||||||
|
|
||||||
SDL_Renderer* Game::renderer = nullptr;
|
SDL_Renderer* Game::renderer = nullptr;
|
||||||
SDL_Event Game::event;
|
SDL_Event Game::event;
|
||||||
@ -243,7 +244,10 @@ void Game::update()
|
|||||||
void Game::render()
|
void Game::render()
|
||||||
{
|
{
|
||||||
SDL_RenderClear(renderer);
|
SDL_RenderClear(renderer);
|
||||||
|
// if (gsm->currentState==GameStateManager::ST_COREGAME)
|
||||||
|
// {
|
||||||
|
// printf("Core Game state\n");
|
||||||
|
// }
|
||||||
for (auto& t : tiles)
|
for (auto& t : tiles)
|
||||||
{
|
{
|
||||||
t->draw();
|
t->draw();
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Vector2D.h"
|
#include "Vector2D.h"
|
||||||
#include "../assetmgr/AssetManager.h"
|
#include "../assetmgr/AssetManager.h"
|
||||||
|
#include "GameStateManager.h"
|
||||||
|
|
||||||
class ColliderComponent;
|
class ColliderComponent;
|
||||||
class AssetManager;
|
class AssetManager;
|
||||||
@ -41,6 +42,7 @@ public:
|
|||||||
static bool playerIsGrounded;
|
static bool playerIsGrounded;
|
||||||
static SDL_Rect camera;
|
static SDL_Rect camera;
|
||||||
static AssetManager* assets;
|
static AssetManager* assets;
|
||||||
|
static GameStateManager* gsm;
|
||||||
enum groupLabels : std::size_t
|
enum groupLabels : std::size_t
|
||||||
{
|
{
|
||||||
groupMap,
|
groupMap,
|
||||||
|
95
src/game/GameStateManager.cpp
Normal file
95
src/game/GameStateManager.cpp
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include "GameStateManager.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void GameStateManager::AdvanceState()
|
||||||
|
{
|
||||||
|
//given the AdvanceState event transition to a new state based on current state and gameData
|
||||||
|
BEGIN_TRANSITION_MAP // (Next State) // - Current State -
|
||||||
|
TRANSITION_MAP_ENTRY (ST_TITLESCREEN) // ST_Init
|
||||||
|
TRANSITION_MAP_ENTRY (ST_COREGAME) // ST_TitleScreen
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Instructions
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_HiScoreList
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Credits
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_CoreGame
|
||||||
|
TRANSITION_MAP_ENTRY (ST_COREGAME) // ST_PauseScreen
|
||||||
|
TRANSITION_MAP_ENTRY (ST_INIT) // ST_GameOver
|
||||||
|
END_TRANSITION_MAP(NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::PauseGame(GameData* gameData)
|
||||||
|
{
|
||||||
|
// given the PauseGame event transition to a new state based on current state and gameData
|
||||||
|
// printf("gameData->isPaused: %d\n",gameData->isPaused);
|
||||||
|
BEGIN_TRANSITION_MAP // - Current State -
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Init
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_TitleScreen
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Instructions
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_HiScoreList
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Credits
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_CoreGame
|
||||||
|
TRANSITION_MAP_ENTRY (ST_INSTRUCTIONS) // ST_PauseScreen
|
||||||
|
TRANSITION_MAP_ENTRY (CANNOT_HAPPEN) // ST_GameOver
|
||||||
|
END_TRANSITION_MAP(gameData)
|
||||||
|
|
||||||
|
}
|
||||||
|
void GameStateManager::GameOver()
|
||||||
|
{
|
||||||
|
//given the PauseGame event transition to a new state based on current state and gameData
|
||||||
|
// printf("GameOver triggered\n");
|
||||||
|
BEGIN_TRANSITION_MAP // - Current State -
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Init
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_TitleScreen
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Instructions
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_HiScoreList
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_Credits
|
||||||
|
TRANSITION_MAP_ENTRY (ST_GAMEOVER) // ST_CoreGame
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_PauseScreen
|
||||||
|
TRANSITION_MAP_ENTRY (EVENT_IGNORED) // ST_GameOver
|
||||||
|
END_TRANSITION_MAP(NULL)
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_Init(EventData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_Init\n");
|
||||||
|
// InternalEvent(ST_TITLESCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_TitleScreen(EventData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_TitleScreen\n");
|
||||||
|
// InternalEvent(ST_TITLESCREEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_Instructions(GameData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_Instructions\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_HiScoreList(GameData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_HiScoreList\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_Credits(GameData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_Credits\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_CoreGame(EventData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_CoreGame\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_PauseScreen(EventData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_PauseScreen\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameStateManager::ST_GameOver(GameData*)
|
||||||
|
{
|
||||||
|
printf("GameStateManager::ST_GameOver\n");
|
||||||
|
}
|
||||||
|
|
57
src/game/GameStateManager.h
Normal file
57
src/game/GameStateManager.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "StateMachine.h"
|
||||||
|
|
||||||
|
// Structure to hold event data passed into state StateMachine
|
||||||
|
struct GameData : public EventData
|
||||||
|
{
|
||||||
|
char* menuId;
|
||||||
|
bool win;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The Game State Machine Class
|
||||||
|
class GameStateManager : public StateMachine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GameStateManager() : StateMachine(ST_MAX_STATES) {}
|
||||||
|
|
||||||
|
// external events taken by this state machine
|
||||||
|
void AdvanceState();
|
||||||
|
void GameOver();
|
||||||
|
void PauseGame(GameData* gameData);
|
||||||
|
private:
|
||||||
|
//state machine state functions
|
||||||
|
void ST_Init(EventData*);
|
||||||
|
void ST_TitleScreen(EventData*);
|
||||||
|
void ST_Instructions(GameData*);
|
||||||
|
void ST_HiScoreList(GameData*);
|
||||||
|
void ST_Credits(GameData*);
|
||||||
|
void ST_CoreGame(EventData*);
|
||||||
|
void ST_PauseScreen(EventData*);
|
||||||
|
void ST_GameOver(GameData*);
|
||||||
|
|
||||||
|
//state map to define state function order
|
||||||
|
BEGIN_STATE_MAP
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_Init)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_TitleScreen)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_Instructions)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_HiScoreList)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_Credits)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_CoreGame)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_PauseScreen)
|
||||||
|
STATE_MAP_ENTRY(&GameStateManager::ST_GameOver)
|
||||||
|
END_STATE_MAP
|
||||||
|
|
||||||
|
//state enumeration order must match order of state method entries on state map
|
||||||
|
enum E_States {
|
||||||
|
ST_INIT = 0,
|
||||||
|
ST_TITLESCREEN,
|
||||||
|
ST_INSTRUCTIONS,
|
||||||
|
ST_HISCORELIST,
|
||||||
|
ST_CREDITS,
|
||||||
|
ST_COREGAME,
|
||||||
|
ST_PAUSESCREEN,
|
||||||
|
ST_GAMEOVER,
|
||||||
|
ST_MAX_STATES
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
84
src/game/StateMachine.cpp
Normal file
84
src/game/StateMachine.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include "StateMachine.h"
|
||||||
|
// =============================================================================
|
||||||
|
// Do NOT modify this file, instead make custom changes in the GameStateManager
|
||||||
|
// or create your own StateMachine including and implementing this header
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
StateMachine::StateMachine(unsigned char maxStates) :
|
||||||
|
currentState(0),
|
||||||
|
_maxStates(maxStates),
|
||||||
|
_eventGenerated(false),
|
||||||
|
_pEventData(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// generates an external event. called once per external event
|
||||||
|
// to start the state machine executing
|
||||||
|
void StateMachine::ExternalEvent(unsigned char newState,
|
||||||
|
EventData* pData)
|
||||||
|
{
|
||||||
|
// if we are supposed to ignore this event
|
||||||
|
if (newState == EVENT_IGNORED) {
|
||||||
|
// just delete the event data, if any
|
||||||
|
if (pData)
|
||||||
|
delete pData;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO - capture software lock here for thread-safety if necessary
|
||||||
|
|
||||||
|
// generate the event and execute the state engine
|
||||||
|
InternalEvent(newState, pData);
|
||||||
|
StateEngine();
|
||||||
|
|
||||||
|
// TODO - release software lock here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generates an internal event. called from within a state
|
||||||
|
// function to transition to a new state
|
||||||
|
void StateMachine::InternalEvent(unsigned char newState,
|
||||||
|
EventData* pData)
|
||||||
|
{
|
||||||
|
if (pData == NULL)
|
||||||
|
pData = new EventData();
|
||||||
|
|
||||||
|
_pEventData = pData;
|
||||||
|
_eventGenerated = true;
|
||||||
|
currentState = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the state engine executes the state machine states
|
||||||
|
void StateMachine::StateEngine(void)
|
||||||
|
{
|
||||||
|
EventData* pDataTemp = NULL;
|
||||||
|
|
||||||
|
// while events are being generated keep executing states
|
||||||
|
while (_eventGenerated) {
|
||||||
|
pDataTemp = _pEventData; // copy of event data pointer
|
||||||
|
_pEventData = NULL; // event data used up, reset ptr
|
||||||
|
_eventGenerated = false; // event used up, reset flag
|
||||||
|
|
||||||
|
assert(currentState < _maxStates);
|
||||||
|
|
||||||
|
// get state map
|
||||||
|
const StateStruct* pStateMap = GetStateMap();
|
||||||
|
|
||||||
|
// execute the state passing in event data, if any
|
||||||
|
(this->*pStateMap[currentState].pStateFunc)(pDataTemp);
|
||||||
|
|
||||||
|
// if event data was used, then delete it
|
||||||
|
if (pDataTemp) {
|
||||||
|
delete pDataTemp;
|
||||||
|
pDataTemp = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsigned char getCurrentState()
|
||||||
|
// {
|
||||||
|
// const StateStruct* pStateMap = GetStateMap();
|
||||||
|
// this->*pStateMap[currentState];
|
||||||
|
// unsigned char state = StateMachine.currentState;
|
||||||
|
// return state;
|
||||||
|
// }
|
64
src/game/StateMachine.h
Normal file
64
src/game/StateMachine.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef _STATE_MACHINE_H
|
||||||
|
#define _STATE_MACHINE_H
|
||||||
|
#include <stdio.h>
|
||||||
|
// =============================================================================
|
||||||
|
// Do NOT modify this file, instead make custom changes in the GameStateManager
|
||||||
|
// or create your own StateMachine including and implementing this header
|
||||||
|
// =============================================================================
|
||||||
|
class EventData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~EventData() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StateStruct;
|
||||||
|
|
||||||
|
// base class for state machines
|
||||||
|
class StateMachine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StateMachine(unsigned char maxStates);
|
||||||
|
virtual ~StateMachine() {}
|
||||||
|
// unsigned char getCurrentState();
|
||||||
|
unsigned char currentState;
|
||||||
|
protected:
|
||||||
|
enum { EVENT_IGNORED = 0xFE, CANNOT_HAPPEN };
|
||||||
|
void ExternalEvent(unsigned char, EventData* = NULL);
|
||||||
|
void InternalEvent(unsigned char, EventData* = NULL);
|
||||||
|
virtual const StateStruct* GetStateMap() = 0;
|
||||||
|
private:
|
||||||
|
const unsigned char _maxStates;
|
||||||
|
bool _eventGenerated;
|
||||||
|
EventData* _pEventData;
|
||||||
|
void StateEngine(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (StateMachine::*StateFunc)(EventData *);
|
||||||
|
struct StateStruct
|
||||||
|
{
|
||||||
|
StateFunc pStateFunc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BEGIN_STATE_MAP \
|
||||||
|
public:\
|
||||||
|
const StateStruct* GetStateMap() {\
|
||||||
|
static const StateStruct StateMap[] = {
|
||||||
|
|
||||||
|
#define STATE_MAP_ENTRY(stateFunc)\
|
||||||
|
{ reinterpret_cast<StateFunc>(stateFunc) },
|
||||||
|
|
||||||
|
#define END_STATE_MAP \
|
||||||
|
}; \
|
||||||
|
return &StateMap[0]; }
|
||||||
|
|
||||||
|
#define BEGIN_TRANSITION_MAP \
|
||||||
|
static const unsigned char TRANSITIONS[] = {\
|
||||||
|
|
||||||
|
#define TRANSITION_MAP_ENTRY(entry)\
|
||||||
|
entry,
|
||||||
|
|
||||||
|
#define END_TRANSITION_MAP(data) \
|
||||||
|
0 };\
|
||||||
|
ExternalEvent(TRANSITIONS[currentState], data);
|
||||||
|
|
||||||
|
#endif //STATE_MACHINE_H
|
Loading…
Reference in New Issue
Block a user