Programming and other useless stuff: Basis of templates and componenttemplates finished.

Sunday, August 10, 2008

Basis of templates and componenttemplates finished.

After a day "off" at my futre mother-in-law's home, and my son getting sick this night, I managed to find some time to work on the template and component template implementation.

While there might still remain some polishing (I don't yet like the embedding of the factory functions for the templates within my entity manager), the basis seems to be finished. This means that I can load templates and refer to component templates. The code itself is straight forward since I relied on some classes I already used some years ago.

First of all, I decided to use XML. For the XML loading I embedded tinyXML into my source code. tinyXML usage is easy and since I already use it for the SSCXML lib, I didn't see any reason to switch to another reader.

Second I use some classes I created some years ago: Parameter, ParameterList and Parameterized. Basically those 3 classes allow embedding of parameters of any depth to an object. The interfaces are slim and it was easy to alter the loading code to use tinyXML. Here's a preview of the interfaces:


class Parameter : public Parameterized
{
public:
// ---------------
// Xtructors.
Parameter();
Parameter( String &_rsName, const String &_rsValue );
Parameter( const Parameter *_pParam );
virtual ~Parameter();

// ---------------
// Data access.
void setName(const String &_rsName);
String getName() const;
String getValue() const;
void setValue(const String &_rsValue);
void setValue(const char* _pValue);

// ---------------
// I/O
virtual bool load( const TiXmlElement *_pElement );
virtual bool save( TiXmlElement *_pElement );

Parameter& operator =(const Parameter& _pParam);

private:
// ---------------
// Data.
String m_sName;
String m_sValue;
};

class Parameterized
{
public:
// ---------------
// Xtructors.
Parameterized();
Parameterized(const Parameterized *_pElement );
virtual ~Parameterized();

// ---------------
// Parameter access.
ParameterList* getParamList() const;
void clear();
void createParamList();

// ---------------
// I/O
virtual bool load( const TiXmlElement *_pElement );
virtual bool save( TiXmlElement *_pElement );

Parameterized& operator =(const Parameterized& _pParameterized);

protected:
// ---------------
// Data.
ParameterList* m_pParamList;
};

class ParameterList
{
public:
// ---------------
// Xtructors.
ParameterList();
ParameterList(const ParameterList *_pList );
virtual ~ParameterList();

// ---------------
// Parameter management.
void addParam( const Parameter* _pElement );
void removeParam( Parameter* _pElement, bool _bDelete = false );
void clear();
unlong getNrParams() const;
Parameter* getParam( unlong _uIndex ) const;
bool findParam( const String &_rsName, Parameter **_rpParam ) const;

ParameterList& operator =(const ParameterList& _pParamList);

private:
// ---------------
// Data.
TDynArray m_apParams;
};

As you can see, the interfaces are quite clear.

Now, I have three base classes: Template, EntityTemplate and ComponentTemplate. While EntityTemplate and ComponentTemplate are derivations of Template, I felt it was necessary to make a difference between a simple templates, an entity template and templates for the components.


class Template
{
public:
// ----------------------------
// Xtructors.
Template() {}
Template(Sidema::String _type, Sidema::String _name) : m_type(_type), m_name(_name) {}
Template(const Template &_temp) : m_type(_temp.m_type), m_name(_temp.m_name) {}
Template(const Template *_temp) : m_type(_temp->m_type), m_name(_temp->m_name) {}
virtual ~Template() {}

// ----------------------------
// Identify the template.
Sidema::String getType() const { return m_type; }
void setType(Sidema::String _type) { m_type = _type; }

Sidema::String getName() const { return m_name; }
void setName(Sidema::String _name) { m_name = _name; }

// ---------------
// I/O
virtual bool load( const TiXmlElement *_pElement ) { return true; }
virtual bool save( TiXmlElement *_pElement ) { return true; }

private:
Sidema::String m_type;
Sidema::String m_name;
};

// An entity template holds the basic components of an entity.
// This can be all elements that are placed within the gaming world, from
// trees and bushes to the NPCs.
class EntityTemplate : public Template
{
public:
static Sidema::String TEMPLATE_NAME;

// ----------------------------
// Creator/Destroyer
static Template* create(Sidema::String _name);
static void destroy(Template *_template);

// ----------------------------
// I/O
bool load( const TiXmlElement *_pElement );
bool save( TiXmlElement *_pElement );

// ----------------------------
// An entity has components that define its "look".
// Components are ie. health, inventory, experience points, gfx, sound, ...
bool addComponent(ComponentTemplate *_component);
void removeComponent(ComponentTemplate *_component);

protected:
// ----------------------------
// Xtructors.
EntityTemplate();
EntityTemplate(const Sidema::String &_name);
virtual ~EntityTemplate();

private:
// Components of this entity.
typedef std::list COMPONENTARRAY;
COMPONENTARRAY m_components;
};

// A component template contains only basic information about the component itself.
// Basically, it holds the initial values. Templates cannot be "updated".
//
class ComponentTemplate : public Template, public Sidema::Parameterized, public Sidema::IReferenceCounted
{
public:
// ----------------------------
// Xtructors.
virtual ~ComponentTemplate();

// ----------------------------
// I/O
virtual bool load( const TiXmlElement *_pElement );
virtual bool save( TiXmlElement *_pElement );

// ----------------------------
// Owner access.
Template *getOwner() const;
void setOwner(Template *_owner);

// ----------------------------
// Reference counted interface.
unlong addRef(void);
unlong release(void);

protected:
// ----------------------------
// Xtructors.
ComponentTemplate(Sidema::String _type, Sidema::String _name);

private:
unlong m_refCount;
Template *m_owner;
};


IReferenceCounted is a reference counter interface. Once the reference drops to 0 (due to release) the object is deleted.

The loading code is easy, too:

// Load the templates.
const TiXmlNode *node = root->FirstChild("template");
while ( node )
{
if ( TiXmlNode::ELEMENT == node->Type() )
{
const TiXmlElement *element = static_cast(node);

// Read the name and the type.
const char* tempName = element->Attribute("name");
const char* tempType = element->Attribute("type");
if ( NULL != tempType && NULL != tempName )
{
Template *theTemplate = createTemplate( tempType, tempName );
if ( NULL == theTemplate )
break;

if ( theTemplate->load(element) )
{
if (!addTemplate(tempName, theTemplate))
{
destroyTemplate( theTemplate );
break;
}
}
}
}
node = node->NextSibling("template");
}

You might have remarked the createTemplate function. Basically this function looks like this:

// Create a template of a given type.
Template*
EntityManager::createTemplate( const Sidema::String &_type, const Sidema::String &_name )
{
TEMPLATECREATORS::iterator it = m_templateCreateFunctions.find(_type);
if ( it != m_templateCreateFunctions.end() )
{
return it->second(_name);
}

return NULL;
}


The different templates are registered on class construction:

EntityManager::EntityManager()
{
registerTemplate(EntityTemplate::TEMPLATE_NAME, &EntityTemplate::create, &EntityTemplate::destroy );
registerComponentTemplate(HealthComponentTemplate::TEMPLATE_NAME, &HealthComponentTemplate::create, &HealthComponentTemplate::destroy);
}

Ok... this is what is basically needed for the templates of the entities and components. I still have to implement the inheritance for the entity templates. But that's quite easy to do, too.

An XML to create an entity templates looks like this:







Ah yes.. I forgot :) The loading code for the health component template looks like this:

bool
HealthComponentTemplate::load( const TiXmlElement *_pElement )
{
if ( ComponentTemplate::load(_pElement) )
{
if ( !getParamList() )
return false;

// Make sure the template vars have been loaded.
if ( getParamList()->findParam( "maxhealth", NULL) )
return true;
}

return false;
}

EDIT NOTE: I had to change the param tag to prm in order to display the xml and the rest of the text correctly. I didn't see this problem with IE. I encountered this with FF3.0.

The ComponentTemplate::load function just contains "return Parameterized::load(_pElement);" and thus loads the param-tags.

"getParamList()->findParam( "maxhealth", NULL)" makes sure that the maxhealth param has been loaded (actually the line says: find the maxheath parameter but don't actually return me it's pointer... I just want to know if it exists...).

Phew... that was a lot of code in this blog today. As you can see, most things are quite simple to implement. The ComponentTemplate class allows for a very flexible parameter definitions. Any derived class "only" has to check, if the parameters it needs are available.

Entity template composition is made easy by just sticking together several component templates.

There a three next steps:

a) implement inheritance for the entity templates.
b) implement the actual classes for the Entities and Components (those which you actually can alter).
c) implement the template to instance code. I have several ideas how to approach this...

Have fun,
Stefan

Labels: , , , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home