Resources - ClanLib Game SDK

#

## Resources

Resources are a fundamental aspect of any game. The graphics in the game may consist of sprites, and the sounds in the game typically consists of wave-samples or similar. Other data used in games could be the data defining a level in a game, or the dialogue used in some scene in the game. As a matter of fact, anything except the actual game code in the game, can be viewed as resources.

We would like resources in a game to be separated from the game as much as possible. For instance, we don't want to change code because we change the filename of some sprite graphics file, and we certainly don't want to update source code, if we change the dimensions or any similar aspect of a resource either. We want to have flexible control of the resources initialization, separated from the game code. For instance we want to be able to specify whether a sample loops, or set it's volume separately from the source code. We want to have as great resource flexibility as possible while developing the game. And finally we would like our resources to be location transparent. If we have some netgame server that includes a level unknown to a joining client, we want that level to be transferred to the client game location transparently, as if the resource resided locally on that client.

To design a resource system, that provides an adequate solution to these problems, we need a level of abstraction between physical resource location/loading, and resource naming in the actual game code. This is where the resource manager in ClanLib comes in. The resource manager, CL_ResourceManager, provides this level of abstraction by presenting an uniform resource interface to ClanLib applications. The resource managers main responsibility is to parse a resource definition file, and load the resources using the proper resource loader, when requested.

A resource definition file in ClanLib is a xml file. It contains two different types of XML tags: sections and resource types. It has a form like this:

    <resources>
      <section name="Game">
        <sample name="sfx_explosion" file="explosion.wav" />
        <surface name="sur_explosion" file="explosion.png" />
        <customtype name="data" text="hello">
          <meaning_of_life value="42" />
        </customtype>
      </section>
    </resources>

The top element of a resource definition file in ClanLib starts with the <resources> tag. It can then be followed either by the special <section> tag, or by a resource type tag. Sections and resource tags only have one required attribute "name". The resource identifer for the explosion sample is "Game/sfx_explosion" in the above example.

Sections are a way to group resources. This aids readability and logical grouping for the game coder, as well as providing an efficient way to load a group of resources at the same time.

Any other element than the <section> element will be considered a resource. You can create as many custom resource element types as you want, in this example the <customtype> resource (note that "customtype" isnt a keyword, it could be any tag name). The only requirement is that it has the "name" attribute, which is used to access the resource from the resource manager. Via the CL_Resource interface, you can access the XML tree from any resource in the resource definition file.

If the above resource definition file was named resources.xml, the following code illustrates how to load and access resources in it:

    CL_ResourceManager resources("resources.xml");
    CL_Resource resource_sample = resources.get_resource("Game/sfx_explosion");
    std::string file = resource_sample.get_element().get_attribute("file");
    CL_Resource resource_data = resources.get_resource("Game/data");
    CL_DomNode meaning_of_life = resource_data.get_element().named_item("meaning_of_life");

### Resource Loading And Unloading

Built on top of the above system, the resource system can also administrate reference counted loading and unloading of resources. CL_Resource contains two functions called load() and unload(), which can be called to increase or decrease the load reference count of a resource. At the first load the resource will emit its sig_load signal, and at the last unload the sig_unload signal is emitted. This can be used to load additional shared data related to a resource.

It is possible to attach data objects to a resource. Such additional data is attached by deriving CL_ResourceData and then using the attach_data function on CL_Resource to attach it. Any other part of the application can then get the pointer to the data object by calling get_data via CL_Resource.

To allow a more automatic way of attaching data objects to resources, CL_ResourceManager provides a static global signal called sig_resource_loaded. When a resource definition file is loaded, it will emit this signal with each resource found in the definition file. By connecting slots to this signal, applications and ClanLib can attach data objects transparently at construction time of CL_ResourceManager.

### Built-In Resource Types

The resource loading system above is used by ClanLib to automatically administrate the following resource types:

- <surface> - represents a CL_Surface
- <sprite> - CL_Sprite
- <sprite_description> - CL_SpriteDescription
- <texture> - CL_Texture
- <font> - CL_Font
- <string> - CL_String
- <integer> - CL_Integer
- <boolean> - CL_Boolean
- <raw> - CL_Raw
- <sample> - CL_Sample

If you create a resource tag of any of these types, you will be able use the resource constructor in their equivalent C++ class (eg. CL_Surface for <surface>), like illustrated in the following code block:

    CL_Surface sur_explosion("Game/sur_explosion", &resources);

All the resource related constructors use the above syntax in ClanLib.

Behind the curtain, ClanLib uses the sig_resource_loaded signal to attach a "surface" resource data object to any <surface> resource type. Then when the above constructor is called, it will increase the reference count on the resource, which causes sig_load to be called on the CL_Resource object. The surface resource data object will be listening on this event, loading the shared surface data, and finally the CL_Surface constructor above will access the shared data calling get_data("surface") on the resource. When the local CL_Surface instance is destroyed, the load reference count will be decreased again, possibly unloading the shared resource data.

This means that constructing another CL_Surface using the same resource ID should only cause the load reference count increase - sharing the image data together with the other CL_Surface instance.

### Resources In Datafiles

It is possible to compile resources used in a resource definition file into a datafile. This can be useful for a various of reasons, the two most obvious being having only one file containing a large set of resource data, and compression of resource data. The datafile format of choice in ClanLib is zip - ClanLib has native zip archive support via the CL_Zip_Archive interface. To create a datafile for your resources, simply zip the resource definition file and its related resource data files into an archive and construct your resources like this:

    CL_ResourceManager resources(
    	"resources.xml",
    	new CL_Zip_Archive("resources.zip"),
    	true);

That's it. Resources are now loaded from the zip archive instead. The above code will cause it look for a resource definition file called resources.xml inside the resources.zip

There's one little detail worth paying attention to: compression. Depending on speed versus size, you may want to instruct your zipping tool to store (some or all) files in uncompressed form inside the archive. Loading from an uncompressed file is in most cases faster, but in particular if a resource needs to do random file access (seek around in a file), compressed files can produce a significant overhead.

### Using Multiple Resource Definition Files

The resource management system in ClanLib supports setting up a search tree for additional resources provided via other resource managers. Using this feature it is possible to have one central resource file with general resources, and other resource files for each level or mod for a game. The following code chunk illustrates how to set it up:

    CL_ResourceManager resources_global("global.xml");
    CL_ResourceManager resources_mod("mod.xml");
    CL_ResourceManager resources_level("level.xml");
    resources_level.add_resources(resources_mod);
    resources_level.add_resources(resources_global);

When requesting a resource from resources_level, it will first search its local resources, then from resources_mod and finally from resources_global.

Questions or comments, write to the ClanLib mailing list.