The Resource Manager class design

Coordinator
Sep 24, 2013 at 5:04 AM
Edited Sep 24, 2013 at 5:26 AM
First of all, a clarification regarding the semantics of the name - the resources we're having in mind are actually the game various assets. That sets apart the class from a memory management resource class, which could be helpful in many situations, but in our memory management scheme, every object is responsible for managing its own resources. Having said that, in the vein of the Board class described earlier, lets look at the mechanics of the class
class IResourceHandler
{
    virtual void Load(std::string file_name) = 0; 
}; 
Again, in designing the components, it is essential to strive for
  • Minimalism
  • Extensibility
  • Portability across platforms, APIs and engines

Next, the primary resources we're interested in are
  • Images (managed by the ImageContainer)
  • Text and fonts (managed by the TextContainer)
  • Sounds (both music and fx tracks handled by the SoundContainer)

In fact, the sound manager can be split up into 2 classes (MusicContainer and FXContainer). Anyway, lets look at the ImageResource class.
class ImageResource 
{
public: 
    ImageResource()
        : surface(nullptr) {} 

    ~ImageResource()
    {
        SDL_FreeSurface(surface); 
    }

    ImageResource(const ImageResouce& copy); 
    ImageResource& operator=(const ImageResource& copy); 

    void Set(std::string& name_)
    {
        surface = LoadSurface(name_); 
    }

private: 
    SDL_Surface *surface; 

    friend class ImageContainer; 
};  
Notice that we do not provide move semantics and some of the others C++11 standard class
features, because this class serves (well, sort of) as a prototype. Second, the copy ctor and
assignment operator are bit tricky, but they have to be provided because the class has a pointer data member. Our other option is to just use a smart pointer, depending on what it is required (unique_ptr for non-shared resource and shared_ptr for shared resource), but this is our first take on the class. Yet another approach is to forbid image copying altogether (besides, it is an expensive operation), but our file loading scheme doesn't allow us to do so for now. Lastly, the ImageManager class can access the surface of an individual resource. Next, lets take a look at the ImageManager itself.
class ImageContainer : public IResourceHandler
{
public: 
    ImageContainer(std::string file_name) 
    {
        Load(file_name); 
    }

    virtual void Load(std::string file_name); 

private: 
    std::vector <ImageResource> image_data; 
}; 
The ImageContainer stores a vector of surfaces so that you don't have to limit yourself to a fixed number of images (as in our current image management scheme, using std::array).

All resources can be managed through a pointer to the base IResourceHandler class. In the initiali version of this class, there was a Get() method that retrieves a surface in the collection, but it is beneficial to minimize the setters and getters in our design as much as possible.

Finally, the implementation of the ImageContainer
// Utility method that reads in strings from a file (preferably, .dat)
static void Read(std::vector <std::string>& sVec, std::string file_name)
{
    std::ifstream data_file(file_name.c_str()); 

    std::copy(std::istream_iterator <std::string> data_file, 
                  std::istream_iterator <std::string>(), 
                  std::back_inserter(sVec)); 
}

void ImageContainer::Load(std::string file_name)
{
    std::vector <std::string> t_data; 

    Read(t_data, file_name); 

    // Request that image data's vector capacity 
    // is at least t_data.size() elements 
    image_data.reserve(t_data.size()); 

    for (size_t i = 0; i < t_data.size(); ++i)
        image_data[i].Set(t_data[i]); 
}
This algorithm uses auxilary vector for the strings, wasting a lot of space, so it is good to consider alternative approaches at a later stage.

P.S. I re-wrote these so that they are less ambigious (FooManager is not very descriptive).