diff --git a/build/BeagleRescue b/build/BeagleRescue index ace83dc..4f34e47 100755 Binary files a/build/BeagleRescue and b/build/BeagleRescue differ diff --git a/src/ecs/KeyboardController.h b/src/ecs/KeyboardController.h index 6bf7f63..0123271 100644 --- a/src/ecs/KeyboardController.h +++ b/src/ecs/KeyboardController.h @@ -87,7 +87,7 @@ public: sprite->Play("Idle"); if (!Game::gravityOnPlayer){ Game::gravityOnPlayer = true; - sprite->Play("Fall"); +// sprite->Play("Fall"); } break; case SDLK_RIGHT: @@ -108,6 +108,13 @@ public: transform->velocity.y = 0; sprite->Play("Fall"); 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; case SDLK_ESCAPE: // exit the game when Escape pressed Game::isRunning = false; diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 9463acc..1d1e8ea 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -28,6 +28,7 @@ UIText* text; UINineSlice* my9Slice; UIText* scoreboardText; UINineSlice* scoreboard9Slice; +GameStateManager* Game::gsm = new GameStateManager(); SDL_Renderer* Game::renderer = nullptr; SDL_Event Game::event; @@ -243,7 +244,10 @@ void Game::update() void Game::render() { SDL_RenderClear(renderer); - +// if (gsm->currentState==GameStateManager::ST_COREGAME) +// { +// printf("Core Game state\n"); +// } for (auto& t : tiles) { t->draw(); diff --git a/src/game/Game.hpp b/src/game/Game.hpp index be6af5b..b463bb4 100644 --- a/src/game/Game.hpp +++ b/src/game/Game.hpp @@ -15,6 +15,7 @@ #include #include "Vector2D.h" #include "../assetmgr/AssetManager.h" +#include "GameStateManager.h" class ColliderComponent; class AssetManager; @@ -41,6 +42,7 @@ public: static bool playerIsGrounded; static SDL_Rect camera; static AssetManager* assets; + static GameStateManager* gsm; enum groupLabels : std::size_t { groupMap, diff --git a/src/game/GameStateManager.cpp b/src/game/GameStateManager.cpp new file mode 100644 index 0000000..ac95ed1 --- /dev/null +++ b/src/game/GameStateManager.cpp @@ -0,0 +1,95 @@ +#include +#include "GameStateManager.h" +#include + +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"); +} + diff --git a/src/game/GameStateManager.h b/src/game/GameStateManager.h new file mode 100644 index 0000000..d2f8168 --- /dev/null +++ b/src/game/GameStateManager.h @@ -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 + }; +}; + diff --git a/src/game/StateMachine.cpp b/src/game/StateMachine.cpp new file mode 100644 index 0000000..49803c6 --- /dev/null +++ b/src/game/StateMachine.cpp @@ -0,0 +1,84 @@ +#include +#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; +// } diff --git a/src/game/StateMachine.h b/src/game/StateMachine.h new file mode 100644 index 0000000..bda2119 --- /dev/null +++ b/src/game/StateMachine.h @@ -0,0 +1,64 @@ +#ifndef _STATE_MACHINE_H +#define _STATE_MACHINE_H +#include +// ============================================================================= +// 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) }, + +#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