Lately I've been using game state stack implementation from a book SFML Game Development and I've stumbled upon a problem with passing data between states. In this implementation State constructors are predefined and I can't really pass values between them. What's the better way to implement game states so I can for example pass player's score from gameplay state to gameover state?
Here is the game state header from the book:
#ifndef BOOK_STATESTACK_HPP
#define BOOK_STATESTACK_HPP
#include <Book/State.hpp>
#include <Book/StateIdentifiers.hpp>
#include <Book/ResourceIdentifiers.hpp>
#include <SFML/System/NonCopyable.hpp>
#include <SFML/System/Time.hpp>
#include <vector>
#include <utility>
#include <functional>
#include <map>
namespace sf
{
class Event;
class RenderWindow;
}
class StateStack : private sf::NonCopyable
{
public:
enum Action
{
Push,
Pop,
Clear,
};
public:
explicit StateStack(State::Context context);
template <typename T>
void registerState(States::ID stateID);
template <typename T, typename Param1>
void registerState(States::ID stateID, Param1 arg1);
void update(sf::Time dt);
void draw();
void handleEvent(const sf::Event& event);
void pushState(States::ID stateID);
void popState();
void clearStates();
bool isEmpty() const;
private:
State::Ptr createState(States::ID stateID);
void applyPendingChanges();
private:
struct PendingChange
{
explicit PendingChange(Action action, States::ID stateID = States::None);
Action action;
States::ID stateID;
};
private:
std::vector<State::Ptr> mStack;
std::vector<PendingChange> mPendingList;
State::Context mContext;
std::map<States::ID, std::function<State::Ptr()>> mFactories;
};
template <typename T>
void StateStack::registerState(States::ID stateID)
{
mFactories[stateID] = [this] ()
{
return State::Ptr(new T(*this, mContext));
};
}
template <typename T, typename Param1>
void StateStack::registerState(States::ID stateID, Param1 arg1)
{
mFactories[stateID] = [this, arg1] ()
{
return State::Ptr(new T(*this, mContext, arg1));
};
}
#endif // BOOK_STATESTACK_HPP
#include <Book/StateStack.hpp>
#include <Book/Foreach.hpp>
#include <cassert>
StateStack::StateStack(State::Context context)
: mStack()
, mPendingList()
, mContext(context)
, mFactories()
{
}
void StateStack::update(sf::Time dt)
{
// Iterate from top to bottom, stop as soon as update() returns false
for (auto itr = mStack.rbegin(); itr != mStack.rend(); ++itr)
{
if (!(*itr)->update(dt))
break;
}
applyPendingChanges();
}
void StateStack::draw()
{
// Draw all active states from bottom to top
FOREACH(State::Ptr& state, mStack)
state->draw();
}
void StateStack::handleEvent(const sf::Event& event)
{
// Iterate from top to bottom, stop as soon as handleEvent() returns false
for (auto itr = mStack.rbegin(); itr != mStack.rend(); ++itr)
{
if (!(*itr)->handleEvent(event))
break;
}
applyPendingChanges();
}
void StateStack::pushState(States::ID stateID)
{
mPendingList.push_back(PendingChange(Push, stateID));
}
void StateStack::popState()
{
mPendingList.push_back(PendingChange(Pop));
}
void StateStack::clearStates()
{
mPendingList.push_back(PendingChange(Clear));
}
bool StateStack::isEmpty() const
{
return mStack.empty();
}
State::Ptr StateStack::createState(States::ID stateID)
{
auto found = mFactories.find(stateID);
assert(found != mFactories.end());
return found->second();
}
void StateStack::applyPendingChanges()
{
FOREACH(PendingChange change, mPendingList)
{
switch (change.action)
{
case Push:
mStack.push_back(createState(change.stateID));
break;
case Pop:
mStack.back()->onDestroy();
mStack.pop_back();
if (!mStack.empty())
mStack.back()->onActivate();
break;
case Clear:
FOREACH(State::Ptr& state, mStack)
state->onDestroy();
mStack.clear();
break;
}
}
mPendingList.clear();
}
StateStack::PendingChange::PendingChange(Action action, States::ID stateID)
: action(action)
, stateID(stateID)
{
}