Текстура SFML отображается в виде белого прямоугольника

У меня есть текстура и спрайт в базовом классе, который расширяется другим классом, однако при рисовании спрайт отображается в виде белого поля. Я знаю, что это как-то связано со спрайтом, теряющим связь с объектом текстуры, но я новичок в C++, поэтому не очень понимаю, как это произошло.

Вот код (я удалил некоторые ненужные части, чтобы уменьшить размер):

Пикап.ч:

#ifndef PICKUPS_PICKUP_H
#define PICKUPS_PICKUP_H

#include <SFML\Graphics.hpp>
#include "..\Player.h"

namespace Pickups
{
    class Pickup
    {
    private:
        sf::Vector2f position;
        sf::Texture texture;
        sf::Sprite sprite;
    public:
        Pickup();

        bool drop(float dt);
        void draw(sf::RenderWindow* window);

        void setTexture(sf::String name);

        void setPos(sf::Vector2f position);
        sf::Vector2f getPos();

        void isColliding(Player* player);

        virtual void onCollect(Player* player) = 0;
    };
}

#endif

пикап.cpp:

#include "Pickup.h"

namespace Pickups
{
    Pickup::Pickup()
    {
    }

    void Pickup::draw(sf::RenderWindow* window)
    {
        window->draw(sprite);
    }

    void Pickup::setTexture(sf::String name)
    {
        if (!texture.loadFromFile("images/pickups/" + name + ".png"))
            std::cout << "Error loading image: images/pickups/" + name.toAnsiString() + ".png" << std::endl;
        else
            sprite.setTexture(texture);
    }
}

Здоровье.ч:

#ifndef PICKUPS_HEALTH_H
#define PICKUPS_HEALTH_H

#include "Pickup.h"

namespace Pickups
{
    class Health : public Pickup
    {
    private:
        int worth;
    public:
        Health(sf::Vector2f position, int worth);
        void onCollect(Player* player);
    };
}

#endif

здоровье.cpp:

#include "Health.h"

namespace Pickups
{
    Health::Health(sf::Vector2f position, int worth)
    {
        setTexture("health");
        setPos(position);
        this->worth = worth;
    }

    void Health::onCollect(Player* player)
    {
        player->addLives(worth);
    }
}

(Я не знаю, является ли это частью проблемы, но я мог бы также опубликовать это) Я сохраняю пикапы в векторе следующим образом:

std::vector<Pickups::Health> pickups;

person Julxzs    schedule 14.01.2015    source источник
comment
Вы должны проверить, что texture.loadFromFile успешно   -  person Collin Dauphinee    schedule 14.01.2015
comment
Вы пытаетесь рисовать перед вызовом setTexture?   -  person Tansir1    schedule 15.01.2015
comment
Кажется, причиной проблемы было хранение пикапов в векторе по какой-то причине...   -  person Julxzs    schedule 15.01.2015


Ответы (2)


std::vector копирует или перемещает вставленные элементы, пока у вас есть конструктор копирования по умолчанию или пока вы не изменяете этот грязный стиль текстуры для каждого элемента (элементы просто должны иметь один общий объект текстуры для фактического указания, поэтому вы тратите много памяти) указатель, который объект sf::Sprite удерживает на текстуру, становится недействительным. Чтобы понять, почему нам нужно думать, что происходит при вставке:

Вы устанавливаете хороший объект Pickupish и добавляете его в вектор, который вызывает конструктор копирования. Допустим, ваш хороший объект, который вы хотели добавить, — это объект A, а теперь добавленный/скопированный объект — B. Оба имеют спрайт S и текстуру T. Обе текстуры действительны, но проблема в следующем: S A указывает на T A, поэтому после копирования в B B S также указывает на T A! Как я предполагаю, A просто временный, поэтому он уничтожается вместе со своей текстурой, и вот он, красивый белый ящик.

Вы можете решить эту проблему другими грязными способами, такими как создание собственного конструктора копирования в Pickup следующим образом:

Pickup::Pickup(const Pickup& other)
: position(other.position), texture(other.texture), sprite(other.sprite)
{ sprite.setTexture(texture); }

или путем хранения std::unique_ptr<Pickups::Health>, а не только Pickups::Health.

Однако гораздо лучше использовать какой-нибудь Resourcemanager, который просто хранит все соответствующие текстуры, в идеале одну, большой набор тайлов, потому что загрузка один раз, но большого, быстрее, чем загрузка нескольких, но маленьких текстур. Вы можете написать свой собственный очень простой менеджер или использовать какой-то другой, например. один из большой библиотеки Thor. Чтобы установить определенный тайл в качестве текстуры для спрайта, просто вызовите sf::Sprite::setTextureRect. .


Хочу отметить некоторые дополнительные улучшения вашего дизайна. Пусть Pickup является производным от sf::Drawable и определяет его чистую виртуальную функцию draw, которую вы можете сделать приватной. Таким образом, вашему производному объекту from Pickup не нужно ничего знать ни от какого sf::RenderTarget, и вы можете просто выполнить target.draw(myPickupObject).

Нет необходимости сохранять позицию, просто пусть Pickup также является производным от sf::Transformable. Вам не нужно реализовывать какие-либо функции, единственное, что вам нужно сделать, это применить матрицу к объекту sf::RenderStates, переданному в draw.

В целом ваша функция draw может выглядеть так:

void Pickup::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    //'applying' transformation-matrix
    states.transform *= getTransform();

    target.draw(sprite, states);
}

Таким образом, ваш Pickup теперь имеет только sf::Sprite в качестве члена, и в целом ваш заголовок просто должен включать SFML/Graphics/Sprite.hpp.

person NaCl    schedule 15.01.2015
comment
Вау, это потрясающий ответ. Огромное спасибо! - person Julxzs; 16.01.2015

Чтобы избежать проблем такого типа, я всегда объявляю свой Texture указателем и удаляю его в деструкторе класса. Таким образом, ваш Texture всегда будет существовать, когда ваш объект не будет уничтожен.

И всегда полезно проверить загрузку изображения:

    if (!texture.loadFromFile("images/pickups/health.png")){
      //some error code
    }

Но проблема не в этом.

person Maltir    schedule 14.01.2015
comment
На самом деле я не использую функцию setTexture, используется текстура из базового класса. Я убедился, что загрузка текстуры также прошла успешно. - person Julxzs; 15.01.2015
comment
Я отредактировал свой код для setTexture и начал его использовать, однако он все еще не работает:/ - person Julxzs; 15.01.2015