Advertisement
Spirit77

Application state management system

Jul 13th, 2014
349
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.45 KB | None | 0 0
  1. ///-------------------------------------------------------------------------------------------------
  2. // AppState.h
  3.  
  4. #ifndef _APPSTATE_H_
  5. #define _APPSTATE_H_
  6. ///-------------------------------------------------------------------------------------------------
  7. #include <deque>
  8. ///-------------------------------------------------------------------------------------------------
  9. class AppState
  10. {
  11. public:
  12.  
  13.     // Possible return states of the program at the completion of an update call
  14.     enum EApplicationState
  15.     {
  16.         eState_Running,
  17.         eState_End,
  18.         eState_ExitProgram,
  19.     };
  20.  
  21.     // Pushes a new state onto the active state stack
  22.     static void PushState(AppState * a_poState);
  23.  
  24.     // Generic interface for updating the active state
  25.     static EApplicationState Update(float a_fDeltaTime);
  26.  
  27.     // Generic interface for rendering states on the active stack
  28.     static bool Draw();
  29.  
  30.     // Initialises required static resources
  31.     static bool Init();
  32.  
  33.     // Cleans up any remaining static resources
  34.     static void Destruct();
  35.  
  36.     // Allows external classes to get the update timestep value
  37.     static float GetUpdateTick() { return m_fUpdateTick; }
  38.  
  39. protected: 
  40.    
  41.     // Prevent external, non-child class objects from trying to create an instance of this class
  42.     AppState();
  43.     // Prevent external, non-child class objects from trying to destroy an instance of this class family
  44.     virtual ~AppState();
  45.  
  46.     // Abstract function called when a new state is pushed onto the stack. Must be defined in child class
  47.     virtual void OnInsert() {}
  48.  
  49.     // Abstract update function. Must be defined in child class
  50.     virtual EApplicationState OnUpdate(float a_fUpdateTick) { return eState_ExitProgram; }
  51.  
  52.     // Abstract draw function. Must be defined in child class
  53.     virtual void OnDraw() {}
  54.  
  55.     // Abstract function called when a state is popped from the stack. Must be defined in child class
  56.     virtual void OnRemove() {}
  57.  
  58.     // Allows AppState to suspend/resume states if superceded by a new state
  59.     virtual void OnSuspend() { m_bSuspended = true; };
  60.     virtual void OnResume() { m_bSuspended = false; };
  61.  
  62.     // Suspend state of this object
  63.     bool m_bSuspended;
  64.  
  65. private:
  66.  
  67.     // Application active state stack
  68.     static std::deque<AppState *> m_dequeGameStates;
  69.  
  70.     // Stack of application states to push onto the active state stack
  71.     static std::deque<AppState *> m_dequeNewGameStates;
  72.  
  73.     // Update & draw timesteps
  74.     static float m_fUpdateTick;
  75.     static float m_fDrawTick;
  76.  
  77.     // Used to track milliseconds elapsed since the most recent processing of update/draw
  78.     static float m_fTimeSinceLastUpdate;
  79.     static float m_fTimeSinceLastDraw;
  80.  
  81. };  // class AppState
  82. ///-------------------------------------------------------------------------------------------------
  83. #endif // #ifndef _APPSTATE_H_
  84.  
  85.  
  86. ///-------------------------------------------------------------------------------------------------
  87. // AppState.cpp
  88.  
  89. #include "AppState.h"
  90. #include <assert.h>
  91. ///-------------------------------------------------------------------------------------------------
  92. // Init static member variables
  93. std::deque<AppState *> AppState::m_dequeGameStates;
  94. std::deque<AppState *> AppState::m_dequeNewGameStates;
  95. float AppState::m_fUpdateTick = 1.f / 60.f;         // Fixed 60 updates per second
  96. float AppState::m_fDrawTick = 1.f / 60.f;           // Max 60 draws per second
  97. float AppState::m_fTimeSinceLastUpdate = 0.f;
  98. float AppState::m_fTimeSinceLastDraw = 0.f;
  99. ///-------------------------------------------------------------------------------------------------
  100. void AppState::PushState(AppState * a_poState)
  101. {
  102.     if (a_poState != nullptr) m_dequeNewGameStates.push_back(a_poState);
  103. }
  104. ///-------------------------------------------------------------------------------------------------
  105. AppState::EApplicationState AppState::Update(float a_fDeltaTime)
  106. {
  107.     // Advance update & draw timers
  108.     m_fTimeSinceLastUpdate += a_fDeltaTime;
  109.     m_fTimeSinceLastDraw += a_fDeltaTime;
  110.  
  111.     // If enough time has accumulated to perform an update,
  112.     while (m_fTimeSinceLastUpdate > m_fUpdateTick)
  113.     {
  114.         // Attempt to update the active state
  115.         if (!m_dequeGameStates.empty())
  116.         {
  117.             AppState * poCurrentState = m_dequeGameStates.back();
  118.             EApplicationState eStateResult = poCurrentState->OnUpdate(m_fUpdateTick);
  119.  
  120.             // On exit result, return exit
  121.             if (eStateResult == eState_ExitProgram) return eStateResult;
  122.  
  123.             // If the current state has ended,
  124.             if (eStateResult == AppState::eState_End)
  125.             {
  126.                 // Remove this state from the stack
  127.                 m_dequeGameStates.pop_back();
  128.                 poCurrentState->OnRemove();
  129.  
  130.                 // Delete the removed state
  131.                 delete poCurrentState;
  132.  
  133.                 // If a buried state exits and there are no new states,
  134.                 if (m_dequeNewGameStates.empty() && !m_dequeGameStates.empty())
  135.                 {
  136.                     // Activate the buried state
  137.                     m_dequeGameStates.back()->m_bSuspended = false;
  138.                     m_dequeGameStates.back()->OnResume();
  139.                 }
  140.             }
  141.         }
  142.  
  143.         // While there are newly pushed states,
  144.         while (!m_dequeNewGameStates.empty())
  145.         {
  146.             // Suspend the topmost state
  147.             if (!m_dequeGameStates.empty()) m_dequeGameStates.back()->OnSuspend();
  148.  
  149.             // Pop the newly pushed state from the back of the new states deque and push it onto the stack
  150.             m_dequeGameStates.push_back(m_dequeNewGameStates.front());
  151.             m_dequeNewGameStates.front()->OnInsert();
  152.             m_dequeNewGameStates.pop_front();
  153.  
  154.             // If there are no more new states to push, activate the topmost state on the stack
  155.             if (m_dequeNewGameStates.empty()) m_dequeGameStates.back()->m_bSuspended = false;
  156.         }
  157.  
  158.         // Decrease the elapsed time counter by the update timestep
  159.         m_fTimeSinceLastUpdate -= m_fUpdateTick;
  160.     }
  161.  
  162.     // If no states exist to process, exit program
  163.     if (m_dequeGameStates.empty() && m_dequeNewGameStates.empty()) return eState_ExitProgram;
  164.  
  165.     // If the topmost state is suspended, something went wrong. Report error
  166.     if (!m_dequeGameStates.empty() && m_dequeGameStates.back()->m_bSuspended) assert("GameState::Execute() active state currently in suspended state" == nullptr);
  167.  
  168.     return eState_Running;
  169. }
  170. ///-------------------------------------------------------------------------------------------------
  171. bool AppState::Draw()
  172. {
  173.     // If too little time has elapsed since the previous draw, return
  174.     if (m_fTimeSinceLastDraw < m_fDrawTick) return false;
  175.  
  176.     // Render all states on the stack, from bottom to top
  177.     for (auto oIter : m_dequeGameStates) oIter->OnDraw();
  178.  
  179.     // Reset the time elapsed since draw variable
  180.     m_fTimeSinceLastDraw = 0.f;
  181.  
  182.     return true;
  183. }
  184. ///-------------------------------------------------------------------------------------------------
  185. bool AppState::Init()
  186. {
  187.     // Init any resources
  188.  
  189.     return true;
  190. }
  191. ///-------------------------------------------------------------------------------------------------
  192. void AppState::Destruct()
  193. {
  194.     // Destroy any uninserted game states
  195.     while (!m_dequeNewGameStates.empty())
  196.     {
  197.         auto oIter = m_dequeNewGameStates.front();
  198.         delete oIter;
  199.         m_dequeNewGameStates.pop_front();
  200.     }
  201.    
  202.     // Destroy any active/inactive game states
  203.     while (!m_dequeGameStates.empty())
  204.     {
  205.         auto oIter = m_dequeGameStates.front();
  206.         delete oIter;
  207.         m_dequeGameStates.pop_front();
  208.     }
  209. }
  210. ///-------------------------------------------------------------------------------------------------
  211. AppState::AppState()
  212.     :   m_bSuspended        (false)
  213. {
  214.  
  215. }
  216. ///-------------------------------------------------------------------------------------------------
  217. AppState::~AppState()
  218. {
  219.  
  220. }
  221. ///-------------------------------------------------------------------------------------------------
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement