重新初始化其父类时,SFML对象将不会绘制

时间:2018-01-15 16:57:49

标签: c++ sfml

我正在开发一个新项目并实施一个基本的场景变化。我将不同的场景设置为它们自己的类,使用初始化函数来创建和重新定位不同的SFML对象。我看到了this answer并且同样写了我的场景切换器:

// Create scene monitoring variable
int scene[2];
scene[0] = 0; // Set current scene to menu
scene[1] = 0; // Set scene change to no

...

// Check for scene change
if(scene[1] == 0) {
    // Run tick function based on current scene
    switch(scene[0]) {
        case 0:
            // Main menu - run tick function
            menu.tick();
    }
}
if(scene[1] == 1) {
    // Reset scene that you've changed to
    switch(scene[0]) {
        case 0:
            // Main menu - reset it
            menu = Menu(window, scene);    // <-- Reinitialise menu here
    }
    // Set change variable to 0
    scene[1] = 0;
}

您可以在github repository上看到完整的代码。

但是,这似乎无法正常工作 - 只要进行场景更改,屏幕就会变为空白。该类被重新加入(我添加了一个cout来检查),绘制功能仍在运行,鼠标点击仍在处理中,但窗口中没有任何内容。

我在这里做错了吗?

1 个答案:

答案 0 :(得分:3)

以这种方式做事可能导致泄漏内存错误。我建议你采用不同的方法: StateStack

这是如何运作的?

拥有StateStack对象的基础是将游戏/应用程序的每种可能状态存储到堆栈中。这样,您就可以按堆栈顺序处理每个。

什么是州?

状态可以更新绘制处理事件。我们可以创建一个接口或一个抽象类来使我们的屏幕表现得像一个状态。

有哪些优点?

使用堆栈结构,您可以轻松控制不同场景处理三种不同处理方法的方式。例如。如果您在暂停菜单中点击鼠标,则无法通过点击事件进入菜单状态或游戏&#34;州。要实现这一点,解决方案非常简单,只需在false方法中返回handleEvent,如果您不希望事件进一步发挥此特定状态。请注意,此提示也可扩展为drawupdate方法。在您的暂停菜单中,您不会update您的游戏&#34;州。在你的游戏&#34;说明你没有draw巡回菜单状态。

实施例

考虑到这一点,这是一种可行的实施方式。首先,State界面:

class State{
public:
    virtual bool update() = 0;
    virtual bool draw(sf::RenderTarget& target) const = 0;

    // We will use a vector instead a stack because we can iterate vectors (for drawing, update, etc)
    virtual bool handleEvent(sf::Event e, std::vector<State*> &stack) = 0;
};

按照此界面,我们可以提供示例MenuStatePauseState

<强> MenuState

class MenuState : public State{
public:
    MenuState(){
        m_count = 0;

        m_font.loadFromFile("Roboto-Regular.ttf");
        m_text.setFont(m_font);
        m_text.setString("MenuState: " + std::to_string(m_count));
        m_text.setPosition(10, 10);
        m_text.setFillColor(sf::Color::White);

    }

    virtual bool update() {
        m_count++;
        m_text.setString("MenuState: " + std::to_string(m_count));
        return true;
    }

    virtual bool draw(sf::RenderTarget &target) const{
        target.draw(m_text);
        return true;
    }

    virtual bool handleEvent(sf::Event e, std::vector<State*> &stack){
        if (e.type == sf::Event::KeyPressed){
            if (e.key.code == sf::Keyboard::P){
                stack.push_back(new PauseState());
                return true;
            }
        }
        return true;
    }

private:
    sf::Font m_font;
    sf::Text m_text;
    unsigned int m_count;
};

<强> PauseState

class PauseState : public State{
public:
    PauseState(){
        sf::Font f;
        m_font.loadFromFile("Roboto-Regular.ttf");
        m_text.setFont(m_font);
        m_text.setString("PauseState");
        m_text.setPosition(10, 10);
        m_text.setFillColor(sf::Color::White);

    }

    virtual bool update() {
        // By returning false, we prevent States UNDER Pause to update too
        return false;
    }

    virtual bool draw(sf::RenderTarget &target) const{
        target.draw(m_text);
        // By returning false, we prevent States UNDER Pause to draw too
        return false;
    }

    virtual bool handleEvent(sf::Event e, std::vector<State*> &stack){
        if (e.type == sf::Event::KeyPressed){
            if (e.key.code == sf::Keyboard::Escape){
                stack.pop_back();
                return true;
            }
        }
        return false;
    }

private:
    sf::Font m_font;
    sf::Text m_text;
};

顺便说一下,当我这样做时,我注意到你必须将字体作为类的属性才能保留引用。如果没有,当你的文字被绘制时,它的字体会丢失,然后失败。另一种面对此问题的方法是使用resource holder,它更有效,更强大。

说这个,我们的main看起来像:

主要

int main() {
    // Create window object
    sf::RenderWindow window(sf::VideoMode(720, 720), "OpenTMS");

    // Set window frame rate
    window.setFramerateLimit(60);

    std::vector<State*> stack;

    // Create menu
    stack.push_back(new MenuState());

    // Main window loops
    while (window.isOpen()) {
        // Create events object
        sf::Event event;
        // Loop through events
        while (window.pollEvent(event)) {
            // Close window
            if (event.type == sf::Event::Closed) {
                window.close();
            }

            handleEventStack(event, stack);
        }

        updateStack(stack);
        // Clear window
        window.clear(sf::Color::Black);
        drawStack(window, stack);
        // Display window contents
        window.display();
    }

    return 0;
}

堆栈函数是简单的for循环,但是细节迭代向量向后。这是模仿堆栈行为的方法,从顶部(size-1索引)开始到0结束。

堆叠功能

void handleEventStack(sf::Event e, std::vector<State*> &stack){
    for (int i = stack.size()-1; i >=0; --i){
        if (!stack[i]->handleEvent(e, stack)){
            break;
        }
    }
}

void updateStack(std::vector<State*> &stack){
    for (int i = stack.size() - 1; i >= 0; --i){
        if (!stack[i]->update()){
            break;
        }
    }
}

void drawStack(sf::RenderTarget &target, std::vector<State*> &stack){
    for (int i = stack.size() - 1; i >= 0; --i){
        if (!stack[i]->draw(target)){
            break;
        }
    }
}

您可以了解有关StateStack和gamedev的更多信息with this book