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).
Related
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.
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.
I wanted to draw sprites on screen having only information about it in my struct and next draw text on it
Information are good
X and Y scale=1
Path is good and point to graphic
position and rotation =0;
int free have a good number
But Text works
so i don't know why Sprite don't
I tried commenting code with drawing and making text don't work
struct ObjectInfo
{
float Xpoz,Ypoz;
std::string TexPath;
float Xscale,Yscale;
float Rotation;
};
ObjectInfo OI[1000];
int free;
void Draw()
{
for(int i=0;i<free;i++)
{
sf::Texture t;
t.loadFromFile(OI[i].TexPath);
sf::Sprite s;
s.setTexture(t);
s.setPosition(OI[i].Xpoz,OI[i].Ypoz);
s.setScale(OI[i].Xpoz,OI[i].Ypoz);
s.setRotation(OI[i].Rotation);
okno.draw(s);
sf::Text text;
text.setFont(font);
text.setCharacterSize(48);
text.setColor(sf::Color::Black);
text.setPosition(s.getPosition());
text.setString(IntToString(i));
okno.draw(text);
}
}
I expected that sprites and Text display
but only text display
When you call s.setTexture(t) sprite s remembers texture t by pointer/reference. So, when your code exit from for loop sf::Texture t is destroyed (C++ destroy scope variables when exiting some scope) and pointer/reference in sprite class points to deleted memory locations, which cause SFML to have error while drawing your sprites. The solution to this problem is the global array for textures which you use. I also recommend adding the global array for sf::Sprites, because it makes your code safer. Here is how it may be implemented:
//In the global scope
sf::Texture textures[1000];
sf::Sprite sprites[1000];
//void draw() new for loop
for(int i=0;i<free;i++)
{
textures[i].loadFromFile(OI[i].texPath);
sprites[i].setTexture(s);
//Set other parameters
okno.draw(s);
sf::Text text;
text.setFont(font);
text.setCharacterSize(48);
text.setColor(sf::Color::Black);
text.setPosition(s.getPosition());
text.setString(IntToString(i));
okno.draw(text);
}
By the way, there are some more improvements to your code. Texture loading operation is a heavy operation, so if paths to textures are immutable, I will recommend you adding some method for loading them at once. Also, creating text class may be a heavy operation, so it is good to add global array of sf::Text classes you use
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.
I am trying to make a simple function or class that selects an image and returns it or passes it in some way to a different class. Is it as simple as knowing what type the Image is considered? or do I need to do something else? I am running Code::Blocks 10.05 with GNU GCC compiler on a windows 8 computer. Any help is appreciated.
Thanks to Aesthete, I made some progress. Now I have this:
class Background{
sf::Image BGI;
sf::Sprite BG;
Image& img;
public:
void rimage(std::string name){
sf::Image extra;
extra.LoadFromFile(name);
img = extra;
}
void init(std::string name){
BGI = img
BG.SetPosition(0.f,0.f);
BG.SetImage(BGI);
}
};
But when I run it, I get this:
...4 error: ISO C++ forbids declaration of 'Image" with no type
Also,
...10 error: 'img' is defined in this scope
I have included the libraries that I need to run SFML, I just left it out to keep things clean, I adjusted the lines the errors above occurred on to make it easier to follow.
Isn't img now sort of a global variable within Background?
and I thought Image& was the type of img... What needs to change here?
You don't need a load method, nor any extra Image objects. You can do all this processing in the constructor.
class Background{
private:
// You only need an image and a background, if that.
sf::Image BGI;
sf::Sprite BG;
public:
// Use a constructor.
Background(std::string name)
{
SetBackground(name, Vector2f(0.f, 0.f));
}
void SetBackground(std::string name, sf::Vector2f pos)
{
BGI.LoadFromFile(name);
BG.SetImage(BGI);
BG.SetPosition(pos);
}
};
// Constructor loads image, sets image to sprite, and set sprite position.
Background bg("MyBackground.png");
// You can change the background image an position like so.
bg.SetBackgrond("newImage.png", Vector2f(10.f, 20.f));