SFML texture not loading when encapsulating class member function is called from outside - c++

tl/dr:I've moved a function call from inside a class to outside a class and the function stopped working.
I've run in to the most baffeling problem in the year or so I've been working with c++. I can't find anything to explain what is happening here but to be honest I have a hard time even formulating a SEO question.
the base operation here is rather simple,
create a sf::Texture and sf::Sprite object
Load a texture to the sf::Texture object
set texture of sf::Sprite to the texture object
display the sf::Sprite
all 4 steps went fine within one function, but because my goal was to build a game engine I started encapsulating it into larger classes.
I created a GameEngine class and let it handle step 4. this went well after some corrections.
then I created a GameObject class to handle the first three steps, all I had to do then as a 'user' of the framework was create the object and tell it to render, this also worked.
Then I hit a snag when I moved the functioncall for step 2. from the constructer of the object to outside of the object.
old situation:
class GameObject
{
ObjectType d_type;
GameEngine *d_engine;
sf::Texture d_texture;
sf::Sprite d_sprite;
bool isactive;
public:
GameObject(GameEngine *engine, ObjectType type);
void addtexture(std::string textpath);
void render();
and
GameObject::GameObject(GameEngine *engine, ObjectType type)
:
d_type(type),
d_engine(engine),
d_texture(),
d_sprite(),
isactive{false}
{
addtexture("textures//MenuBackGround.png"); //<- problematic line
d_sprite.setTexture(d_texture);
}
void GameObject::addtexture(std::string textpath)
{
if(!d_texture.loadFromFile(textpath))
{
cout << "couldn't load texture in\n";
} else
{
cout << "did load texture\n";
}
}
this works and I see the texture I created apear in the window. If I now create a class Loadingscreen:
class Loading_Screen : public Base_State
{
std::vector<GameObject> d_backgrounds;
public:
Loading_Screen(GameEngine *engine);
virtual ~Loading_Screen();
with implementation:
Loading_Screen::Loading_Screen(GameEngine *engine)
{
GameObject temp(engine, ObjectType::BACKGROUND);
d_backgrounds.push_back(temp);
temp.addtexture("textures//MenuBackGround.png");
}
I only see a blackscreen. but in both cases I get the message that the texture was loaded.

Assuming you're actually rendering d_backgrounds, I think the error is here:
Loading_Screen::Loading_Screen(GameEngine *engine)
{
GameObject temp(engine, ObjectType::BACKGROUND);
d_backgrounds.push_back(temp);
temp.addtexture("textures//MenuBackGround.png");
}
You are creating a GameObject object. Then you insert a copy of it into the container, and what you're trying to addtexture later is not the same object you inserted.
Try this:
Loading_Screen::Loading_Screen(GameEngine *engine)
{
GameObject temp(engine, ObjectType::BACKGROUND);
temp.addtexture("textures//MenuBackGround.png");
d_backgrounds.push_back(temp);
}
Looking at SFML api, Texture and Sprite have both proper copy constructors, so it should be fine this way.

Related

I cant acces functions of a base class

Currently, I am learning c++ and just for fun, I wanted to code a little chess game (without an AI of course). I use visual studio community aside and SFML 2.5 as a renderer and for graphical objects. I tried to make a model called "figure" for all figures. So I have a figure class that inherits from sfml sprite (a drawable) and a pawn class f.e. that inherits from the figure. Sf:: sprite -> figure-> pawn/queen/tower etc... but for some reason, I can't use the pawn as a sprite, for example, I can't draw it with the draw function of my windowRenderer.
But the function documentation says it requires a drawable object. I get an error message that says something like: the conversation in the base class that is not accessible is not valid. Have I done something wrong or is it not possible to use a sprite like this. Here are my constructors because I think I its most likely I made an error there. I have only coded in java so far so the separation into header and implementation file is a little foreign for me also the constructor syntax is different.
figure.h:
class figure : sf::Sprite {
public:
figure(int startPosition);
void changeImage(std::string);
void dissapear();
void loadImage(std::string);
private:
sf::Image img;
};
figure.cpp:
figure::figure(int startPosition):sf::Sprite(){
}
pawn.h:
class pawn :
public figure
{
public:
pawn(int startPosition);
~pawn();
private:
void move(bool canBeat, bool isAsStart);
};
pawn.cpp:
pawn::pawn(int startPosition):figure (startPosition)
{
}
in main.cpp:
pawn pawn1(position);
sf::RenderWindow window(sf::VideoMode(sets.windowX, sets.windowY), "frame");
window.draw(pawn1);
Try this
class figure : public sf::Sprite
Inheritence for classes is private by default.

Drawing from a vector of sprites SFML

Every object in my game world has a vector of sprites that visually represent that object. My issue is that i cant seem to draw them properly on the screen:
This is the drawable object that every drawable inherits from:
class Drawable {
private:
static vector<Drawable*> sprites;
protected:
vector<sf::Texture> myTextures;
vector<sf::Sprite> mySprites;
public:
Drawable();
static vector<Drawable*> getSprites();
void draw(sf::RenderWindow&) const;
};
And its .cpp:
vector<Drawable*> Drawable::drawables;
Drawable::Drawable() {
drawables.push_back(this);
}
vector<Drawable*> Drawable::getDrawables() {
return drawables;
}
void Drawable::draw(sf::RenderWindow& window) const {
for (auto sprite : mySprites) {
window.draw(sprite);
}
}
Example of a object that inherits from drawable:
class Terrain : public Drawable {
private:
void loadSprite(string);
public:
Terrain(string);
};
and its .cpp:
Terrain::Terrain(string fileName) {
loadSprite(fileName);
}
void Terrain::loadSprite(string fileName) {
sf::Texture texture;
texture.loadFromFile(fileName);
myTextures.push_back(texture);
sf::Sprite sprite;
sprite.setTexture(texture);
mySprites.push_back(sprite);
}
In this case the terrain sprite is only a white-box during run-time. I think this is because the "texture" and "sprite" var in loadSprite gets destroyed after the method goes out of scope.
I know i could probably solve the issue by saving "texture" and "sprite" in the terrain-class (not creating them locally in a method like now). But this seems unnecessary to me, cant i store them in the vectors mySprites and myTextures instead?
I think this is because the "texture" and "sprite" var in loadSprite gets destroyed after the method goes out of scope.
You're right. sf::Sprite stores a reference to sf::Texture. loadSprite would work one-time only if you'd do sprite.setTexture(myTextures.back());. But std::vector's elements will be reallocated as you push_back. I recommend std::vector<std::shared_ptr<sf::Texture>> for simplicity.
Better yet, load all the textures at once, so that you don't have duplicate ones and use IDs to refer to them.

Variable not passing trough (maybe end of scope)

So I am trying to assign a variable trough a function. The code in the function would normally exist in the constructor but because I dont want to have to write it in the constructor for all the classes that inherite the same parent I made a function.
But the function doesnt seem to work properly or assign the vallue properly.
This is the code in the constructor:
sf::Texture holdTexture;
sf::Sprite holdSprite;
Wolf::Wolf(float speed, Player& p) :
Monster(speed, p)
{
holdTexture.loadFromFile("Assets\\Wolf.png");
sprite.setTexture(holdTexture);
sprite.setOrigin(24,22);
}
The sprite value is declared in a parent-parent-parent called Entity.h
class Entity {
public:
Entity();
sf::Sprite sprite;
public:
sf::Vector2f getEntityCoords();
protected:
void loadSprite(const std::string &filename);
};
Which has the function:
void Entity::loadSprite(const std::string &filename) {
sf::Texture holdTexture;
holdTexture.loadFromFile(filename);
sprite.setTexture(holdTexture);
}
So now my constructor looks like:
Wolf::Wolf(float speed, Player& p) :
Monster(speed, p)
{
loadSprite("Assets\\Wolf.png");
}
Wolf is derived from Monster. Monster is derived from Entity. And the call to loadSprite works.
But now the sprite just turns into a white box. As the title says, it may be an end of scope issue but I am not experienced enough with c++ to know for sure. Looking around hasnt gotten me an answer too. I hope this is not a stupid question. If you need to see more code please ask, it would be great if someone could help :)
As long as the sprite lives the sprite needs an existing texture to be present. As soon as the method ends the sf::Texture holdtexturewould be cleared from the memory and because of that it would display an white square.
I fixed it by adding an sf::Texture to the Entity class and assigning that texture to the sprite.

SFML texture displaying as a white box

I have a texture and sprite in a base class that is being extended by another class, however when drawn, the sprite displays as a white box. I know this is something to do with the sprite losing it's link to the texture object, but I'm kind of new to C++, so I'm not really sure how it happened.
Here is the code (I've removed some of the irrelevant parts to cut down the size):
Pickup.h:
#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
pickup.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);
}
}
Health.h:
#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
health.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);
}
}
(I don't know if this is part of the problem, but I might as well post it too)
I store the pickups in a vector like so:
std::vector<Pickups::Health> pickups;
A std::vector copies or moves the inserted elements, so as long as you have the default copy constructor or as long as you do not change this dirty a texture per element-style, (the elements just need to have one common texture object to actually point to, so you waste much much memory) the pointer that the sf::Sprite object holds to the texture gets invalid. To see why we need to think whats happens on insertion:
You setup a nice Pickupish object and add it to the vector which calls the copy-constructor. Lets say your nice object that you wanted to add is object A and the now added/copied object is B. Both have a sprite S and a texture T. Both textures are valid, but the problem is this: A's S points to A's T, so after copying it to B B's S points also to A's T! As I assume A is just temporary so it gets destructed, together with its texture, and there you have it, a nice white box.
You can solve this in some other dirty ways like making your own copy-constructor in Pickup like this:
Pickup::Pickup(const Pickup& other)
: position(other.position), texture(other.texture), sprite(other.sprite)
{ sprite.setTexture(texture); }
or by storing std::unique_ptr<Pickups::Health>'s and not just Pickups::Health's.
However a much better way you should use is some kind of Resourcemanager, which just stores all relevant textures, ideally one, a big tileset, because loading once but big is faster than loading multiple but small textures. You can write your own very simple manager or use some other e.g. the one from the great Thor library. To set a specific tile as texture for a Sprite just call sf::Sprite::setTextureRect.
I want to mention some additional improvements to your design. Let Pickup derive from sf::Drawable and define its pure virtual draw function, which you can make private. Thus your from Pickup deriving object doesn't need to know from any sf::RenderTarget and you can just do target.draw(myPickupObject).
There is no need to store the position, just let Pickup derive from sf::Transformable, too. You don't have to implement any functions, the only thing you need to do is applying the matrix to the sf::RenderStates object thats passed to draw.
Overall your draw function might look like this:
void Pickup::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
//'applying' transformation-matrix
states.transform *= getTransform();
target.draw(sprite, states);
}
So your Pickup has now only sf::Sprite as member and overall your header just needs to include SFML/Graphics/Sprite.hpp.
For avoid this type of problem I always declare my Texture as a pointer and deleting it in the destructor of the class. Like this your Texture will always exist whenever your object is not destroyed.
And it's always good to verify the loading of the image :
if (!texture.loadFromFile("images/pickups/health.png")){
//some error code
}
But it's not the problem here.

Circular dependency conundrums

In my C++ project, I have an Engine class, a Renderer class and an Object class.
The instance of Renderer renders instances of Object. However instances of Object add themselves or remove themselves from the list of things to render.
From a usability point of view, it should be possible to set whether an instance of Object is drawn or not from the Object, but without the circular dependency.
I have a possible solution to this problem, but I do not know if it is a good idea:
The update cycle in the game is done through an Update() function in the Engine class that calls the Update() functions for every object. When it comes to call the Update() function for each instance of Object, I could set it to check for two boolean variables in the Object class. One, for whether it should be drawn, and one for whether it is actually being drawn. This should thus allow for an instance of Object to be added or removed from the list of things to render as required.
Is it a good idea to do it this way? Is this way efficient or is there a better way without the circular dependency?
EDIT:
I have rewritten my question for greater clarity and moved the second part to a new question where it was probably more relevant and to avoid confusing things further here.
You would want Object to inherit from Rendered, and Renderer to only be aware of Rendered, not Object (See below):
#include <iostream>
#include <list>
struct Renderer;
struct Rendered
{
virtual void renderMe( Renderer& ) = 0;
protected:
//Renderer won't delete me...
virtual ~Rendered(){}
};
struct Object : Rendered
{
virtual void renderMe( Renderer& )
{
std::cout << "I'm rendered!" << std::endl;
}
};
struct Renderer
{
void add( Rendered& r )
{
renderList_.push_back( & r );
}
void remove( Rendered& r );//...use your imagination
void render()
{
for( std::list<Rendered*>::iterator i = renderList_.begin(); i != renderList_.end(); ++i )
{
(*i)->renderMe( *this );
}
}
private:
std::list<Rendered*> renderList_;
};
struct Engine
{
Renderer& renderer_;
Object myObject_;
Engine( Renderer& r )
: renderer_( r )
{
renderer_.add( myObject_ );
}
~Engine()
{
renderer_.remove( myObject_ );
}
};
int test()
{
Renderer r;
Enginer e( r );
r.render();
return 0;
}
Not sure I really understand the intention. However, i would like to revisit your original question:
Currently, in order for an instance of Object to be drawn, it needs to
add itself to the instance of Renderer's list of objects to draw. This
means it needs a pointer to the instance of Renderer, however Renderer
also needs to know about Object in order to draw it.
Why is this a problem? If you just need pointers, you can declare the class type upfront:
class Object;
class Renderer;
class Engine;
Although, even cleaner would be using an Interface class.
EDIT:
Do I understand correctly, the problem is that you want to pass Rendered instance to Object, so the object can paint itself using the renderer?
What about the Interface class then:
class Paintable
{
public:
virtual void paint(Renderer *) = 0;
};
class Object : public Paintable
{
...
}
All your paintable object will be extending from the interface. This way the Renderer class doesn't need to hold vector of Objects, but vector of pointers to Paintable. (e.g. Renderer is no longer dependent on Object)
ANSWER: to second comment
Not sure you have a choice. You need the rendering piece of code to have access to Object's internal data (color, position, etc ...) and to the Renderer handle. Your choices are have the code in Renderer accessing object's internal data. This is, I think, a bad idea, as the Renderer would need to have different code handling different objects, and also possibly accessing private members of the Object. The other way around is to have intelligent objects that can render themselves given the Renderer handle. So they can call: renderer.paintLine(...), etc. Using the Interface class is neat, as the Renderer doen't need to have any knowledge about the Objects it holds.