C++ SFML迁移精灵代码至Player类后显示白框求助
SFML精灵迁移到Player类后显示白框问题解决
问题描述
原本在Main.cpp中编写的精灵纹理代码可正常运行,但将逻辑迁移至Player类后,精灵显示为白框。目前仅使用方向静止精灵,需先解决该问题再进行动画开发。
提供代码
Main.cpp
#include <SFML/Graphics.hpp> #include <iostream> #include "Initialise.h" #include"Player.h" using namespace sf; int main() { sf::RenderWindow window(sf::VideoMode(1920, 1080, 32), "Game Demo", sf::Style::Default); sf::Texture txt_playerIdle; if (!txt_playerIdle.loadFromFile("[removed from post, is correct directory]")) { std::cout << "Error loading the image!" << std::endl; } Player player(1.f, 2.f, 0.f, txt_playerIdle); player.GetPlayerSprite().setTextureRect({ 0, 0, 32, 32 }); const float f_FPS = 60.0f; bool b_redraw = true; sf::Clock clock; sf::Event event; while (window.isOpen()) { if (clock.getElapsedTime().asSeconds() >= 1.0f / f_FPS) { b_redraw = true; clock.restart(); } player.MovePlayer(); while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) { window.close(); } } if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) { player.GetPlayerSprite().setTextureRect({ 0, 32, 32, 32 }); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) { player.GetPlayerSprite().setTextureRect({ 0, 96, 32, 32 }); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) { player.GetPlayerSprite().setTextureRect({ 0, 0, 32, 32 }); } else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) { player.GetPlayerSprite().setTextureRect({ 0, 64, 32, 32 }); } if (b_redraw) { window.clear(); window.draw(player.GetPlayerSprite()); window.display(); b_redraw = false; } } return 0; }
Player.cpp
#include "Player.h" void Player::SetPlayerSpeed(float NewSpeed) { PlayerSpeed = NewSpeed; } float Player::GetPlayerSpeed() { return this->PlayerSpeed; } void Player::SetPlayerSprite(sf::Sprite sprite) { PlayerSprite = sprite; } sf::Sprite& Player::GetPlayerSprite() { return PlayerSprite; } void Player::MovePlayer() { if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) { PlayerSprite.setPosition(PlayerSprite.getPosition().x, PlayerSprite.getPosition().y - PlayerSpeed); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) { PlayerSprite.setPosition(PlayerSprite.getPosition().x - PlayerSpeed, PlayerSprite.getPosition().y); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) { PlayerSprite.setPosition(PlayerSprite.getPosition().x, PlayerSprite.getPosition().y + PlayerSpeed); } if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) { PlayerSprite.setPosition(PlayerSprite.getPosition().x + PlayerSpeed, PlayerSprite.getPosition().y); } }
Player.h
#pragma once #include "GameObject.h" using namespace std; class Player :public GameObject { private: int PlayerHealth; int PlayerMaxHealth; float PlayerSpeed; sf::Texture PlayerTexture; sf::Sprite PlayerSprite; public: void SetPlayerHealth(int NewPlayerHealth); int GetPlayerHealth(); void SetPlayerMaxHealth(int NewPlayerMaxHealth); int GetPlayerMaxHealth(); void SetPlayerSpeed(float NewPlayerSpeed); float GetPlayerSpeed(); void SetPlayerSprite(sf::Sprite NewPlayerSprite); /*sf::Sprite GetPlayerSprite();*/ sf::Sprite& GetPlayerSprite(); void MovePlayer(); Player(float speed, float scale, float rotation, sf::Texture texture) { this->SetScale(scale); this->SetRotation(rotation); PlayerSpeed = speed; PlayerTexture = texture; PlayerSprite.setTexture(texture); } };
问题根源
SFML的sf::Sprite并不存储纹理数据,仅持有纹理的引用。你的代码存在两个核心问题:
Player构造函数的参数是sf::Texture texture(传值方式),会创建Main中txt_playerIdle的临时副本。- 调用
PlayerSprite.setTexture(texture)时,Sprite保存的是这个临时纹理的引用。构造函数执行完毕后,临时纹理被销毁,Sprite的纹理引用失效,导致渲染时显示白框。 - 类中的
PlayerTexture成员完全没用,因为你没让Sprite绑定这个成员纹理。
修复方案
方案1:传递纹理引用(推荐,保留Main中的纹理加载逻辑)
修改Player类的构造函数,让Sprite绑定到Main中持久存在的纹理:
- 修改
Player.h中的构造函数声明与实现:
// 构造函数参数改为引用 Player(float speed, float scale, float rotation, sf::Texture& texture) { this->SetScale(scale); this->SetRotation(rotation); PlayerSpeed = speed; // 直接绑定传入的纹理引用,无需复制 PlayerSprite.setTexture(texture); }
- 删除
Player类中无用的PlayerTexture成员变量。
方案2:让Player类自行管理纹理
如果希望Player类独立负责纹理加载,可改为接收文件路径:
- 修改
Player.h:
// 保留PlayerTexture成员,构造函数改为接收路径 private: int PlayerHealth; int PlayerMaxHealth; float PlayerSpeed; sf::Texture PlayerTexture; // 保留,用于存储纹理 sf::Sprite PlayerSprite; public: Player(float speed, float scale, float rotation, const std::string& texturePath) { this->SetScale(scale); this->SetRotation(rotation); PlayerSpeed = speed; // 内部加载纹理 if (!PlayerTexture.loadFromFile(texturePath)) { std::cerr << "Failed to load texture: " << texturePath << std::endl; } PlayerSprite.setTexture(PlayerTexture); }
- 修改
Main.cpp中的实例化代码:
// 移除Main中的txt_playerIdle变量,直接传入路径 Player player(1.f, 2.f, 0.f, "[removed from post, is correct directory]");
额外优化(可选)
避免在Main中直接操作Player的Sprite,封装纹理矩形设置逻辑,符合面向对象原则:
在Player.h中添加:
void SetTextureRect(const sf::IntRect& rect) { PlayerSprite.setTextureRect(rect); }
然后Main中的代码改为:
player.SetTextureRect({0, 0, 32, 32}); // 按键判断部分也同步修改为调用SetTextureRect
内容的提问来源于stack exchange,提问作者RelCade
相关产品推荐
相关产品推荐

