Sf::Texture as Class Member Doesn't Work

sf::Texture as class member doesn't work?

This is called the white square problem.

Basically, at some point, your object is copied but the copy constructor doesn't update the copied sprite texture to use the copied texture, and the original texture is destroyed so the copied sprite doesn't have a valid texture anymore.

A quick fix can simply run the initialisation code inside the copy constructor and copy assignment operator.


BTW

myimg_texture.create(icon.width, icon.height);
myimg_texture.update(myimg_image);

can directly use icon.pixelData instead of myimg_image and thus you don't need an sf::Image at all.

Or you can do as follow if you need the sf::Image for another purpose:

myimg_texture.loadFromImage(myimg_image);

SFML - Vector with custom class doesn't load textures

I've finally solved it! Thanks, @rafix07!

this

worldObjects.push_back({name, width, height, x, y});

line should be substituted by:

WorldObject *tmp = new WorldObject(name, width, height, x, y);
worldObjects.push_back(*tmp);

Remember to delete the object when it's not needed to reduce risk of memory leaks.

Initialize texture once for derived classes in sfml

The Flyweight Pattern

SFML website tells me that the most efficient way to use textures is to update sprites. Otherwise, our program may consume a lot of memory.

Yes, the relationship between sf::Sprite and sf::Texture corresponds to The Flyweight Pattern, a structural design pattern.

An sf::Texture is a kind of heavyweight object whose data we want to share when possible to save memory. An sf::Sprite is a flyweight object, and multiple sf::Sprite objects can refer to the same sf::Texture object to share its data.

The sf::Sprite only contains information that is specific to a particular use of the texture it refers to, e.g., the position of the texture, its rotation, size – this is the extrinsic state. An sf::Texture object contains data (i.e., the texture image itself) that can be shared by multiple sf::Sprite objects – this is the intrinsic state that is shared by all the sf::Sprite objects that refer to the same sf::Texture.

In the SFML library you will also find this pattern with sf::Text/sf::Font and sf::Sound/sf::SoundBuffer.



Not giving up Flyweight

Keeping the pattern exposed above in mind, you may want to follow it in your design as well:

class Base {
public:
Base(const sf::Texture& texture): sprite(texture) {}
protected:
sf::Sprite sprite;
};

class Derived: public Base {
public:
Derived(const sf::Texture& texture): Base(texture) {}
// ...
};

The sprite data member is initialized with the sf::Texture passed by reference to the constructor, and multiple Base and Derived objects can share the same sf::Texture.

As an example:

sf::Texture myTexture;
myTexture.loadFromFile("myImageFile.png");

Base b1(myTexture), b2(myTexture);
Derived d1(myTexture), d2(myTexture);

b1, b2, d1 and d2 all share the same sf::Texture instance – myTexture:

Flyweight applied

Of course, none of these objects should outlive myTexture.

sf::Texture applied in a wrong way

It looks like the texture coordinates might be outside of the texture.

If the pointPosition function refers to the p0, p1, p2, and p3 points in side.cpp, then it also looks like you're using those points as texture coordinates.

Consider how you would might create a 2D square-

//Create a VertexArray as quads.
sf::VertexArray vertices;
vertices.setPrimitiveType(sf::Quads);
vertices.resize(4);

//The square position values (same as sf::Vertex::position).
sf::Vector2f v0 = sf::Vector2f(10, 10);
sf::Vector2f v1 = sf::Vector2f(200, 10);
sf::Vector2f v2 = sf::Vector2f(200, 200);
sf::Vector2f v3 = sf::Vector2f(10, 200);

//The texture coordinates (same as sf::Vertex::texCoords).
sf::Vector2f tv0 = sf::Vector2f(0, 0 );
sf::Vector2f tv1 = sf::Vector2f(0+tex.getSize().x, 0 );
sf::Vector2f tv2 = sf::Vector2f(0+tex.getSize().x, tex.getSize().y);
sf::Vector2f tv3 = sf::Vector2f(0, tex.getSize().y);

//Put them in vertices.
vertices[0] = sf::Vertex(v0,tv0);
vertices[1] = sf::Vertex(v1,tv1);
vertices[2] = sf::Vertex(v2,tv2);
vertices[3] = sf::Vertex(v3,tv3);

The square points don't have to be the same as the texture coordinates, since the texture coordinates will stretch to each square point. If you were to change a point in one of the square points, it may look like this.

BEFORE

Sample Image

AFTER (v2 changed to 200,300)

Sample Image

We don't need to change the texture coordinates at all, just the vertex positions.

I'm not sure if that's exactly what's going on here since I can't see what pointPosition is, but it's my best guess.

Also, trying to draw outside of a texture can result in it looking like the border pixels of the image are stretched (possibly what's happening in your example), and sometimes you can even display data from other information in video memory as a result of going beyond your texture's memory.

(SFML) Issues with inheritance (C++)

Ok, after consulting the SFML Forums, I have refactored the code to the following:

Sprite.h:

#include "AssetManager.h"

class Sprite{
public:
sf::Sprite m_sprite;

sf::Vector2f sprite_scale;
sf::Vector2u original_size;
sf::Vector2f texture_size;

Sprite(){}
sf::Sprite set_sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size);
};

Sprite.cpp:

#include "Sprite.h"

sf::Sprite Sprite::set_sprite(sf::Texture& tx, sf::IntRect rect, sf::Vector2f size) {

sf::Sprite spr(tx);

spr.setTextureRect(rect);

original_size =tx.getSize();

texture_size.x = static_cast<float>(original_size.x);
texture_size.y = static_cast<float>(original_size.y);

sprite_scale.x = size.x / texture_size.x;
sprite_scale.y = size.y / texture_size.y;

spr.setScale(sf::Vector2f(sprite_scale.x, sprite_scale.y));
spr.setOrigin(sf::Vector2f(original_size.x / 2, original_size.y / 2));

return spr;
}

Entity.h:

#pragma once
#include "Sprite.h"
#include "collision.h"
#include "Timer.h"

class Entity {
public:
Sprite spr;
sf::Sprite entity_sprite;
int health;
float max_speed;
sf::Vector2f speed;
sf::Vector2f direction;
float acceleration;
bool collision = false;
timer t;
float acc_time;
};

Player.h:

#pragma once
#include "Entity.h"

class Player:public Entity {
public:
Player();
float acc_time = t.accumulate_time();
void keyboard_controls();
void mouse_controls(sf::Vector2f cursor);
};

Player.cpp:

#include "Player.h"
#include <math.h>

Player::Player() {

speed = { 0,0 };
acceleration = 2;
max_speed = 500 + acceleration;
entity_sprite = spr.set_sprite(AssetManager::LoadTexture("res/wildcard.png"), { 0,0,60,63 }, { 60,63 });
}

In short, the Sprite class' constructor is replaced with a method that has the same exact role, that way I can simply declare a Sprite object with no parameters inside the Entity class, and I won't have any issues with the derived Player class since I won't be asked to create default constructors for both the Sprite and Entity classes.

C++ SFML Sprite not being drawn through classes

When you declare a member of a class as static it means no matter how many objects of the class are created, there is only ONE copy of the static member. You need to declare it as "extern".
extern sf::Texture PlayerTexture

How to use SFML Textures as static data members?

sf::Texture Lovedek::texture.loadFromFile("bullet_graphics.png",sf::IntRect(0, 0, 2, 10));

This line has some syntax errors in itself, but it is not loading the texture in the right place anyways.

First you need to initialize a static class variable somewhere, usually in the cpp file for that class.

// In your header for Lovedek
class Lovedek
{
...
static sf::Texture texture; // Declare the static texture
...
};

// In your cpp file for Lovedek

sf::Texture Lovedek::texture; // Initialize the static texture

Then think about when you want to load the texture, probably near the beginning of your main function, or in some sort of setup function right?

You can either make Lovedek::texture public and load the texture outside the class, or you can keep it private and implement something like a LoadAssets() static method to do that.

For the public approach:

// Make Lovedek::texture a public class member

// Then put this somewhere in your game's setup (before starting the game loop)

Lovedek::texture.loadFromFile("bullet_graphics.png", sf::IntRect(0, 0, 2, 10));

For the private approach:

// Change your Lovedek class
class Lovedek
{
static sf::Texture texture;
public:
static void LoadAssets()
{
texture.loadFromFile("bullet_graphics.png", sf::IntRect(0, 0, 2, 10));
}
}

// Then put this somewhere in your game's setup (before starting the game loop)

Lovedek::LoadAssets();

C++ having trouble returning sf::Texture

sf::Sprite render::loadSpri(sf::Texture temptex)
{
sf::Sprite tempspri;

tempspri.setTexture(temptex);

return tempspri;
}

It's very related to sf::Texture as class member doesn't work?. Have a look at my answer there too.

What happens is that your sprite is set to display the temptex but this texture is destroyed when the function returns since it is passed by value to the function.

You can fix this by using reference to sf::Texture instead.


More details regarding your comment below. I simplify a little bit your situation and code to highlight what is really important to understand.

Generating a texture with a loadTexture() like the following one is fine since only the returned copy is accessible to the user – he cannot set a sprite to use the original texture hence he won't suffer from the white square syndrome.

sf::Texture loadTexture(std::string name)
{
sf::Texture ret;
if (!ret.loadFromFile(name)) { doSomethingAboutIt(); }
return ret;
}

Regarding the loadSprite() function, you can use a const ref to sf::Texture as follow to prevent any copy and thus the white square problem.

sf::Sprite loadSprite(sf::Texture const& tex)
{
sf::Sprite ret(tex);
return ret;
}

But there is still one pitfall: you have to be careful how you store the texture returned by loadTexture()! The life time of this texture should be as long as the life time of your sprite. Your code above (and below for reference) should work then:

void render::start()
{
_mainWindow.create(sf::VideoMode(1024, 768, 32), "A New Life");

textures[1].tex = loadTexture("civilian.png");
textures[1].spri = loadSprite(textures[1].tex);
GameLoop();
}


Related Topics



Leave a reply



Submit