Недавно я начал изучать SFML по книге и столкнулся с проблемой: при загрузке изображения вместо этого появляется большой белый квадрат.
У меня есть класс TextureHolder
для управления игровыми ресурсами, такими как текстуры, спрайты и т. д. (еще не закончен). Я пытался применить подход RAII, но теперь он не работает (я учусь по книге)
Текстура.hpp
#pragma once
#include <map>
#include <memory>
#include <SFML/Graphics.hpp>
namespace Textures {
enum class ID { Landscape, Airplane, Missile };
}
class TextureHolder final {
public:
void load(Textures::ID id, const std::string& filename);
sf::Texture& get(Textures::ID id);
const sf::Texture& get(Textures::ID id) const;
private:
std::map<Textures::ID, std::unique_ptr<sf::Texture>> m_TextureMap;
};
Текстура.cpp
#include <iostream>
#include "Textures.hpp"
void TextureHolder::load(Textures::ID id, const std::string &filename) {
std::unique_ptr<sf::Texture> texture(new sf::Texture());
if (!texture->loadFromFile(filename)) {
std::cerr << "Failed to load texture from file: " << filename << std::endl;
return;
}
m_TextureMap.insert(std::make_pair(id, std::move(texture)));
}
sf::Texture &TextureHolder::get(const Textures::ID id) {
const auto found = m_TextureMap.find(id);
return *found->second;
}
const sf::Texture& TextureHolder::get(const Textures::ID id) const {
const auto found = m_TextureMap.find(id);
return *found->second;
}
Game.hpp
#pragma once
#include <bitset>
#include <SFML/Graphics.hpp>
enum Movement {
Up,
Down,
Left,
Right,
MovementCount
};
class Game final {
public:
explicit Game();
void run();
~Game() = default;
private:
void processEvents();
void update(sf::Time deltaTime);
void render();
void handlePlayerInput(sf::Keyboard::Key , bool );
sf::RenderWindow m_window;
sf::Texture m_texturePlane;
sf::Sprite m_spritePlane;
std::bitset<MovementCount> m_isMoving;
};
Game.cpp
#include "Game.hpp"
#include <iostream>
#include "Textures.hpp"
namespace {
constexpr short WIDTH = 640;
constexpr short HEIGHT = 480;
const std::string TITLE = "SFML Application";
constexpr float PLAYER_SPEED = 500.0f;
}
Game::Game() : m_window(sf::VideoMode(WIDTH, HEIGHT), TITLE) {
m_window.setVerticalSyncEnabled(true);
TextureHolder texture_holder;
texture_holder.load(Textures::ID::Airplane, "/home/davit/CLionProjects/untitled/plane-animated-top-down-game-art.png");
m_spritePlane.setTexture(texture_holder.get(Textures::ID::Airplane));
}
void Game::run() {
sf::Clock clock;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
const auto TimePerFrame = sf::seconds(1.f / 60.f);
while (m_window.isOpen()) {
const sf::Time elapsedTime = clock.restart();
timeSinceLastUpdate += elapsedTime;
while (timeSinceLastUpdate > TimePerFrame) {
timeSinceLastUpdate -= TimePerFrame;
processEvents();
update(TimePerFrame);
}
render();
}
}
void Game::processEvents() {
sf::Event event;
while (m_window.pollEvent(event)) {
switch (event.type) {
case sf::Event::KeyPressed:
handlePlayerInput(event.key.code, true);
break;
case sf::Event::KeyReleased:
handlePlayerInput(event.key.code, false);
break;
case (sf::Event::Closed):
m_window.close();
break;
}
}
}
void Game::handlePlayerInput(sf::Keyboard::Key key, bool isPressed) {
if (key == sf::Keyboard::W)
m_isMoving[Up] = isPressed;
else if (key == sf::Keyboard::S)
m_isMoving[Down] = isPressed;
else if (key == sf::Keyboard::A)
m_isMoving[Left] = isPressed;
else if (key == sf::Keyboard::D)
m_isMoving[Right] = isPressed;
}
void Game::update(const sf::Time deltaTime) {
sf::Vector2f movement(0.f, 0.f);
if (m_isMoving[Up])
movement.y -= PLAYER_SPEED;
if (m_isMoving[Down])
movement.y += PLAYER_SPEED;
if (m_isMoving[Left])
movement.x -= PLAYER_SPEED;
if (m_isMoving[Right])
movement.x += PLAYER_SPEED;
m_spritePlane.move(movement * deltaTime.asSeconds());
}
void Game::render() {
m_window.clear();
m_window.draw(m_spritePlane);
m_window.display();
}
Проблема здесь та же, что и в прошлый раз, когда вы публиковали этот код.
Из документации SFML для sf::Sprite::setTexture
.
Аргумент текстуры относится к текстуре, которая должна существовать до тех пор, пока спрайт использует его.
Это неверно для вашего кода, поэтому вы получаете белый прямоугольник.
Game::Game() : m_window(sf::VideoMode(WIDTH, HEIGHT), TITLE) {
m_window.setVerticalSyncEnabled(true);
TextureHolder texture_holder;
texture_holder.load(Textures::ID::Airplane, "/home/davit/CLionProjects/untitled/plane-animated-top-down-game-art.png");
m_spritePlane.setTexture(texture_holder.get(Textures::ID::Airplane));
} // *** all textures are destroyed here ***
Переменная texture_holder
уничтожается в конце конструктора Game
, и все текстуры в ней уничтожаются одновременно. Вам необходимо переместить держатель текстуры из конструктора Game
. Поместите это, например, в класс Game
.
Так
class Game {
...
TextureHolder m_texture_holder; // <==== HERE
sf::RenderWindow m_window;
sf::Texture m_texturePlane;
sf::Sprite m_spritePlane;
Поместив держатель текстуры в класс Game
, вы гарантируете, что все текстуры будут существовать до тех пор, пока существует объект Game
.
См. документацию SFML по адресу Проблема белого квадрата — там довольно хорошо объяснено, в чем проблема и как ее исправить.