Added FSM and Game State Manager

This commit is contained in:
Alan Youngblood 2021-02-15 21:10:18 -05:00
parent 78629be4d8
commit e4de32f9f7
8 changed files with 315 additions and 2 deletions

Binary file not shown.

View File

@ -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;

View File

@ -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();

View File

@ -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,

View 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");
}

View 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
View 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
View 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