Passing class member object - SFML draw() - c++

It seems like a very weird situation. I just want to draw a sf::Text object that is handle outside the main loop (in another class).
I show you only the essential. This code works (it draws other things that are handle directly in the main) and so it compiles.
Main :
int main()
{
//we handle the creation of the window //
//...
//Example where the sf::Text is handle in the main (it works)
sf::Font font;
font.loadFromFile("arial.ttf");
sf::Text mainTxt("It works !!",font);
mainTxt.setPosition(sf::Vector2f(64,"It works !!"));
mainTxt.setCharacterSize(25);
mainTxt.setFillColor(sf::Color::Blue);
TextManager textManager(); //My class that don't work...
sf::Event event;
while (window.isOpen())
{
// We handle event (we don't care here)
// ...
window.draw(mainTxt); // it works
window.draw(textManager.getMyText()); // do nothing
window.display();
window.clear();
}
return 0;
}
TextManager header :
#ifndef TEXTMANAGER_H
#define TEXTMANAGER_H
#include <SFML/Graphics.hpp>
class TextManager
{
public:
TextManager();
virtual ~TextManager();
sf::Text getMyText();
private:
sf::Text myText;
};
#endif // TEXTMANAGER_H
TextManager cpp
#include "TextManager.h"
TextManager::TextManager()
{
sf::Font font;
font.loadFromFile("arial.ttf");
sf::Text myText("Not drawn on the screen :-(",font);
myText.setPosition(sf::Vector2f(164,0));
myText.setCharacterSize(25);
myText.setFillColor(sf::Color::Blue);
}
// in this example (that do not work) I do not change fspText. But, I want to update it at each call of the get function. So, it is not a constant class member.
sf::Text TextManager::getMyText() {
return myText;
}
TextManager::~TextManager()
{
//dtor
}
What I really do not understand, is the fact that with a custom class of mine, I can access a class member object with this type of getter. I also did some research, and I think It should return a copy of the sf::Text object.
I try lot of things, like return reference or const reference etc... I do not understand.
I hope my problem is well displayed.
Thank you for your help ;-)
And have a nice day !

This
TextManager textManager(); //My class that don't work...
is function declaration, not construction of object.
Should be:
TextManager textManager; //My class that don't work...
By
sf::Text myText("Not drawn on the screen :-(",font);
you define a local variable called myText the same as your data member. So, myText returned by getMyText is just not affected.
Read the docs before coding:
It is important to note that the sf::Text instance doesn't copy the
font that it uses, it only keeps a reference to it. Thus, a sf::Font
must not be destructed while it is used by a sf::Text (i.e. never
write a function that uses a local sf::Font instance for creating a
text).
taken from SFML reference.
class TextManager
{
public:
TextManager();
virtual ~TextManager();
sf::Text& getMyText(); // <- pass by reference
private:
sf::Font myFont;
sf::Text myText;
};
and
TextManager::TextManager()
{
myFont.loadFromFile("arial.ttf");
myText.setString("Not drawn on the screen :-(");
myText.setFont(myFont);
myText.setPosition(sf::Vector2f(164,0));
myText.setCharacterSize(25);
myText.setFillColor(sf::Color::Blue);
}
might work.

Related

Display texture and sprite on the window in class function

So this is my cpp file that has the function. I comment out some member factor as I felt like it wasn't being read at all but will include them to see if I am able to use them. My texture seems to be able to appear but no matter the size or position I try to put it it is not being shown at all. It seems to load properly as I have my if statement to check when it failed to load the texture I want to use. SO what am I doing wrong that is not appearing at all?
//sf::Texture suit::image = suit::setUpTexture();
//
//sf::Texture& suit::setUpTexture()
//{
// image.loadFromFile("Jester.png");
// sf::Sprite sprite;
// sprite.setTexture(image);
//
// return image;
//}
suit::suit()
{
sf::Texture image;
image.loadFromFile("Jester.png");
sf::Sprite sprite;
sprite.setTexture(image);
sf::Sprite::setScale(5,5);
sprite.setPosition(950,950);
if(!image.loadFromFile("Jester.png"))
{
exit(1);
}
}
My Header
class suit : public sf::Sprite
{
private:
// static sf::Texture image;
// static sf::Texture& setUpTexture();
public:
suit();
};
When linking an a texture to a sprite, you need to tell that sprite the actual reference to the one texture.
sprite.setTexture(image&);
Calling the font address by using the '&' operator should solve your problem.
You should also load your image only once by calling:
if(!image.loadFromFile("Jester.png"))
{
exit(1);
}
instead of:
image.loadFromFile("Jester.png");
I Actually don't know if you did it after relooking at your code, but do not forget to draw your actor using the draw() method.

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.

C++ OOP LNK2001 ERROR

As the title suggests, I have an OOP error in C++, the LNK2001 unresolved externals error. Here is my code, where did I go wrong?
I'm using sfml to use the graphics in VS2015.
// OOPExample.hpp
#pragma once
#include <SFML\Graphics.hpp>
#include <SFML\System.hpp>
#ifndef OOPEX_H
#define OOPEX_H
class OOPExample{
public:
static sf::CircleShape shape;
static float rot;
static void draw();
static void rotate();
};
#endif // OOPEX_H
// OOPExample.cpp
#include "OOPExample.hpp"
#include <SFML\Graphics.hpp>
void OOPExample::rotate() {
OOPExample::rot += 0.1f;
return;
};
void OOPExample::draw() {
OOPExample::shape.setFillColor(sf::Color::Red);
OOPExample::shape.setRotation(rot);
return;
};
// Source.cpp
#include <SFML/Graphics.hpp>
#include "OOPExample.hpp"
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
OOPExample oopexample();
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
window.draw(oopexample().shape);
window.display();
}
return 0;
}
From what I've seen it looks like I need a definition of the methods declared in OOPExample.hpp, but I have those exact definitions in OOPExample.cpp. Am I instantiating the class incorrectly in Source.cpp?
To your question about the link errors. The comments highlight most of them... These are not OOP errors but rather link time errors when you build your project. I don't know if you have any experience with compiled system-level languages; but it would be a good idea to learn the basics of the compile-link cycle and what is expected by the linker when putting the final program together. A simple example of how to define a static member variable follows.
// class1.h
Class1
{
public:
private:
static float rotation;
};
// class1.cpp
#include "class1.h"
int Class1::rotation = 5.0f;
Note that int Class1::rotation = 5.0f; happens once at program initialisation.
I don't know if you are following some tutorial where they are creating this class like this, but you have a worrying amount of static members. This is an OOP problem. If you want to make many objects/instances of OOPExample you need to understand what static means. In the context of a class, when you apply the static keyword to a variable it means that all OOPExample objects will share that one variable. This makes static member variables good for default values and things like the number of a given class. You could have a static int OOPExample::count; to count the number of OOPExample objects that you have made. I will put that in an example later.
There may be many reasons for link errors, particularly missing definitions. PcAF highlighted an important one in the comments to your question. But you may also be missing the SFML libraries. I vaguely remember SFML tutorials including detailed instructions on how to link their libraries in your environment. In VS that will be somewhere in your project properties. You will obviously get similar errors if you declared something in the header that is not in the implementation (usually the cpp). This was the case with your static variable, but applies to functions as well.
Now there is a lot wrong with the three files you provided. I edited them to highlight some of the problems but it is far from perfect. I would not approach it in this way because sf::CircleShape is already an object-orientated entity. It has all the things you are trying to implement already implemented. Never over-abstract a problem (I also realised at some point that we were rotating a filled circle haha). You should really follow the advice to get a good textbook and start from the ground-up. SFML is a huge library that will distract you from understanding the fundamentals of C++. OOP is but one aspect of C++ and you need to embrace all C++ fundamentals to use OOP effectively. If you do this, you will have the most powerful abstraction mechanisms available (in my opinion).
My edits follow, but really, it is just a demonstration of how deep the rabbit hole goes (it gets much worse). How to instantiate OOPExample is shown in main.
// OOPExample.h
#ifndef OOPEX_H
#define OOPEX_H
// Only include what you need to. Users of this header should be exposed to as
// little SFML as possible.
#include <SFML/Graphics/CircleShape.hpp>
class OOPExample{
public:
// Parameterless constructor.
OOPExample(); // Note, this sets the rotation to the default rotation.
// One that takes a initial rotation.
OOPExample(float initial_rotation);
// Rotate 0.1f (by default) or by user specified amount.
void rotate(float rotation = 0.1f);
// window.draw() takes a Drawable as its first argument. Fortunately,
// CircleShape is a shape which in turn is a Drawable. Notice that we
// return by constant reference. Callers cannot edit our shape but they
// get a reference to the sf::CircleShape shape instance so they can read
// it.
// const, & (i.e. reference), pointers requires a deep understanding of
// object ownership, copying by value, by reference, and now of
// particular interest in C++11, moving.
const sf::CircleShape &getShape() const;
// You forgot to declare and define this.
void setRotation(float rotation);
// Set the default rotation for all objects created with the
// parameterless constructor.
static void setDefaultRotation(float rotation);
// The destructor.
virtual ~OOPExample();
private:
sf::CircleShape shape;
// sf::CircleShape already has a rotation with getters and setters.
// Don't over abstract!
// Our setRotation, rotate functions seem a bit unneccesary.
// float rotation;
// Defaults.
static sf::CircleShape default_circle;
static float default_rotation;
// Count example.
static int count;
};
#endif // OOPEX_H
// OOPExample.cpp
// Personally, and I know with most other C++ developers, I prefer my header's
// extension to be .h. .hpp usually identifies headers with
// implementations/definitions of classes in the header file itself (necessary
// in some circumstances).
#include "OOPExample.h"
//
// How to initialise static member variables. This happens once at the
// beginning of the program.
//
// All our circles have a default radius of 5.
sf::CircleShape OOPExample::default_circle = sf::CircleShape(5);
// And rotation of 0.
float OOPExample::default_rotation = 0;
int OOPExample::count = 0;
// The definition of the parameterless constructor.
OOPExample::OOPExample()
// A class initialiser list. How we build a new object.
: shape(default_circle) // We copy the default circle.
{
// Do other stuff to construct the object if you need to. For example:
shape.setFillColor(sf::Color::Red);
setRotation(default_rotation);
count++; // We just made another OOPEXample instance.
}
// The definition of a constructor that takes an initial rotation. I just
// realised we are rotating a circle!
OOPExample::OOPExample(float initial_rotation)
: shape(default_circle) // We copy the default circle.
{
// Do other stuff to construct the object if you need to. For example:
shape.setFillColor(sf::Color::Red);
// Notice: we used the user provided argument this time.
setRotation(initial_rotation);
count++; // We just made another OOPEXample instance.
}
void OOPExample::rotate(float rotation)
{
shape.rotate(rotation);
// return; // No need to specify a return for a void.
}
const sf::CircleShape &OOPExample::getShape() const
{
return shape;
}
void OOPExample::setRotation(float rotation)
{
shape.setRotation(rotation);
}
void OOPExample::setDefaultRotation(float rotation)
{
// OOPExample scoping is unnecessary.
OOPExample::default_rotation = rotation;
}
OOPExample::~OOPExample()
{
// Do things required for cleanup, i.e. deinit.
// One OOPExample just reached the end of its lifetime. Either it
// was deleted or reached the end of the
// scope (i.e. {}) it was created in.
count--;
}
// main.cpp
#include "OOPExample.h"
#include <SFML/Graphics.hpp>
int main()
{
sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
// Call the default, parameterless, constructor to instantiate an object
// of OOPExample.
OOPExample oopexample;
// Create another with a initial rotation of your choosing.
OOPExample another_obj(0.5f);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.clear();
// The instance object of OOPexample is referred to by oopexample.
// window.draw(oopexample().shape);
// This member is now private.
//window.draw(oopexample.shape);
window.draw(oopexample.getShape());
window.display();
}
return 0;
}

Texture going out of scope in SFML

Well, I'm trying to make a menu using SFML 2.1. I have a 'something.h' header file and two source files. For the buttons, I've created a texture file. Now, since I want all the menu buttons to have the same texture, I was trying to declare the texture globally. I tried a couple of ways to do this. I tried to declare it just before all the class declarations in something.h, but I found out that you can't use texture.loadFromFile("blahblah") without a function. SO, I decided to make a class for this, and the following is the code.
Something.h
#include<SFML/Graphics.hpp>
#include<iostream>
class Textureinitialize
{
public:
sf::Texture texture;
sf::Font font;
void loadtexture(const std::string& texturestring); //Specify file-name of texture
void loadfont(const std::string& fontstring); //Specify file-name of font
};
class Menubutton
{
public:
sf::Sprite menubuttonsprite;
sf::Text menubuttontext;
sf::Vector2i spritekaposition;
void loadthesprite(Textureinitialize obj1); //Load sprite from Texture and Text from font
void loadtexturepos(sf::IntRect rect1); //Load the sprite rectangle from the Texture
void spriteposition(sf::Vector2i originpos); //Specify position of button on the screen
void texttodisplay(std::string displaystring); //String to be displayed in button
void positionoftext(sf::Vector2i textpos,int textsize); //Set position and size of text to be displayed
};
This gave no error, but the menu button sprite does not display on the screen. I think the problem is with the loading of the texture. I looked around, and found that somehow the texture is going out of scope. Please could anyone help me?
EDIT:
I guess more code is the need of the hour. Here are the contents of the two source files I mentioned.
Something.cpp
#include "Menudata.h"
void Textureinitialize::loadtexture(const std::string& texturestring)
{
if(!texture.loadFromFile(texturestring))
std::cout<<"\nFailed to load textures";
}
void Textureinitialize::loadfont(const std::string& fontstring)
{
if(!font.loadFromFile(fontstring))
std::cout<<"\nFailed to load font";
}
void Menubutton::loadthesprite(Textureinitialize obj1)
{
menubuttonsprite.setTexture(obj1.texture);
menubuttontext.setFont(obj1.font);
}
void Menubutton::spriteposition(sf::Vector2i originpos)
{
spritekaposition.x=originpos.x;
spritekaposition.y=originpos.y;
menubuttonsprite.setPosition(originpos.x,originpos.y);
}
void Menubutton::loadtexturepos(sf::IntRect rect1)
{
menubuttonsprite.setTextureRect(rect1);
}
void Menubutton::texttodisplay(std::string displaystring)
{
menubuttontext.setString(displaystring);
}
void Menubutton::positionoftext(sf::Vector2i textpos,int textsize)
{
menubuttontext.setPosition(spritekaposition.x+textpos.x,spritekaposition.y+textpos.y);
menubuttontext.setCharacterSize(textsize);
menubuttontext.setColor(sf::Color::Red);
}
Main.cpp
#include "Menudata.h"
int main()
{
Textureinitialize a;
a.loadtexture("Menubutton.tga");
a.loadfont("arial.ttf");
Menubutton b;
b.loadthesprite(a);
b.loadtexturepos(sf::IntRect(0,0,80,48));
b.spriteposition(sf::Vector2i(50,50));
b.texttodisplay("Hello!");
b.positionoftext(sf::Vector2i(50,14),20);
sf::RenderWindow window(sf::VideoMode(200,200),"My Window");
while(window.isOpen())
{
sf::Event event;
while(window.pollEvent(event))
{
if(event.type==sf::Event::Closed)
window.close();
if(event.type==sf::Event::MouseButtonPressed)
{
b.loadtexturepos(sf::IntRect(0,48,80,48));
if(event.mouseButton.button==sf::Mouse::Left)
std::cout<<"\nHello!!!";
}
if(event.type==sf::Event::MouseButtonReleased)
b.loadtexturepos(sf::IntRect(0,0,80,48));
}
window.clear(sf::Color::Green);
window.draw(b.menubuttonsprite);
window.draw(b.menubuttontext);
window.display();
}
return 0;
}
The reason I thought the texture is going out of scope was that I read somewhere on Google that if you're getting a white rectangle instead of the image, you might have the said issue. Also, I just noticed that I couldn't see any text in the white box. I do have a function which displays text on the buttons in the class 'Menubutton'. I think the problem might be in the initialization of font. I hope what I've done in the class 'Textureinitialize' isn't wrong. I guess the problem might lie there.
As far as managing SFML resources goes, e.g. textures, I can only recommend this template:
https://github.com/LaurentGomila/SFML-Game-Development-Book/tree/master/02_Resources/Include/Book
This is a, in my opionion, simple way to access the texture whenever you need it to construct your buttons
P.S.: To improve readability of your code, I suggest using notations such as my_function or myFunction, instead of myfunction (all lowercase).