Sophie

Sophie

distrib > Fedora > 13 > x86_64 > by-pkgid > f48941b2df41f17d4c9519d16b753438 > files > 293

ClanLib06-devel-0.6.5-16.fc13.x86_64.rpm


<HTML>
<HEAD>
<TITLE>ClanLib Game SDK: Resource API Overview</TITLE>
<STYLE TYPE="text/css"><!--
HTML BODY
{
	font-family: verdana, helvetica, sans-serif;
	font-size: 14px;
	border-style: solid;
}
H1 { font-size: 22px; }
H2 { font-size: 18px; }
H3 { font-size: 16px; }
H4 { font-size: 14px; }
P { font-size: 14px; }
LI { font-size: 14px; }
--></STYLE>
</HEAD>

<body bgcolor=white text=black>
<center>
<table width=70%>
<tr>
<td>

<center><table><tr><td>
<img
	SRC="Images/clanlib_logo_small.gif"
 alt="ClanLib logo"><br>
</td></tr></table></center>
<h1>Resource API Overview</h1>



<h2>Abstract:</h2>

<p>This document explains the fundamentals of resource management, and the 
rationale behind it. It explains how the resource system can be extended 
with new user defined resource types, and how the predefined basic ClanLib
resource types can be used to instantiate ClanLib display and surface objects.</p>

<h3>Resources in ClanLib</h3>
<p>Resources are a fundamental aspect of any game. The graphics in the game,
consists 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.</p>

<p>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 wan't 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. For instance we don't want to rebuild some resource
data file each time we make a change, and when we make a release of the game, we don't 
want to have to change anything in the game code. 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.</p>

<p>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, <a href="../Reference/html/CL_ResourceManager.html">CL_ResourceManager</a> provides this level of abstraction, by
presenting a 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. This of course demands a little
explanation. A resource definition file (typically using a .scr extension), defines
all the resources that a given game uses. All entries in the resource definition
file are mappings from physical file locations, to resource id's. This looks something
like this:

<ul><pre><font face="Arial,Courier New,Courier">res_id = some_resource_file (type=some_resource_type);
</font></pre></ul>

<p>The above code is translated to mean, that when the game requests resource 'res_id',
the resource loaded by the resource loader associated with 'some_resource_type' is
returned.</p> 

<h3>Resource types</h3>
<p>ClanLib defines a resource, simply as something that has a resource type, and which
can be loaded and unloaded. ClanLib defines a range of built in resource types:</p>
<ul>
<li>surface</li>
<li>sample</li>
<li>font</li>
<li>string</li>
<li>integer</li>
<li>boolean</li>
<li>raw</li>
</ul>
<p>The purpose of these resource types should be self explanatory. Each resource type has
several configuration options associated with it. All options in the resource definition
file must be enterered within the parantheses following the physical resource location,
and must be separated by commas. A later part of this overview will describe the options
associated with each resource type.</p>

<h3>A resource usage example</h3>
<p>Let us look at an example resource definition file, and see how that can be translated
to resources usable by the ClanLib classes during runtime:</p>

<ul><pre><font face="Arial,Courier New,Courier">res_surface = test.tga (type=surface);
res_sample = test.wav (type=sample);
</font></pre></ul>

<p>As mentioned previously, this definition file is interfaced to via the 
<a href="../Reference/html/CL_ResourceManager.html">CL_ResourceManager</a> class. The <a href="../Reference/html/CL_ResourceManager.html">CL_ResourceManager</a>
is created using one of the following constructors:</p>

<ul><pre><font face="Arial,Courier New,Courier">CL_ResourceManager::CL_ResourceManager(
	const char *config_file,
	CL_InputSourceProvider *provider = NULL,
	bool read_directly_from_source=false,
	bool delete_inputsource_provider=false);

CL_ResourceManager::CL_ResourceManager(
	const char *file_name,
	const bool is_datafile);
</font></pre></ul>

<p>Both are variations on the same theme. Both receive as input the name of a file. If
either 'read_directly_from_source' is false, or 'is_datafile' is true, the filename
is assumed to be the filename of a datafile, containing both the resource definition
file, and the resources. If not, the filename is assumed to be the name of the resource
definition file. If the resource definition file cannot be found, or if a syntax error
is found during parsing of the resource definition file, an exception of type 
<a href="../Reference/html/CL_Error.html">CL_Error</a> is thrown.</p>

<p>The instantiated <a href="../Reference/html/CL_ResourceManager.html">CL_ResourceManager</a> class, is in most cases 
only used indirectly. Classes like <a href="../Reference/html/CL_Surface.html">CL_Surface</a> and 
<a href="../Reference/html/CL_SoundBuffer.html">CL_SoundBuffer</a> have resource load constructors, receiving as
argument a pointer to the instantiated resource manager, as well as a resource id
specifying the specific resource to be loaded as data for that given object.</p>

<p>When using resources, make sure you include <ClanLib/core.h></p>

<p>Some example code could look like this:</p>
<ul><pre><font face="Arial,Courier New,Courier">/*
	//	my_resources.scr dump:
	section Surfaces
	{
		my_surface = test.tga (type=surface);
	}
*/

CL_ResourceManager manager(
	"my_resources.scr", // Name of resource definition file
	false); // This is not read from a datafile - load resource data through normal files

CL_Surface my_surface("Surfaces/my_surface", &manager);
</font></pre></ul>
<p>Another example using the other resource manager constructor could look like this:</p>
<ul><pre><font face="Arial,Courier New,Courier">CL_ResourceManager manager(
	"my_resources.scr",             // Name of resource definition file
	CL_InputSourceProvider::create_datafile_provider(
		"my_datafile.dat"),     // The resource definition file is stored in a datafile
	true,                           // Read resource data directly from source
	true);                          // Delete inputsourceprovider upon destruction
CL_Surface my_surface("Surfaces/my_surface", &manager);
</font></pre></ul>

<h3>Resource sections</h3>
<p>As you may have noted in the previous section, my_resources.scr, contains 
a 'section Surfaces' construct. 'section' is a keyword in ClanLib 
resource definition files, and sections can be build
up, to provide better grouping of resources. Sections can be nested inside each other,
but a resource can be a part of only one section (as the resource name is qualified
with the section hierachy of which it is a part (as in "Surfaces/my_surface")). The
syntax of a resource section is as follows:</p>
<ul><pre><font face="Arial,Courier New,Courier">section MySection
{
	section MyNestedSection
	{
		my_resource = resource.tga (type=surface);
	}
}
</font></pre></ul>
<p>'my_resource' has the following fully qualified resource_id: 
"MySection/MyNestedSection/my_resource".</p>

<p>One of the advantages of grouping is that you can load all resources in a group into memory at once.
This allows your game to do all in-game loading at game start, instead of loading the resources as
they are used in the game. The following code will do that:</p>

<ul><pre><font face="Arial,Courier New,Courier">manager.load_section("MySection");
</font></pre></ul>

<p>When you load an entire section of resources, you will have to inform the resource manager when it
can unload them from memory again. All the resources in clanlib are reference counted, and if you do
not call the CL_ResourceManager::unload_section() function, your application will memory leak!</p>

<h3>Custom resources</h3>

<p>It is possible to add your own resource types to clanlib, or even extend the functionality of already
existing resource types.</p>

<p>The resource system uses an interface called CL_ResourceType to identify resource types in the resource
files, and instantiate the appropiate data objects for a resource. When a resource is being loaded, the
following happens:</p>

<ul>
<li>The resource manager parses a resource description in the resource file, and creates a CL_Resource
object containing the description.</li>
<li>The manager walks through a linked list of registered resource types, finding all resource types that
aknowledges the resource description.</li>
<li>Each resource type is asked to create and attach a resource data object (CL_ResourceData) that can
handle the resource' data.</li
<li>The CL_Resource object has some signals that are invoked when a resource is to be loaded, unloaded, or
saved to a datafile. If a resource data object is interesting in reacting on this (a surface resource data
object would load the surface into memory on a load signal), they must connect a slot to the appropiate
signal.</li>
<li>Each resource data object uses a identifying string that can be used to locate the object's pointer.
A surface data object might register itself as "surface", and a call to CL_Resource.get_data("surface")
will then return a pointer this object.</li>
</ul>

<p>This may sound a little advanced, but a small example will show how simple it can look. First
we create out data object:</p>

<ul><pre><font face="Arial,Courier New,Courier">resources.scr:
our_map= some_location.map	(
					size=100x200,
					type=map
				};



class ResourceData_Map : public CL_ResourceData
{
public:
	ResourceData_Map(CL_Resource &resource)
	: CL_ResourceData(resource), width(0), height(0), map(NULL)
	{
		// Connect to the file load signal:
		slot_load_file = resource.sig_load_file().connect(this, &ResourceData_Map::on_load_file);
	
		// Attach resource data object to the resource.
		resource.attach_data("map", this);
	}
	
	~ResourceData_Map()
	{
		delete[] map;
	}
	
	void on_load_file()
	{
		// get the size option (100x200)
		CL_ResourceOption &size_opt = get_resource().get_options().get_option("size");
		
		// get the first value of the size (100)
		width = CL_String(size_opt.get_value(0)).get_as_int();
		
		// get the second value of the size (200)
		height = CL_String(size_opt.get_value(1)).get_as_int();
		
		// allocate memory for the map
		map = new char[width*height];

		// open the file "some_location.map"
		FILE *file = fopen(get_resource().get_location().c_str(), "rb");
		fread(map, width*height, file);
		fclose(file);
	}
	
	char *map;
	int width, height;
	CL_Slot slot_load_file;
};
</font></pre></ul>

<p>The data object attaches itself to the resource using the name 'map'. We can obtain a pointer to
the object by using the CL_Resource::get_data() function: </p>

<ul><pre><font face="Arial,Courier New,Courier">// open the resource file
CL_ResourceManager resources("resources.scr", false);

// get the map resource
CL_Resource &map_resource = resources.get_resource("our_map");

// get the attached Map data from the resource
ResourceData_Map *map = (ResourceData_Map*) map_resource.get_data("map");
</font></pre></ul>

<p>Note that the above code will not produce a call to ResourceData_Map::on_load_file(). This will
first happen when a call is done to CL_Resource::load(). The same thing applies with unloading; if
you loaded an object by calling load(), you must also call unload(). The resource class reference
counts the loading, so several objects can call load(), but only the first call will actually perform
the load.</p>

<p>When the resource manager parses the resource file, it will need to know what resource types exist,
and how to attach data objects. In order to do this, the resource manager use a linked list of
CL_ResourceType objects. It walks through all the resource type objects, and then asks wether it can
attach data to the resource. If no resource type recognizes the resource, the resource manager will throw
and exception, telling the program that it has encountered an unknown resource type.</p>

<p>ClanLib contain a template class called CL_RegisterResourceType<ResourceDataClass> that allows you
to construct simple resource type objects, that just attach a single data object to the resource. Registration
of resource types require that you create an instance of CL_ResourceType. The template class is inheriated
from CL_ResourceType and is nothing more than a convience class. An example of its usage:</p>

<ul><pre><font face="Arial,Courier New,Courier">void MyApp::main(int argc, char **argv)
{
	CL_SetupCore::init();

	CL_RegisterResourceType&lt;ResourceData_Map&gt; resource_type_map("map"); // register resource type 'map'
	CL_ResourceManager manager("resources.scr", false);
	CL_Resource &map_resource = manager.get_resource("our_map");

	map_resource.load(); // calls on_load_file() on our data object
	ResourceData_Map *map = (ResourceData_Map *) map_resource.get_data("map");
	
	std::cout &lt;&lt; "width of map: " &lt;&lt; map-&gt;width &lt;&lt; std::endl;

	map_resource.unload(); // calls on_unload() on our data object
	
	CL_SetupCore::deinit();
}
</font></pre></ul>

<p>That's it! We have constructed our own custom resource.</p>

<h3>Extending existing resource types</h3>

<p>It is possible to have several resource type objects for the same resource. This is practical if you
want to extend an already existing resource type with more functionality. Each resource type object can
attach its own data to the resource object, allowing you to eg. add "animation_data" to a surface type.</p>

<p>The following is a small example of how to extend the surface type with some additional data:</p>

<ul><pre><font face="Arial,Courier New,Courier">class AnimationData;

class AnimationSurface : public CL_Surface
{
public:
	AnimationSurface(const std::string &res_id, CL_ResourceManager *resources)
	: CL_Surface(res_id, resources)
	{
		CL_Resource &resource = resources-&gt;get_resource(res_id);
		anim_data = (AnimationData *) resource.get_data("animation_data");
	}
	
	AnimationData *get_anim_data() { return anim_data; }
	
private:
	AnimationData *anim_data;
};

class AnimationData : public CL_ResourceData
{
public:
	AnimationData(CL_Resource &resource)
	{
		some_data = resource.get_option("anim_data").get_value();
		resource.attach_data(this);
	}
	
	std::string some_data;
};

class MyApplication : public CL_ClanApplication
{
public:
	virtual int main(int argc, char **argv)
	{
		CL_SetupCore::init();
		CL_SetupDisplay::init();
		CL_RegisterResourceType&lt;AnimationData&gt; restype_anim("surface");
		
		CL_Display::set_videomode(640, 480, 16, false);
		
		CL_ResourceManager resources("resources.scr", false);
		AnimationSurface surf("my_surf", &resources);
		surf.put_screen(0, 0);
		CL_Display::flip_display();
		
		std::cout &lt;&lt; "some data: " &lt;&lt; surf.get_anim_data()-&gt;some_data.c_str() &lt;&lt; std::endl;
		
		CL_SetupDisplay::deinit();
		CL_SetupCore::deinit();
	
		return 0;
	}
} app;
</font></pre></ul>

</TD>
</TR>
</TABLE>
<BR>
<BR>
<FONT COLOR="#a0a0a0">Questions or comments, write to the <a href="mailto:clanlib-user.x.dtu.dk">ClanLib mailing list</a>.</FONT>
</CENTER>
</BODY>
</HTML>