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.
Related
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.
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.
I'm trying to make a videogame with C++ in Cocos2d-x but I have an issue with PhysicContact.
In my gamescene I have a contact listener that check for collisions with the character and objects' PhysicBody of class Item. It all works fine, but now I want to recognize what object has collided because I want to call a method of the object's class called getThrow().
class Item : public cocos2d::Layer
{
public:
Sprite* itemArt;
int itemType;
PhysicsBody* itemCollider;
void createArt(int type);
void getThrow();
Item(int type);
};
I have tried it with the PhysicContact information, first I obtain the object PhysicBody and then its Node, but with this I just obtain the object's Sprite and at this point I don't know how to reach the object to call his method.
bool Level0::onContactBegin(PhysicsContact &contact)
{
auto bodyA = contact.getShapeA()->getBody()->getNode();
auto bodyB = contact.getShapeB()->getBody()->getNode();
//HERE IS WHERE I WANT TO RUN bodyB->getThrow()
return true;
}
I also have tried with getUserData() and getUserObject() but I don't know how to call a method with an object pointer.
I don't know if I'm trying something incorrectly, but I would appreciate your answers.
Thanks in advance! (If more details are needed just tell me, I'm new at this big community)
You need to do this:
((Item*) b->getNode())->getThrow();
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.
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.