1
\$\begingroup\$

I am using a state stack as the mechanism to transition through states in my game. Currently moving to a new state works correctly, but when I want to go back to a previous state(removing top state) the application crashes. The GameSelectState class is supposed to have a menu button that when clicked should go back to the MenuState.

Game.hpp:

#pragma once
#include <SFML/Graphics/RenderWindow.hpp>
#include "StateStack.hpp"
#include "TextureManager.hpp"


class Game
{
public:
    Game();
    void run();

private:
    sf::RenderWindow window;
    StateStack states;
    TextureManager textures;
};

Game.cpp:

#include <memory>
#include <SFML/System/Time.hpp>
#include "Game.hpp"
#include "SplashState.hpp"
#include "GameState.hpp"


Game::Game() :
    window(sf::VideoMode(1680, 1050), "Variadic-Poker"),
    states(),
    textures()
{
    this->states.pushState(std::make_unique<SplashState>(window, states, textures));
}

void Game::run()
{
    const sf::Time timePerFrame = sf::seconds(1.f / 60.f);

    sf::Clock clock;
    sf::Time elapsedTime = sf::Time::Zero;

    while (this->window.isOpen())
    {
        sf::Time deltaTime = clock.restart();
        elapsedTime += deltaTime;

        while (elapsedTime > timePerFrame)
        {
            elapsedTime -= timePerFrame;

            this->states.getState()->processEvent();
            this->states.getState()->update(deltaTime);
            this->window.clear(sf::Color::Black);
        }

        this->states.getState()->draw();
        this->window.display();
    }
}

StateStack.hpp:

#pragma once
#include <memory>
#include <stack>
#include "GameState.hpp"


class StateStack
{
public:
    StateStack();

    std::unique_ptr<GameState>& getState();

    void pushState(std::unique_ptr<GameState> state);

    void popState();

    void clearStates();

    void changeState(std::unique_ptr<GameState> state);

private:
    std::stack<std::unique_ptr<GameState>> states;
};

StateStack.cpp:

    #include "StateStack.hpp"


    StateStack::StateStack() :
        states()
    {
    }



std::unique_ptr<GameState>& StateStack::getState() 
{
    return this->states.top();
}

void StateStack::pushState(std::unique_ptr<GameState> state)
{
    this->states.push(std::move(state));
}

void StateStack::popState()
{
    this->states.pop();
}

void StateStack::clearStates()
{
    while (!this->states.empty())
    {
        this->states.pop();
    }
}

void StateStack::changeState(std::unique_ptr<GameState> state)
{
    this->popState();
    this->states.push(std::move(state));
}

GameSelectState.hpp:

#pragma once
#include <SFML/Graphics/Sprite.hpp>
#include <SFGUI/Window.hpp>
#include <SFGUI/Button.hpp>
#include "GameState.hpp"
#include "TextureManager.hpp"


class StateStack;

class GameSelectState : public GameState
{
public:
    GameSelectState(sf::RenderWindow& window, StateStack& states, TextureManager& textures);

    virtual void processEvent() override;
    virtual void update(sf::Time deltaTime) override;
    virtual void draw() override;

    void transitionToMenu();
    void transitionToPlay();

private:
    sf::Sprite background;
    sfg::Button::Ptr menuButton;
};

GameSelect.cpp:

 #include <memory>
    #include <SFML/Window/Event.hpp>
    #include "StateStack.hpp"
    #include "GameSelectState.hpp"
    #include "MenuState.hpp"
    #include "PlayState.hpp"


    GameSelectState::GameSelectState(sf::RenderWindow& window, StateStack& states, TextureManager& textures) :
        GameState(window, states, textures),
        menuButton(),
    {

        menuButton = sfg::Button::Create("  Menu  ");
        menuButton->GetSignal(sfg::Widget::OnLeftClick).Connect( [this] { transitionToMenu(); });

        hud.Add(menuButton);
    }

    void GameSelectState::processEvent()
    {
        sf::Event event;

        while (this->window.pollEvent(event))
        {
            this->hud.HandleEvent(event);

            switch (event.type)
            {
            case sf::Event::Closed:
                this->window.close();
                break;
            case sf::Event::Resized:
                this->selectionWindow->SetPosition(sf::Vector2f(window.getSize().x / 2.5f, window.getSize().y / 3.1f));
                break;
            }
        }
    }

    void GameSelectState::update(sf::Time deltaTime)
    {
        this->hud.Update(deltaTime.asMilliseconds());
    }

    void GameSelectState::draw()
    {
        this->window.clear(sf::Color::Black);
        this->window.draw(this->background);
        this->sfgui.Display(this->window);
    }

    void GameSelectState::transitionToMenu()
    {
        this->menuButton->Show(false);
        this->states.popState();
    }

    void GameSelectState::transitionToPlay()
    {
        this->menuButton->Show(false);
        this->selectionWindow->Show(false);
        this->states.pushState(std::make_unique<PlayState>(this->window, this->states, this->textures));
    }

The transitionToMenu() method in the GameSelectState class is supposed to be called when the button gets clicked, this method pops the top of the stack and it is supposed to return to the menu of the game. Currently when I click the button the application crashes. I am not sure if issue is occurring because of some error with the unique pointer manipulations or if the problem is related to the reloading of the previous game state in the game loop. This is my first time making a game and first time implementing a state machine, I would appreciate any pointers or help with the implementation of state transitioning in the game.

\$\endgroup\$
  • 1
    \$\begingroup\$ Did you put a breakpoint on the click of the button then step-by-step debug where it crashes? \$\endgroup\$ – Vaillancourt Apr 4 '17 at 23:53
  • \$\begingroup\$ I've never really used a debugger but it is currently signaling a problem in the processEvent() method in the GameSelectState class, in this line: while (this->window.pollEvent(event)) \$\endgroup\$ – InversePalindrome Apr 5 '17 at 0:18
  • \$\begingroup\$ You should really start to use a debugger. That's the tool to find this kind of issue :) \$\endgroup\$ – Vaillancourt Apr 5 '17 at 0:20
1
\$\begingroup\$

Do you remember these cartoons where the goofy character saw the branch of the tree, but realized too late that he was on the wrong side of the cut?

That's what you seem to be doing here.

void GameSelectState::transitionToMenu()
{
    this->menuButton->Show(false);
    this->states.popState();
}

Here, this->states.popState(); goes into you statesStack, deletes the 'current' state, and returns.

...returns to where?

The state, presumably the one that called the function, is the cause of it self deletion. When the code continues, it continues on a deleted object, where all hell can break lose.

Now it might not be the 'best' way to fix this, but you could set up the state transition but actually perform it in your while (elapsedTime > timePerFrame) loop, outside of your stack.

\$\endgroup\$
  • 1
    \$\begingroup\$ I very much appreciate the use of analogy here. I'll keep that in mind for the next time I see one of these types of errors crop up. :) \$\endgroup\$ – DMGregory Apr 5 '17 at 0:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.