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

SFML C++游戏开发:洗牌时Sprite定位偏移与裁剪异常

问题:SFML洗牌操作后Sprite定位与裁剪异常

使用C++ SFML框架开发游戏时,初始启动阶段Sprite可正确居中于棋盘单元格,但执行洗牌操作后,Sprite无法在单元格内居中,且边缘出现裁剪。调整定位逻辑与缩放计算后问题仍未解决。

相关代码

#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <cstdlib>
#include <iostream>
#include <iomanip>

static sf::RectangleShape board[8][8];
static sf::Texture textures[7];
static sf::Sprite sprites[8][8];
int highlightedRow = 0;
int highlightedCol = 0;
const float cellSize = 62.0f;


// Initialize random seed
void RandomSeed()
{
    std::srand(std::time(0));
}
// Center the sprite within the board block
void spriteScaleCenter(int row, int col, float scale)
{
    sprites[row][col].setScale(scale, scale);
    float xOffset = (cellSize - sprites[row][col].getGlobalBounds().width) / 2.0f;
    float yOffset = (cellSize - sprites[row][col].getGlobalBounds().height) / 2.0f;
    sprites[row][col].setPosition(board[row][col].getPosition().x + xOffset, board[row][col].getPosition().y + yOffset);
}

// Stores random images to textures and sets the sprite textures
void randTexturesSprites()
{
    // Stores images in textures array
    if (!textures[0].loadFromFile("image0.png"))
    {
        std::cout << "Failed to load texture: " << "image0.png" << std::endl;
        return;
    }
    if (!textures[1].loadFromFile("image1.png"))
    {
        std::cout << "Failed to load texture: " << "image1.png" << std::endl;
        return;
    }
    if (!textures[2].loadFromFile("image2.png"))
    {
        std::cout << "Failed to load texture: " << "image2.png" << std::endl;
        return;
    }
    if (!textures[3].loadFromFile("image3.png"))
    {
        std::cout << "Failed to load texture: " << "image3.png" << std::endl;
        return;
    }
    if (!textures[4].loadFromFile("image4.png"))
    {
        std::cout << "Failed to load texture: " << "image4.png" << std::endl;
        return;
    }
    if (!textures[5].loadFromFile("image5.png"))
    {
        std::cout << "Failed to load texture: " << "image5.png" << std::endl;
        return;
    }
    if (!textures[6].loadFromFile("image6.png"))
    {
        std::cout << "Failed to load texture: " << "image6.png" << std::endl;
        return;
    }


    // Assign random textures to each sprite and set scale
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            board[i][j].setSize(sf::Vector2f(cellSize, cellSize));
            board[i][j].setPosition((i + 7.9) * cellSize, (j + 0.2) * cellSize);
            if ((i + j) % 2 == 0)
            {
                board[i][j].setFillColor(sf::Color(43, 42, 42)); // Grey
            }
            else
            {
                board[i][j].setFillColor(sf::Color(0, 0, 0)); // Black
            }

            int randomTextureIndex = rand() % 7;
            sprites[i][j].setTexture(textures[randomTextureIndex]);

            // Set the scale of the sprites
            float scale = cellSize / static_cast<float>(textures[randomTextureIndex].getSize().x);
            scale -= 0.07f;
            sprites[i][j].setScale(scale, scale);

            spriteScaleCenter(i, j, scale);
        }
    }
}



int main()
{
    
    // Set up the window
    sf::RenderWindow window(sf::VideoMode(1000, 600), "Blitz", sf::Style::Close);

    RandomSeed();

    // Set up the board
    randTexturesSprites();


    // Game loop
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
            else if (event.type == sf::Event::KeyPressed)
            {
                randTexturesSprites();
            }
        }

        // Clear the window
        window.clear();

        // Set the background color
        window.clear(sf::Color(234, 182, 118)); // Change to your desired background color

        // Draw the board with highlight
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 8; j++)
            {
                window.draw(board[i][j]);
                window.draw(sprites[i][j]);
            }
        }

        // Display the content
        window.display();

    }

    return 0;
}

效果对比

  • 初始启动效果:
    初始启动效果
  • 洗牌后效果:
    洗牌后效果

问题根源

  1. 重复加载纹理:每次洗牌都重新加载所有纹理,不仅浪费资源,还可能导致Sprite关联的纹理状态异常,影响后续尺寸计算。
  2. 重复初始化棋盘:每次洗牌都重新设置棋盘的位置、尺寸和颜色,无意义的重复操作可能引入浮点精度累积误差。
  3. 依赖getGlobalBounds()计算偏移:该方法基于Sprite的变换状态计算边界,在纹理频繁切换时可能出现计算偏差,导致定位错误。

修复方案

1. 拆分初始化逻辑

  • 将纹理加载、棋盘初始化与洗牌操作分离,纹理和棋盘仅在程序启动时初始化一次。

2. 直接计算偏移量

  • 避免使用getGlobalBounds(),改用纹理原始尺寸与缩放比例直接计算Sprite的居中偏移,确保定位准确。

修改后的完整代码

#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <cstdlib>
#include <iostream>
#include <iomanip>

static sf::RectangleShape board[8][8];
static sf::Texture textures[7];
static sf::Sprite sprites[8][8];
int highlightedRow = 0;
int highlightedCol = 0;
const float cellSize = 62.0f;

// Initialize random seed
void RandomSeed()
{
    std::srand(std::time(0));
}

// Load all textures once at startup
bool LoadTextures()
{
    if (!textures[0].loadFromFile("image0.png"))
    {
        std::cout << "Failed to load texture: image0.png" << std::endl;
        return false;
    }
    if (!textures[1].loadFromFile("image1.png"))
    {
        std::cout << "Failed to load texture: image1.png" << std::endl;
        return false;
    }
    if (!textures[2].loadFromFile("image2.png"))
    {
        std::cout << "Failed to load texture: image2.png" << std::endl;
        return false;
    }
    if (!textures[3].loadFromFile("image3.png"))
    {
        std::cout << "Failed to load texture: image3.png" << std::endl;
        return false;
    }
    if (!textures[4].loadFromFile("image4.png"))
    {
        std::cout << "Failed to load texture: image4.png" << std::endl;
        return false;
    }
    if (!textures[5].loadFromFile("image5.png"))
    {
        std::cout << "Failed to load texture: image5.png" << std::endl;
        return false;
    }
    if (!textures[6].loadFromFile("image6.png"))
    {
        std::cout << "Failed to load texture: image6.png" << std::endl;
        return false;
    }
    return true;
}

// Initialize board once at startup
void InitBoard()
{
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            board[i][j].setSize(sf::Vector2f(cellSize, cellSize));
            board[i][j].setPosition((i + 7.9) * cellSize, (j + 0.2) * cellSize);
            if ((i + j) % 2 == 0)
            {
                board[i][j].setFillColor(sf::Color(43, 42, 42)); // Grey
            }
            else
            {
                board[i][j].setFillColor(sf::Color(0, 0, 0)); // Black
            }
        }
    }
}

// Assign random textures to sprites and set proper scale/position
void randTexturesSprites()
{
    for (int i = 0; i < 8; i++)
    {
        for (int j = 0; j < 8; j++)
        {
            int randomTextureIndex = rand() % 7;
            sprites[i][j].setTexture(textures[randomTextureIndex]);

            // Calculate scale based on texture width
            float scale = cellSize / static_cast<float>(textures[randomTextureIndex].getSize().x);
            scale -= 0.07f;
            sprites[i][j].setScale(scale, scale);

            // Calculate centered position using texture size and scale
            sf::Vector2f texSize = textures[randomTextureIndex].getSize();
            float scaledWidth = texSize.x * scale;
            float scaledHeight = texSize.y * scale;
            float xOffset = (cellSize - scaledWidth) / 2.0f;
            float yOffset = (cellSize - scaledHeight) / 2.0f;
            sprites[i][j].setPosition(board[i][j].getPosition().x + xOffset, board[i][j].getPosition().y + yOffset);
        }
    }
}

int main()
{
    sf::RenderWindow window(sf::VideoMode(1000, 600), "Blitz", sf::Style::Close);

    RandomSeed();

    // Load textures and initialize board once
    if (!LoadTextures())
    {
        return 1;
    }
    InitBoard();

    // Set initial sprites
    randTexturesSprites();

    // Game loop
    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            {
                window.close();
            }
            else if (event.type == sf::Event::KeyPressed)
            {
                randTexturesSprites();
            }
        }

        window.clear(sf::Color(234, 182, 118));

        // Draw board and sprites
        for (int i = 0; i < 8; i++)
        {
            for (int j = 0; j < 8; j++)
            {
                window.draw(board[i][j]);
                window.draw(sprites[i][j]);
            }
        }

        window.display();
    }

    return 0;
}

说明

  • 纹理仅加载一次,避免重复IO操作和状态异常。
  • 棋盘初始化仅执行一次,消除无意义的重复计算。
  • 直接通过纹理原始尺寸与缩放比例计算偏移,确保Sprite始终精确居中于单元格,解决裁剪问题。

内容的提问来源于stack exchange,提问作者Muhammad Mujtaba Hussain

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 22:00:55