We now have most of our state loading code in place and can make use of this in the MenuState
class. First we must do a little legwork and set up a new way of assigning the callbacks to our MenuButton
objects, since this is not something we could pass in from an XML file. The approach we will take is to give any object that wants to make use of a callback an attribute named callbackID
in the XML file. Other objects do not need this value and LoaderParams
will use the default value of 0
. The MenuButton
class will make use of this value and pull it from its LoaderParams
, like so:
void MenuButton::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); m_callbackID = pParams->getCallbackID(); m_currentFrame = MOUSE_OUT; }
The MenuButton
class will also need two other functions, one to set the callback function and another to return its callback ID:
void setCallback(void(*callback)()) { m_callback = callback;} int getCallbackID() { return m_callbackID; }
Next we must create a function to set callbacks. Any state that uses objects with callbacks will need an implementation of this function. The most likely states to have callbacks are menu states, so we will rename our MenuState
class to MainMenuState
and make MenuState
an abstract class that extends from GameState
. The class will declare a function that sets the callbacks for any items that need it and it will also have a vector of the Callback
objects as a member; this will be used within the setCallbacks
function for each state.
class MenuState : public GameState { protected: typedef void(*Callback)(); virtual void setCallbacks(const std::vector<Callback>& callbacks) = 0; std::vector<Callback> m_callbacks; };
The MainMenuState
class (previously MenuState
) will now derive from this MenuState
class.
#include "MenuState.h" #include "GameObject.h" class MainMenuState : public MenuState { public: virtual void update(); virtual void render(); virtual bool onEnter(); virtual bool onExit(); virtual std::string getStateID() const { return s_menuID; } private: virtual void setCallbacks(const std::vector<Callback>& callbacks); // call back functions for menu items static void s_menuToPlay(); static void s_exitFromMenu(); static const std::string s_menuID; std::vector<GameObject*> m_gameObjects; };
Because MainMenuState
now derives from MenuState
, it must of course declare and define the setCallbacks
function. We are now ready to use our state parsing to load the MainMenuState
class. Our onEnter
function will now look like this:
bool MainMenuState::onEnter() { // parse the state StateParser stateParser; stateParser.parseState("test.xml", s_menuID, &m_gameObjects, &m_textureIDList); m_callbacks.push_back(0); //pushback 0 callbackID start from 1 m_callbacks.push_back(s_menuToPlay); m_callbacks.push_back(s_exitFromMenu); // set the callbacks for menu items setCallbacks(m_callbacks); std::cout << "entering MenuState "; return true; }
We create a state parser and then use it to parse the current state. We push any callbacks into the m_callbacks
array inherited from MenuState
. Now we need to define the setCallbacks
function:
void MainMenuState::setCallbacks(const std::vector<Callback>& callbacks) { // go through the game objects for(int i = 0; i < m_gameObjects.size(); i++) { // if they are of type MenuButton then assign a callback based on the id passed in from the file if(dynamic_cast<MenuButton*>(m_gameObjects[i])) { MenuButton* pButton = dynamic_cast<MenuButton*>(m_gameObjects[i]); pButton->setCallback(callbacks[pButton->getCallbackID()]); } } }
We use dynamic_cast
to check whether the object is a MenuButton
type; if it is then we do the actual cast and then use the objects callbackID
as the index into the callbacks
vector and assign the correct function. While this method of assigning callbacks could be seen as not very extendable and could possibly be better implemented, it does have a redeeming feature; it allows us to keep our callbacks inside the state they will need to be called from. This means that we won't need a huge header file with all of the callbacks in.
One last alteration we need is to add a list of texture IDs to each state so that we can clear all of the textures that were loaded for that state. Open up GameState.h
and we will add a protected
variable.
protected: std::vector<std::string> m_textureIDList;
We will pass this into the state parser in onEnter
and then we can clear any used textures in the onExit
function of each state, like so:
// clear the texture manager for(int i = 0; i < m_textureIDList.size(); i++) { TheTextureManager::Instance()-> clearFromTextureMap(m_textureIDList[i]); }
Before we start running the game we need to register our MenuButton
type with the GameObjectFactory
. Open up Game.cpp
and in the Game::init
function we can register the type.
TheGameObjectFactory::Instance()->registerType("MenuButton", new MenuButtonCreator());
We can now run the game and see our fully data-driven MainMenuState
.
3.21.93.20