Resource management
Another vital part of larger projects is an efficient way of managing resources. Since we're going to have several types of resources, such as textures, fonts, and sounds, it would make sense to have separate managers for all of them. It's time for a base class:
template<typename Derived, typename T> class ResourceManager{ public: ResourceManager(const std::string& l_pathsFile){ LoadPaths(l_pathsFile); } virtual ~ResourceManager(){ ... } T* GetResource(const std::string& l_id){ ... } std::string GetPath(const std::string& l_id){ ... } bool RequireResource(const std::string& l_id){ ... } bool ReleaseResource(const std::string& l_id){ ... } void PurgeResources(){ ... } protected: bool Load(T* l_resource, const std::string& l_path) { return static_cast<Derived*>(this)->Load(l_resource, l_path); } private: ... };
The idea behind this particular resource management system is certain segments of code requiring and later releasing a certain resource identifier. The first time a resource is required it will be loaded into memory and kept there. Every time it's required after that will simply increment an integer that gets stored with it. The integer represents how many instances of code rely on this resource being loaded. Once they are done using the resource, it begins being released, which brings the counter down each time. When it reaches zero, the resource is removed from memory.
It's fair to point out that our resource manager base class utilizes the Curiously Recurring Template Pattern for setting up the resource instances after they're created. As manager classes don't really need to be stored together in the same container anywhere, static polymorphism makes a lot more sense than using virtual methods. Since textures, fonts, and sounds may be loaded in different ways, each subsequent manager must implement their own version of the Load
method, like so:
class TextureManager : public ResourceManager<TextureManager, sf::Texture> { public: TextureManager() : ResourceManager("textures.cfg"){} bool Load(sf::Texture* l_resource, const std::string& l_path){ return l_resource->loadFromFile( Utils::GetWorkingDirectory() + l_path); } };
Each single manager also has its own file, listing the relationships between names of resources and their paths. For textures, it can look something like this:
Intro media/Textures/intro.png PlayerSprite media/Textures/PlayerSheet.png ...
It simply avoids the need to pass around paths and filenames, by instead relating a name to each resource.