来自:http://www.codeproject.com/Articles/30775/TetroGL-An-OpenGL-Game-Tutorial-in-C-for-Win32-pla
目录
1、Handling the States of your Game
2、补充 简单使用
1、Handling the States of your Game
In almost every game, you will encounter different 'states': Usually you have a main menu (which allows the user to start a new game, set some options, view the highscores), the main play state, the highscore state, etc. It quickly becomes a mess in your code if you have to manage everything in the same class: The update and draw functions becomes a gigantic switch in which you have to take care of all the possible states, all the variables are mixed together which makes the code hard to maintain, and so on. Luckily, there's an easy design pattern that elegantly solves the problem: The state pattern. The principle is quite simple: Each state of your game has its own separate class which inherits from a common 'state' class. So, in our example we have a class for the menu, a class for the play state, a class for the highscores, and so on. A state manager class keeps track of the current state of the game and redirect every call to this active state (draw, update, key down, ...). When you have to switch to another state, you simply inform the state manager of the new state. You can find quite a lot of nice articles about this pattern on the net, so I won't enter into much details here. Take a look at the first link in the references if you want to go more in details about this design pattern.
In the sources, you will find a CStateManager
class which looks like:
// Manages the different states of the game.
class CStateManager
{
public:
// Default constructor
CStateManager();
// Default destructor
~CStateManager();
// Switches to another active state.
void ChangeState(CGameState* pNewState)
{
if (m_pActiveState)
m_pActiveState->LeaveState();
m_pActiveState = pNewState;
m_pActiveState->EnterState();
}
// Returns the current active state.
CGameState* GetActiveState() { return m_pActiveState; }
// 'Events' function, they are simply redirected to
// the active state.
void OnKeyDown(WPARAM wKey);
void OnKeyUp(WPARAM wKey);
void OnChar(WPARAM wChar);
void Update(DWORD dwCurrentTime);
void Draw();
private:
// Active State of the game (intro, play, ...)
CGameState* m_pActiveState;
};
This class manages the current state of the game and redirects all 'events' call to it: If you look at the implementation ofOnKeyDown
,OnKeyUp
,Update
andDraw
, you will see that they simply call the same function on them_pActiveState
instance. When switching to another state, the state manager calls theLeaveState
of the current state and theEnterState
of the new state. States can implement those functions to do special initialization or clean up when the state becomes active or inactive.
The CGameState
is very easy too:
// Base class for the different states
// of the game.
class CGameState
{
public:
// Constructor
CGameState(CStateManager* pManager);
// Destructor
virtual ~CGameState();
// The different 'events' functions. Child classes can
// implement the ones in which they are interested in.
virtual void OnKeyDown(WPARAM ) { }
virtual void OnKeyUp(WPARAM ) { }
virtual void OnChar(WPARAM ) { }
virtual void Update(DWORD ) { }
virtual void Draw() { }
// Functions called when the state is entered or left
// (transition from/to another state).
virtual void EnterState() { }
virtual void LeaveState() { }
protected:
// Helper function to switch to a new active state.
void ChangeState(CGameState* pNewState);
// The state manager.
CStateManager* m_pStateManager;
};
The different classes that will manage the states of the game inherit from this class. These child classes can then implement the 'event' functions in which they are interested. The
ChangeState
function is there only as a helper function: It simply call
ChangeState
of the
CStateManager
.
//原文end........
2、补充 简单使用
CStateManager类的其它部分:
CStateManager::CStateManager() : m_pActiveState(NULL)
{
}
CStateManager::~CStateManager()
{
}
void CStateManager::OnKeyDown(WPARAM wKey)
{
if (m_pActiveState)
m_pActiveState->OnKeyDown(wKey);
}
void CStateManager::OnKeyUp(WPARAM wKey)
{
if (m_pActiveState)
m_pActiveState->OnKeyUp(wKey);
}
void CStateManager::OnChar(WPARAM wChar)
{
if (m_pActiveState)
m_pActiveState->OnChar(wChar);
}
void CStateManager::Update(DWORD dwCurrentTime)
{
if (m_pActiveState)
m_pActiveState->Update(dwCurrentTime);
}
void CStateManager::Draw()
{
if (m_pActiveState)
m_pActiveState->Draw();
}
CGameState类的其它部分:
CGameState::CGameState(CStateManager* pManager)
: m_pStateManager(pManager)
{
}
CGameState::~CGameState()
{
}
void CGameState::ChangeState(CGameState* pNewState)
{
m_pStateManager->ChangeState(pNewState);
}
简单使用:
其中,CMenuState类、CPlayState类、CHighScoreState类均继承自CGameState类。
CStateManager *m_pStateManager;
CMainWindow::CMainWindow(..)
{
//...
m_pStateManager = new CStateManager;
m_pStateManager->ChangeState(CMenuState::GetInstance(m_pStateManager));
}
void CMainWindow::ProcessEvent(UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
// ...
// Keydown event
case WM_KEYDOWN :
m_pStateManager->OnKeyDown(wParam);
break;
// Keyup event
case WM_KEYUP :
m_pStateManager->OnKeyUp(wParam);
break;
// Character event
case WM_CHAR:
m_pStateManager->OnChar(wParam);
break;
}
}
void CMainWindow::Update(DWORD dwCurrentTime)
{
m_pStateManager->Update(dwCurrentTime);
}
void CMainWindow::Draw()
{
// ...
m_pStateManager->Draw();
// ...
}
//...
//...
void CMenuState::SelectionChosen()
{
switch (m_iCurrentSelection)
{
case 0:
if (!m_pCurrentGame)
m_pCurrentGame = CPlayState::GetInstance(m_pStateManager);
m_pCurrentGame->Reset();
ChangeState(m_pCurrentGame);
break;
case 1:
if (m_pCurrentGame && !m_pCurrentGame->IsGameOver())
ChangeState(m_pCurrentGame);
break;
case 2:
ChangeState(CHighScoreState::GetInstance(m_pStateManager));
break;
case 3:
PostQuitMessage(0);
break;
}
}
//...
//...
void CPlayState::OnKeyDown(WPARAM wKey)
{
switch (wKey)
{
//case...
case VK_ESCAPE:
ChangeState(CMenuState::GetInstance(m_pStateManager));
break;
case VK_RETURN:
if (m_bGameOver)
{
CHighScoreState* pHighScores =
CHighScoreState::GetInstance(m_pStateManager);
pHighScores->SetNewHighScore(m_ulCurrentScore);
ChangeState(pHighScores);
}
}
}
//...
//...
void CHighScoreState::OnKeyDown(WPARAM wKey)
{
if (m_bEnterName)
{
//...
}
else
{ switch(wKey)
{
case VK_ESCAPE:
case VK_RETURN:
ChangeState(CMenuState::GetInstance(m_pStateManager));
break;
}
}
}