Hi guys,
I’m having the seemingly age-old problem of an instance of sf::Sprite losing its texture. Now, before you shout at me, I
have done my research. I’ve read the sf::Sprite documentation and the tutorial. I’ve spent the last 3 days (on and off, obviously) reading various forum posts – both here and elsewhere – about this and I know what’s causing it. The problem I’ve got is most of the solutions simply say “use a resource manager” or “pass a pointer to the texture”. Okay great, I get it… but none of them seems to say
how to do either of those things or provide any examples (or at least none that work for me).
For background I decided it would be fun to try to implement the ‘Boids’ flocking algorithm using C++ and SFML. I was wrong – since hitting this problem it’s been anything but fun!! I created a Boid class and a GameEngine class (the latter was hacked together from a tutorial I found).
Here’s my Boid class:
// Class for objects that move with A-Life boid-type behavior
class Boid
{
public:
// default constructor
Boid();
// overloaded constructor (creates a Boid of type NORMAL with the specified intial position)
Boid(float StartX, float StartY);
enum Type
{
NORMAL = 0,
PREDATOR,
PREY
};
bool IsPerching;
sf::Vector2f GetVelocity() const;
sf::Vector2f GetPosition() const;
sf::Sprite GetSprite() const;
void SetVelocity(sf::Vector2f NewVelocity);
void SetPosition(sf::Vector2f NewPosition);
void SetTexture(sf::Texture TextureImage);
int FindDistanceFromNeighbour(Boid b);
void Update(sf::Vector2f NewPosition);
private:
float CurrentSpeed;
float MAX_SPEED;
sf::Vector2f Velocity;
sf::Vector2f Position;
sf::Sprite BoidSprite;
//sf::Texture BoidTexture;
};
Boid::Boid()
{
// the type of Boid
Type BoidType = NORMAL;
// the Boid's starting position
Position.x = 0;
Position.y = 0;
// is the Boid perching?
IsPerching = false;
// How fast is the Boid travelling
CurrentSpeed = .0f;
// What is the Boid's maximum speed?
MAX_SPEED = 1.f;
// THIS HAS BEEN COMMENTED OUT AS I NO LONGER DO THIS (it didn’t work anyway)
// associate a texture with the sprite
//BoidTexture.loadFromFile("boid_small.png");
//BoidSprite.setTexture(BoidTexture, true);
// set the initial position of the sprite
BoidSprite.setPosition(Position);
}
Boid::Boid(float StartX, float StartY)
{
// the Boid's starting position
Position.x = StartX;
Position.y = StartY;
// is the Boid perching?
IsPerching = false;
// How fast is the Boid travelling
CurrentSpeed = .0f;
// What is the Boid's maximum speed?
MAX_SPEED = 1.f;
// set the initial position of the sprite
BoidSprite.setPosition(Position);
}
sf::Vector2f Boid::GetVelocity() const
{
return Velocity;
}
sf::Vector2f Boid::GetPosition() const
{
return Position;
}
sf::Sprite Boid::GetSprite() const
{
return BoidSprite;
}
void Boid::SetPosition(sf::Vector2f NewPosition)
{
Position.x = NewPosition.x;
Position.y = NewPosition.y;
return;
}
void Boid::SetTexture(sf::Texture TextureImage)
{
BoidSprite.setTexture(TextureImage, true);
}
void Boid::SetVelocity(sf::Vector2f NewVelocity)
{
Velocity.x = NewVelocity.x;
Velocity.y = NewVelocity.y;
return;
}
void Boid::Update(sf::Vector2f NewPosition)
{
// move the sprite to its new position
BoidSprite.setPosition(NewPosition);
}
int Boid::FindDistanceFromNeighbour(Boid b) // TODO write a function to calculate distance from neighbouring Boid(s)
{
return 0;
}
And here’s the header file plus what I think are the relevant sections of my GameEngine class (I’m trying to keep the code as brief and relevant as possible but if you think more or less is needed let me know and I’ll edit the post as necessary).
class GameEngine
{
public:
GameEngine();
void Start();
private:
// an SFML RenderWindow
sf::RenderWindow Window;
// declare a Sprite and Texture for he background
sf::Sprite BackgroundSprite;
sf::Texture BackgroundTexture;
void Input();
void Update(float DTimeAsSeconds);
void Draw();
};
void Initialise_Flock();
void Initialise_Positions();
void Move_All_Boids_To_New_Positions();
// our 3 main rules
sf::Vector2f Rule1(Boid b);
sf::Vector2f Rule2(Boid b);
sf::Vector2f Rule3(Boid b);
// additional rules
sf::Vector2f Tend_Toward_Place(Boid b);
sf::Vector2f Bound_Position(Boid b);
void Limit_Velocity(Boid b);
int MAX_FLOCK_SIZE = 20;
Boid* Flock = new Boid[MAX_FLOCK_SIZE]; // TODO change to std::vector
// declare an instance of image_manager
image_manager ImageManager;
...
void GameEngine::Start()
{
// TEMP This stuff goes here???
Initialise_Flock();
Initialise_Positions();
// timing
sf::Clock EngineClock;
while (Window.isOpen())
{
// restart the clock and save the elapsed time in dt (delta time)
sf::Time dt = EngineClock.restart();
// make a fraction from the delta time
float dtAsSeconds = dt.asSeconds();
Input();
Update(dtAsSeconds);
Draw();
}
return;
}
...
void GameEngine::Update(float DTimeAsSeconds)
{
Move_All_Boids_To_New_Positions();
return;
}
void GameEngine::Draw()
{
// rub out the last frame
Window.clear(sf::Color::White);
// draw the background
Window.draw(BackgroundSprite);
// draw our Boids
for (int i = 0; i < MAX_FLOCK_SIZE; i++)
{
Window.draw(Flock[i].GetSprite());
}
// show everything we have just drawn
Window.display();
}
...
void Move_All_Boids_To_New_Positions()
{
// This is where our Boid Algorithm rules will be applied
// TEMP for testing...
sf::Vector2f tempPosUpdate;
tempPosUpdate.x = 0.25;
tempPosUpdate.y = 0.25;
sf::Vector2f currentPos;
for (int i = 0; i < MAX_FLOCK_SIZE; i++)
{
currentPos = Flock[i].GetPosition();
currentPos = Add_Vec(currentPos, tempPosUpdate);
Flock[i].SetPosition(currentPos);
Flock[i].Update(currentPos);
}
}
In Main I just instantiate GameEngine, which runs until either the window is closed or the escape key is pressed. All the ‘action’ happens inside GameEngine. This is probably the wrong way to do things but I’m new to OOP and it confuses the hell out of me. Still, white rectangle problem aside, it works so I’ll stick with it for now and re-write or re-factor as I learn.
I’m using
this image manager to try to load the texture and keep it in scope. I’m instantiating image_manager globally in my GameEngine.cpp file so it should be available to everything inside there… right? (Otherwise how else am I supposed to keep the texture in scope?)
I’ve tried passing
ImageManager.get_image( "boid_small.png") to the Boid’s
SetTexture(sf::Texture TextureImage) in various different places – for example as I’m adding Boids to the Flock array in
Initialise_Flock(), or just before
Window.draw(Flock[].GetSprite()); in
GameEngine::Draw().
Nothing works! Argh!!
I can’t add sf::Texture as a member variable of Boid and set the texture in the constructor because ImageManager doesn’t exist outside of GameEngine so the constructor doesn’t know what it is… this is why OOP
really confuses me. I’m used to modular programming where I could just put ImageManager in a global module and make it available to anything that needed it anywhere in the program.
Please help me understand what I’m doing wrong. As I said above everything else works for now – the ‘Flock’ is initialised and all the boids move as per
Move_All_Boids_To_New_Positions() – it’s just that they are all represented by white rectangles instead of having textures!
Thank you in advance!