I'm making a game using SFML in C++. I wanted to separate creation of sprites into a separate function to reduce cluttering in the run function(the function containing the game loop).
I've made a struct called AssetHolder to hold various resources like textures, sounds etc in the form of std::map<std::string, resource_type>
. Consider the code snippets below.
assetHolder.h
#include<SFML/Graphics.hpp>
#include<map>
#include<string>
struct AssetHolder {
std::map<std::string, sf::Texture*> textures;
//Other resources I may add in future.
};
menuScene.cpp:
#include"menuScene.h"
namespace menuScene {
std::map<std::string, sf::Sprite> sprites;
void load(AssetHolder &assets) {
sprites["background"].setTexture(*assets.textures["menuBackgroundTex"]);
}
void render(sf::RenderWindow &window) {
window.clear(sf::Color::Magenta);
window.draw(sprites["background"]);
window.display();
}
Scene run(sf::RenderWindow &window) {
while(true) {
sf::Event event;
while(window.pollEvent(event)) {
if(event.type == sf::Event::Closed) {
return Scene::Null;
}
}
render(window);
}
}
}
game.cpp
#include"game.h"
namespace game {
sf::RenderWindow window;
AssetHolder assets;
Scene currentScene = Scene::Menu;
void init() {
window.create(sf::VideoMode(640, 360), "Platformer");
window.setFramerateLimit(60);
sf::Texture menuBackgroundTex;
menuBackgroundTex.loadFromFile("assets/images/menuBackgroundTex.png");
menuBackgroundTex.setRepeated(true);
assets.textures["menuBackgroundTex"] = &menuBackgroundTex;
menuScene::load(assets);
}
void loop() {
while(window.isOpen()) {
switch(currentScene) {
case Scene::Menu: {
currentScene = menuScene::run(window);
break;
}
case Scene::Play: {
currentScene = playScene::run(window);
break;
}
case Scene::Exit: {
currentScene = exitScene::run(window);
break;
}
case Scene::Null: {
window.close();
break;
}
}
}
}
}
In my main function, I simply call game::init()
and game::loop()
.
But when I run this code, it doesn't work. The program doesn't crash. It just displays a white rectangle in place of the sprite. I guess it is due to the fact that when the load function is over, the data is deleted.
How can I do this correctly then?
PS: If you wonder what is Scene and why I am returning it; Scene is an enum denoting the possible scenes/gamestates. The run function in a scene returns the next scene.
In the game.cpp file I was creating the sf::Texture object on the stack. I created a local variable and was using it's address. So, as soon as the function got over, it took away the data too due to it being on the stack.
I tried using the new keyword to allocate data on the heap & it worked. As the data was allocated on the heap, it remained accessible after the function got over.
PS: A better solution than using the plain new keyword is using it with std::unique_ptr. Unlike a pointer obtained via the new keyword, it doesn't have to be explicitly deleted. So, it prevents accidental memory leaks.