Our
MainMenuState
class now loads from an XML file. We need to make our other states do the same. We will only cover the code that has changed, so assume that everything else has remained the same when following through this section.
We will start with
PlayState.cpp
and its onEnter
function.
bool PlayState::onEnter() { // parse the state StateParser stateParser; stateParser.parseState("test.xml", s_playID, &m_gameObjects, &m_textureIDList); std::cout << "entering PlayState "; return true; }
We must also add the new texture clearing code that we had in MainMenuState
to the onExit
function.
// clear the texture manager for(int i = 0; i < m_textureIDList.size(); i++) { TheTextureManager::Instance()-> clearFromTextureMap(m_textureIDList[i]); }
These are the only alterations that we will need to do here but we must also update our XML file to have something to load in PlayState
.
<PLAY> <TEXTURES> <texture filename="helicopter.png" ID="helicopter"/> <texture filename="helicopter2.png" ID="helicopter2"/> </TEXTURES> <OBJECTS> <object type="Player" x="500" y="100" width="128" height="55" textureID="helicopter" numFrames="4"/> <object type="Enemy" x="100" y="100" width="128" height="55" textureID="helicopter2" numFrames="4"/> </OBJECTS> </PLAY>
Our Enemy
object will now need to set its initial velocity in its load function rather than the constructor, otherwise the load
function would override it.
void Enemy::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); m_velocity.setY(2); }
Finally we must register these objects with the factory. We can do this in the Game::init
function just like the MenuButton
object.
TheGameObjectFactory::Instance()->registerType("Player", new PlayerCreator()); TheGameObjectFactory::Instance()->registerType("Enemy", new EnemyCreator());
Our PauseState
class must now inherit from MenuState
as we want it to contain callbacks. We must update the PauseState.h
file to first inherit from MenuState
.
class PauseState : public MenuState
We must also declare the setCallbacks
function.
virtual void setCallbacks(const std::vector<Callback>& callbacks);
Now we must update the PauseState.cpp
file, starting with the onEnter
function.
bool PauseState::onEnter() { StateParser stateParser; stateParser.parseState("test.xml", s_pauseID, &m_gameObjects, &m_textureIDList); m_callbacks.push_back(0); m_callbacks.push_back(s_pauseToMain); m_callbacks.push_back(s_resumePlay); setCallbacks(m_callbacks); std::cout << "entering PauseState "; return true; }
The setCallbacks
function is exactly like MainMenuState
.
void PauseState::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()]); } } }
Finally we must add the texture clearing code to onExit
.
// clear the texture manager for(int i = 0; i < m_textureIDList.size(); i++) { TheTextureManager::Instance()-> clearFromTextureMap(m_textureIDList[i]); }
And then update our XML file to include this state.
<PAUSE> <TEXTURES> <texture filename="resume.png" ID="resumebutton"/> <texture filename="main.png" ID="mainbutton"/> </TEXTURES> <OBJECTS> <object type="MenuButton" x="200" y="100" width="200" height="80" textureID="mainbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="200" y="300" width="200" height="80" textureID="resumebutton" numFrames="0" callbackID="2"/> </OBJECTS> </PAUSE>
Our final state is GameOverState
. Again this will be very similar to other states and we will only cover what has changed. Since we want GameOverState
to handle callbacks it will now inherit from MenuState
.
class GameOverState : public MenuState
We will then declare the setCallbacks
function.
virtual void setCallbacks(const std::vector<Callback>& callbacks);
The onEnter
function should be looking very familiar now.
bool GameOverState::onEnter() { // parse the state StateParser stateParser; stateParser.parseState("test.xml", s_gameOverID, &m_gameObjects, &m_textureIDList); m_callbacks.push_back(0); m_callbacks.push_back(s_gameOverToMain); m_callbacks.push_back(s_restartPlay); // set the callbacks for menu items setCallbacks(m_callbacks); std::cout << "entering PauseState "; return true; }
The texture clearing method is the same as in the previous states, so we will leave you to implement that yourself. In fact onExit
is looking so similar between states that it would be a good idea to make a generic implementation for it in GameState
and just use that; again we will leave that to you.
You may have noticed the similarity between the onEnter
functions. It would be great to have a default onEnter
implementation but, unfortunately, due to the need to specify different callback functions, our callback implementation will not allow this and this is one of its main flaws.
Our
AnimatedGraphic
class will now need to grab the animSpeed
value from LoaderParams
in its load
function.
void AnimatedGraphic::load(const LoaderParams *pParams) { SDLGameObject::load(pParams); m_animSpeed = pParams->getAnimSpeed(); }
We will also have to register this type with GameObjectFactory
.
TheGameObjectFactory::Instance()->registerType("AnimatedGraphic", new AnimatedGraphicCreator());
And finally we can update the XML file to include this state:
<GAMEOVER> <TEXTURES> <texture filename="gameover.png" ID="gameovertext"/> <texture filename="main.png" ID="mainbutton"/> <texture filename="restart.png" ID="restartbutton"/> </TEXTURES> <OBJECTS> <object type="AnimatedGraphic" x="200" y="100" width="190" height="30" textureID="gameovertext" numFrames="2" animSpeed="2"/> <object type="MenuButton" x="200" y="200" width="200" height="80" textureID="mainbutton" numFrames="0" callbackID="1"/> <object type="MenuButton" x="200" y="300" width="200" height="80" textureID="restartbutton" numFrames="0" callbackID="2"/> </OBJECTS> </GAMEOVER>
We now have all of our states loading from the XML file and one of the biggest benefits of this is that you do not have to recompile the game when you change a value. Go ahead and change the XML file to move positions or even use different textures for objects; if the XML is saved then you can just run the game again and it will use the new values. This is a huge time saver for us and gives us complete control over a state without the need to recompile our game.
13.59.107.152