You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++ SFML迁移初始化代码至其他函数时的作用域问题求助

SFML网格渲染空白问题:原因与解决方案

问题描述

使用C++ SFML创建带图片的简易网格时,初始化代码全部放在gameLoop内程序运行正常,但将精灵网格初始化逻辑拆分到initGuiBoard函数后,窗口仅显示纯白色。

原正常代码(gameLoop内完整初始化):

void gameLoop()
{
    sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Minesweeper", sf::Style::Titlebar | sf::Style::Close);
    std::vector<std::vector<sf::Sprite>> grid(DEFAULT_ROWS_NUM, std::vector<sf::Sprite>(DEFAULT_COLS_NUM));

    sf::Texture unrevealedEmptyTexture;
    if (!unrevealedEmptyTexture.loadFromFile("Images/unrevealed_empty.png"))
        return;

    // 设置每个精灵的位置和缩放
    for (int i = 0; i < DEFAULT_ROWS_NUM; i++)
    {
        for (int j = 0; j < DEFAULT_COLS_NUM; j++)
        {
            grid[i][j].setTexture(unrevealedEmptyTexture);
            grid[i][j].setPosition(i * CELL_SIZE, j * CELL_SIZE);

            // 缩放至CELL_SIZE
            grid[i][j].setScale((float)CELL_SIZE / unrevealedEmptyTexture.getSize().x, (float)CELL_SIZE / unrevealedEmptyTexture.getSize().y);
        }
    }

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        //window.clear(sf::Color::White);
        drawBoard(window, grid);
        window.display();
    }
}

拆分后的initGuiBoard函数:

std::vector<std::vector<sf::Sprite>> initGuiBoard()
{
    std::vector<std::vector<sf::Sprite>> grid(DEFAULT_ROWS_NUM, std::vector<sf::Sprite>(DEFAULT_COLS_NUM));

    sf::Texture unrevealedEmptyTexture;
    if (!unrevealedEmptyTexture.loadFromFile("Images/unrevealed_empty.png"))
        return {};

    // 设置每个精灵的位置和缩放
    for (int i = 0; i < DEFAULT_ROWS_NUM; i++)
    {
        for (int j = 0; j < DEFAULT_COLS_NUM; j++)
        {
            grid[i][j].setTexture(unrevealedEmptyTexture);
            grid[i][j].setPosition(i * CELL_SIZE, j * CELL_SIZE);

            // 缩放至CELL_SIZE
            grid[i][j].setScale((float)CELL_SIZE / unrevealedEmptyTexture.getSize().x, (float)CELL_SIZE / unrevealedEmptyTexture.getSize().y);
        }
    }

    return grid;
}

拆分后的gameLoop调用:

void gameLoop()
{
    sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Minesweeper", sf::Style::Titlebar | sf::Style::Close);
    std::vector<std::vector<sf::Sprite>> guiGrid = initGuiBoard();

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        //window.clear(sf::Color::White);
        drawBoard(window, guiGrid);
        window.display();
    }
}

核心原因

SFML的Sprite仅持有对Texture的引用(指针),并不复制Texture的像素数据。在拆分后的代码中,initGuiBoard函数内的unrevealedEmptyTexture是局部变量,函数执行完毕返回后,该Texture会被销毁,导致所有Sprite指向的Texture失效,渲染时无法获取有效像素数据,最终显示空白。

解决方案

方案1:将Texture提升为全局变量

把Texture定义在函数外部,使其生命周期与程序一致:

// 全局Texture,生命周期覆盖整个程序运行期
sf::Texture unrevealedEmptyTexture;

std::vector<std::vector<sf::Sprite>> initGuiBoard()
{
    std::vector<std::vector<sf::Sprite>> grid(DEFAULT_ROWS_NUM, std::vector<sf::Sprite>(DEFAULT_COLS_NUM));
    
    if (!unrevealedEmptyTexture.loadFromFile("Images/unrevealed_empty.png"))
        return {};

    // 设置精灵逻辑不变
    for (int i = 0; i < DEFAULT_ROWS_NUM; i++)
    {
        for (int j = 0; j < DEFAULT_COLS_NUM; j++)
        {
            grid[i][j].setTexture(unrevealedEmptyTexture);
            grid[i][j].setPosition(i * CELL_SIZE, j * CELL_SIZE);
            grid[i][j].setScale((float)CELL_SIZE / unrevealedEmptyTexture.getSize().x, (float)CELL_SIZE / unrevealedEmptyTexture.getSize().y);
        }
    }

    return grid;
}

方案2:将Texture作为引用参数传入

让gameLoop管理Texture的生命周期,通过引用传递给initGuiBoard:

std::vector<std::vector<sf::Sprite>> initGuiBoard(sf::Texture& unrevealedEmptyTexture)
{
    std::vector<std::vector<sf::Sprite>> grid(DEFAULT_ROWS_NUM, std::vector<sf::Sprite>(DEFAULT_COLS_NUM));
    
    if (!unrevealedEmptyTexture.loadFromFile("Images/unrevealed_empty.png"))
        return {};

    // 设置精灵逻辑不变
    for (int i = 0; i < DEFAULT_ROWS_NUM; i++)
    {
        for (int j = 0; j < DEFAULT_COLS_NUM; j++)
        {
            grid[i][j].setTexture(unrevealedEmptyTexture);
            grid[i][j].setPosition(i * CELL_SIZE, j * CELL_SIZE);
            grid[i][j].setScale((float)CELL_SIZE / unrevealedEmptyTexture.getSize().x, (float)CELL_SIZE / unrevealedEmptyTexture.getSize().y);
        }
    }

    return grid;
}

void gameLoop()
{
    sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Minesweeper", sf::Style::Titlebar | sf::Style::Close);
    sf::Texture unrevealedEmptyTexture; // Texture生命周期覆盖整个循环
    std::vector<std::vector<sf::Sprite>> guiGrid = initGuiBoard(unrevealedEmptyTexture);

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        drawBoard(window, guiGrid);
        window.display();
    }
}

方案3:封装为类(推荐)

将网格和Texture封装到同一个类中,确保两者生命周期一致,同时优化代码结构:

class GuiBoard {
private:
    sf::Texture unrevealedEmptyTexture;
    std::vector<std::vector<sf::Sprite>> grid;

public:
    GuiBoard() {
        // 加载Texture
        if (!unrevealedEmptyTexture.loadFromFile("Images/unrevealed_empty.png")) {
            // 可添加加载失败处理逻辑
            return;
        }

        // 初始化网格精灵
        grid.resize(DEFAULT_ROWS_NUM, std::vector<sf::Sprite>(DEFAULT_COLS_NUM));
        for (int i = 0; i < DEFAULT_ROWS_NUM; i++) {
            for (int j = 0; j < DEFAULT_COLS_NUM; j++) {
                grid[i][j].setTexture(unrevealedEmptyTexture);
                grid[i][j].setPosition(i * CELL_SIZE, j * CELL_SIZE);
                grid[i][j].setScale((float)CELL_SIZE / unrevealedEmptyTexture.getSize().x, 
                                    (float)CELL_SIZE / unrevealedEmptyTexture.getSize().y);
            }
        }
    }

    // 绘制网格
    void draw(sf::RenderWindow& window) {
        for (auto& row : grid) {
            for (auto& sprite : row) {
                window.draw(sprite);
            }
        }
    }
};

void gameLoop()
{
    sf::RenderWindow window(sf::VideoMode(WINDOW_WIDTH, WINDOW_HEIGHT), "Minesweeper", sf::Style::Titlebar | sf::Style::Close);
    GuiBoard guiBoard; // 类实例持有Texture和网格,生命周期一致

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        guiBoard.draw(window);
        window.display();
    }
}

内容的提问来源于stack exchange,提问作者neo gru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 18:42:07