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; }
效果对比
- 初始启动效果:

- 洗牌后效果:

问题根源
- 重复加载纹理:每次洗牌都重新加载所有纹理,不仅浪费资源,还可能导致Sprite关联的纹理状态异常,影响后续尺寸计算。
- 重复初始化棋盘:每次洗牌都重新设置棋盘的位置、尺寸和颜色,无意义的重复操作可能引入浮点精度累积误差。
- 依赖
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
相关产品推荐
相关产品推荐

