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
:
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
AFTER (v2 changed to 200,300)
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
How to Tell If a Class/Struct Has No Data Members
Finding the Max Value in a Map
How to Convert a Time into Epoch Time
Compiling Code Containing Dynamic Parallelism Fails
Is It Legal to Write to Std::String
Why Does Std::Vector Work with Incomplete Types in Class Definitions
Size of the Classes in Case of Virtual Inheritance
How to Overload the Conditional Operator
Is Static Init Thread-Safe with Vc2010
Safety of Casting Between Pointers of Two Identical Classes
Differencebetween If (Null == Pointer) VS If (Pointer == Null)
How to Know the Right Max Size of Vector? Max_Size()? But No
How to Print the Value of Nullptr on Screen
Can't Use Structure in Global Scope
Why Is the Data Type Needed in Pointer Declarations
What Are the Rules Governing C++ Single and Double Precision Mixed Calculations