Component-based Entities using Properties

Started by
23 comments, last by owl 14 years, 4 months ago

Component-based Entities using Properties

Introduction This is my second public design for using compoents with entities to prevent deep hierarchy problems and make the game object difinition more data-driven. By data-driven, I mean that you can define your game objects in for example XML. Third party dependencies - ClanLib (http://www.clanlib.org) Some history Over the last couple years I've been doing a lot of thinking on component-based entities. I really liked the idea of approaching the definition of entities this way. It chuncks up logic in the game nicely, makes it easy to write small scripted chuncks of logic, it's data-driven to the point where you could define entierly new game objects in for example XML. The last couple of books from the Game Programming Gems series have covered this topic with different approaches, but the approach I'm suggesting here have not been discussed before to my knowledge; -using properties to add the glue between components! During my time designing these component-based entity systems, I discovered that most of the time, if not all the time, when one component needed to communicate with another component, this was either to change some data, or that someone else had to change some data. There was a problem here, because often you'd find that a component owning a specific data, (like, you'd want the movement component, to own the data for positioning your entities) had to inform other components about changing data, or change the state of data on other components' request. Like with the MovementComponent example, it turns out a ton of components would like to know about the Position of their entity. Your dependency chain between components starts to weave and wrap around your logic, the more components depend on this common data that one component owns, the messier it gets, until you're left with an unmanagable pile of garbage code of inter-dependencies! This raised the idea one day, to let the entities own all the data! Let components add data to their parent entity, let them change it, and let all components in an entity have the option to react to data changing! This approach allowed components to be written totaly unaware of any other components in the mix. It only cares for it's entity and the properties it want to manipulate with it's logic. The current version The current version of the component-based entity system using properties, does not contain any specialized components (like those that can be defined from script) or any system for defining game objects in XML. Those systems should be fairly simple to extend the engine with however, and could (and should) be written externally from the EntityEngine library. The source-code http://svn2.xp-dev.com/svn/Trefall-ComponentSystem/Source HOW TO/ FAQ 1)What is a game object? - A Game Engine side object that inherits from Entity in Entity engine, to wrap the Entity engine functionality, and add additional Game Engine specific functionality to the Game Engine's Game Objects. 2)What is an Entity? - A wrapper of ComponentContainer and PropertyContainer. 3)What is a ComponentContainer? - Holds a map of components, and robust/secure methods for adding and manipulating components. 4)What is a PropertyContainer? - Holds a map of properties, and robust/secure methods for adding/removing and manipulating properties. 5)What is a Property? - Entity Engine's secure variable for data storage, including a smart pointer. It's owned by it's ComponentContainer/Entity. It serves as glue between components, as components can listen for an altered property and act upon such changes. Two components can work on the same property (as Entity is the owner). This allows multiple components to work on the Position property of an Entity for instance, without them ever knowing about each other. 6)What is a Component? - Entity Engine's modular chuncks of logic for entities. Instead of using inheritance to define new entity types, a modular approach is used. This seperates logic cleanly, makes extending objects simple and the definition of a new game object data driven. A new game object definition, is simply about defining which components it holds. 7)How to register new components with the entity engine?
entityManager.GetComponentFactory().RegisterComponent(ExamineComponent::GetType(), &ExamineComponent::Create);
entityManager.GetComponentFactory().RegisterComponent(MovementComponent::GetType(), &MovementComponent::Create);


8)How to attach a component to a game object? - Some initialization method for a zone:
GameObject *objectRobot = new GameObject(entityManager, zone);
objectRobot->AddComponent("Examine");
objectRobot->AddComponent("Movement");
objectRobot->GetProperty<CL_String>("Description") = "It's a useless robot!";
objectRobot->GetProperty<float>("MovementSpeed") = 50.0f;


9)How to write a new component from scratch - ExamineComponent.h ---------------------------
#pragma once

#include <EntityEngine/Component.h>
#include <EntityEngine/Property.h>

namespace EntityEngine { class Entity; }

class ServerPlayer;

class ExamineComponent : public EntityEngine::Component
{
public:
	ExamineComponent(EntityEngine::Entity* entity, const CL_String &name);
	virtual ~ExamineComponent() {}

	virtual void RequestCommands(std::vector<CL_String> &requestCommands, ServerPlayer *player);
	virtual void ExecuteCommand(const CL_String &command, ServerPlayer *player);

	static CL_String GetType() { return "Examine"; }
	static EntityEngine::Component* Create(EntityEngine::Entity* entity, const CL_String &name) { return new ExamineComponent(entity, name); }

protected:
	EntityEngine::Property<CL_String> description;

	void OnExamine(ServerPlayer *player);
};


---------------------------- - ExamineComponent.cpp ----------------------------
#include "precomp.h"
#include "ExamineComponent.h"
#include "../GameObject.h"
#include "../ServerPlayer.h"
#include <EntityEngine/Entity.h>
#include <NetworkSharedLibrary/NetEvents.h>

using namespace EntityEngine;

ExamineComponent::ExamineComponent(Entity* entity, const CL_String &name)
: Component(entity, name)
{
	description = entity->AddProperty<CL_String>("Description", CL_String());
}

void ExamineComponent::RequestCommands(std::vector<CL_String> &requestCommands, ServerPlayer *player)
{
	requestCommands.push_back("Examine");
}

void ExamineComponent::ExecuteCommand(const CL_String &command, ServerPlayer *player)
{
	if(command == "Examine")
	{
		OnExamine(player);
	}
}

void ExamineComponent::OnExamine(ServerPlayer *player)
{
	GameObject *gameObject = (GameObject *)entity;
	gameObject->SendViewEvent(CL_NetGameEvent(STC_OBJECT_DESCRIPTION, description.Get()), player->GetConnection());
}


------------------------------------ - MovementComponent.h ------------------------------------
#pragma once

#include <EntityEngine/Component.h>
#include <EntityEngine/Property.h>

namespace EntityEngine { class Entity; }

class MovementComponent : public EntityEngine::Component
{
public:
	MovementComponent(EntityEngine::Entity* entity, const CL_String &name);
	virtual ~MovementComponent() {}

	virtual void Update(int deltaTime);

	static CL_String GetType() { return "Movement"; }
	static EntityEngine::Component* Create(EntityEngine::Entity* entity, const CL_String &name) { return new MovementComponent(entity, name); }

protected:
	EntityEngine::Property<float> movementSpeed;
	EntityEngine::Property<CL_Pointf> newDestinationPosition;
	std::vector<CL_Pointf> movementDestinations;

	CL_Slot slotNewDestinationPositionChanged;
	void OnNewDestinationPosition(const CL_Pointf &oldValue, const CL_Pointf &newValue);

	void ClearDestinationPositions();
	void AddDestinationPosition(const CL_Pointf &position);
	void SendDestinationPositions();
};


---------------------------------------- - MovementComponent.cpp ----------------------------------------
#include "precomp.h"
#include "MovementComponent.h"
#include "../GameObject.h"
//...
#include <EntityEngine/Entity.h>
#include <NetworkSharedLibrary/NetEvents.h>

using namespace EntityEngine;

MovementComponent::MovementComponent(Entity* entity, const CL_String &name)
: Component(entity, name)
{
	movementSpeed = entity->AddProperty<float>("MovementSpeed", 100.0f);
	newDestinationPosition = entity->AddProperty<CL_Pointf>("NewDestinationPosition", CL_Pointf());
	slotNewDestinationPositionChanged = newDestinationPosition.ValueChanged().connect(this, &MovementComponent::OnNewDestinationPosition);

	GameObject *gameObject = (GameObject*)entity;
	gameObject->AddClientComponentRequirement(GetType());
}

void MovementComponent::OnNewDestinationPosition(const CL_Pointf &oldValue, const CL_Pointf &newValue)
{
	ClearDestinationPositions();
	AddDestinationPosition(newValue);
	SendDestinationPositions();
}

void MovementComponent::Update(int deltaTime)
{
	GameObject *gameObject = (GameObject*)entity;
	
	//... perform some movement logic
}

void MovementComponent::ClearDestinationPositions()
{
	movementDestinations.clear();
}

void MovementComponent::AddDestinationPosition(const CL_Pointf &position)
{
	movementDestinations.push_back(position);
}

void MovementComponent::SendDestinationPositions()
{
	CL_NetGameEvent movementDestinationEvent(STC_OBJECT_MOVEMENT_LIST);

	for(size_t i = 0; i < movementDestinations.size(); ++i)
	{
		movementDestinationEvent.add_argument(movementDestinations.x);
		movementDestinationEvent.add_argument(movementDestinations.y);
	}

	GameObject* gameObject = (GameObject*)entity;
	gameObject->SendViewEvent(movementDestinationEvent);
}


[Edited by - Trefall on October 27, 2009 11:05:48 AM]
Advertisement
Hey Trefall!

It's nice you want to share something with us, but your post looks to be a candidate for an online journal. It's just that GDNet is very active and this post will not stay at the top for long unless stickied.

Maybe you can throw your code at CodeProject or DevX as an alternative for paying for a GDNet membership and getting a dev journal.

Quote:Let components add data to their entity, let them change it, and let all components in an entity have the option to react to data changing!


You are implying that components own entities, then imply the exact opposite. May want to edit that.

A little feedback... You are going through something I once went though:
A burning desire to innovate abstract declarations and to network any and all possible combinations thereof.
My friend, this system will burn you out if you are not careful. I did something like this once, and I made things so
complicated I tore down my project. I now know I was being screamingly redundant. Please take this in the spirit it is intended, I am not trying to be condescending.

Note that your engine is really a glorified method of object declaration.
Why write this...

GameObject *objectRobot = new GameObject(entityManager, zone);objectRobot->AddComponent("Examine");objectRobot->AddComponent("Movement");objectRobot->GetProperty<CL_String>("Description") = "It's a useless robot!";objectRobot->GetProperty<float>("MovementSpeed") = 50.0f;


When you could write this?

struct Robot{   void Examine();   void Move();   CL_String Description;   float     MovementSpeed;};


We programmers love to experiment, but please note your engine isn't doing anything C++ can't do. We have all the means to declare and define what we want to begin with. If we want a managing object we also write that, as well as any ABCs that can group like objects under a pointer type. Please note one of the LAST things we want to do is to build each and every member of an object one by one each time we want to instantiate it! That's the kind of thing high-level languages hide from us.

It looks like you want run time checking. Here's a little tip: Don't use strings for this. That's a bigger headache than you might think. The sizes are effectively arbitrary and the comparison methods are tedious to type after a while [sick]. I suggest GUIDs or cleverly laid out bit fields queried in a COM-Like fashion. Have a header!

FYI, forgive the virtual inlines. Small methods just shake the inline keyword out of me. [smile]

EDIT: Damn the omission of the backslash in my macros! [crying]
Forgive that too.

//=========================================================================/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *	File: AbstractType.hpp	Date: 2009/09/27	Time: 19:44:30	Description	-----------	An object that offers virtually limitless and accurate	runtime type queries under a strict derivation rule.	Uses GUIDs for unique identification or a 32-bit integer.	It is NOT reccomended to try and mix the two id types	under one project, as certain accomodations are made for each.	#define AT_BITFIELD to use bitfields, else GUID is used.	In this case you must generate a new GUID for every derivation.	NOTES	-----------------------------	Vtable overhead may be vicious. Use sparingly.	Copyright © Sage Lennon Gerard. All Rights Reserved. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *///=========================================================================#pragma once#ifndef __ABSTRACTTYPE_INC__#define __ABSTRACTTYPE_INC__#ifndef AT_BITFIELD#include <rpc.h>typedef GUID ATID;typedef const GUID& inATID;// Comparison functorstruct ATCmp{	inline bool operator()(inATID a, inATID b) const	{ return memcmp(&a,&b,sizeof(ATID)) < 0; }};inline bool operator<(inATID a, inATID b) { return memcmp(&a,&b,sizeof(ATID)) < 0; }// Category and Unit are not really useful when using ATID as unsigned int.inline unsigned short ATIDCategory( inATID a ){	return a.Data2;}inline unsigned short ATIDUnit( inATID a ){	return a.Data3;}#elsetypedef unsigned __int32 ATID;typedef const unsigned __int32 inATID;// Comparison functorstruct ATCmp{	inline bool operator()(inATID a, inATID b) const	{ return a < b; }};inline bool operator<(inATID a, inATID b){	return a < b;}// Category and Unit are only really useful when using ATID as unsigned int.inline unsigned short ATIDCategory( inATID a ){	return (a & 0xffff0000)>>16;}inline unsigned short ATIDUnit( inATID a ){	return (a & 0x0000ffff);}#endif/****************************************************************************AbstractTypeAbstract object that offers COM-Like runtime type queries using minimalisticpolymorphism.*****************************************************************************/struct AbstractType{		inline	AbstractType() {}	virtual inline ~AbstractType() {}	virtual bool	QueryType	( inATID ) const = 0;	virtual inATID	QueryType	() const = 0;};/******************************************************************************Macros to simplify deriving from AbstractType.******************************************************************************//******************************************************************************OFTYPE derives from another derivative of AbstractType.Do NOT use AbstractType itself!******************************************************************************/#define OFTYPE( base_, name_, atid_ ) class _ofType##name_ : public base_ { public: 	inline bool QueryType( inATID i ) const { return (atid_ == i) ? true : base_::QueryType(i); } 	inline inATID QueryType() const { return atid_; } }; class name_ : public _ofType##name_ /******************************************************************************OFNEWTYPE derives from AbstractType ONLY.******************************************************************************/#define OFNEWTYPE( name_, atid_ ) class _ofType##name_ : public AbstractType { public: 	inline bool QueryType( inATID i ) const { return (atid_ == i) ? true : false; } 	inline inATID	QueryType() const { return atid_; } }; class name_ : public _ofType##name_ #endif // __ABSTRACTTYPE_INC__


I hope this helps.
Cheers!
-Zyro
Thank you for your reply, zyrolasting.

I'll make sure to edit and fix the line that indicates that components own entities, since it's most definately the opposite, which I'm also sure is the impression all readers would sit back with after reading the whole text here.

I understand where you're coming from though, and I also understand that we like to approach defining game objects in two different ways. In fact, the code example I showed:

GameObject *objectRobot = new GameObject(entityManager, zone);objectRobot->AddComponent("Examine");objectRobot->AddComponent("Movement");objectRobot->GetProperty<CL_String>("Description") = "It's a useless robot!";objectRobot->GetProperty<float>("MovementSpeed") = 50.0f;


is only the hardcoded way of defining this robot object, while in fact the whole point with a component-based approach is to make object definition data-driven. Like I said in the text, it wouldn't take a lot of programming effort to allow a designer to define new object types via XML.

<object type="Robot">  <components>    <component>Examine</component>    <component>Movement</component>  </components>  <properties>    <property name="Description">It's a useless robot!</property>    <property name="MovementSpeed">50.0</property>  </properties></object>


Imagine how simple it would be to create a flavourish graphical UI that would allow designers to string together components and edit the available properties? No recompilation, no nothing, your designers can define completely new object types 100% outside sourcecode.

Now, it also takes little effort to expose the required functionality to a scripting language, that would allow you to define new component types in script. Now scripters could define new game logic, and immediately test this new logic without ever having to recompile the game.

So, I'm very excited about the possibilities of this approach, because I don't want to shut down the server, recompile the game and run the server again every time I make a change to it's data or it's logic.

I think that every game have different requirements, and for some projects this component-based approach can be a real benefit.

There's a problem with component-based designs, however, and that is that there probably are as many ways to approach it as there are programmers in the world. This is great in some ways, because it makes this a topic of endless discussion. There have been some clear disadvantages with using components, like dependency chains between components (if you're going to use this component, then you have to use this and that, and those components too)... I'm quite excited about the fact that this does not apply to my approach, since there's no dependency between components. Multiple components may have a common data type stored in the Entity that they use, but this is perfectly fine. Just make sure your components react to when it changes...

Another drawback is that you have to iterate over a container of components every time you're updating each entity. This is a sacrifice you'd make for getting the possibilities of the data-driven and modular approach.

Quote:but please note your engine isn't doing anything C++ can't do. We have all the means to declare and define what we want to begin with. If we want a managing object we also write that, as well as any ABCs that can group like objects under a pointer type. Please note one of the LAST things we want to do is to build each and every member of an object one by one each time we want to instantiate it! That's the kind of thing high-level languages hide from us.


You're totaly right. Like I said, my approach is for those who'd like to make object definition data-driven, because they are working on a project where such an approach would save their team time. And you're absolutely right. You don't want to define every single module and give every single property data every single time you're instanciating an object. Like I explained above to my best ability, you do the definition in XML, from there on you'd just want to do something like

entityFactory->Create("Robot");


or more likely, through some scripting language like

entityFactory:Create("Robot")


Certainly my code does not contain this functionality yet, so you're more than right to point this lack of functionality out. But neither does the open source code here give you XML or Scripting support. You could even do a ton of optimization on that codebase if you'd want to.

I'll be updating and improving this code throughout it's life in the project I'm currently working on. When script support arrives, I'll open up that sourcecode too, when XML support arrives I'll open that up too and when we're in the optimization stage, the sourcecode will surely be updated.

Getting through enums/integers instead of strings is certainly a good optimization. We haven't bothered with this yet, since our game hasn't really required it yet. Same thing goes for using std::map instead of a hashmap for instance... I'm also sure we could approach our virtual methods a bit differently, reducing the number of virtuals.

I appreciate your input. The component-based approach seems to always be a source for a lot of discussion and opinions :)
Quote:Imagine how simple it would be to create a flavourish graphical UI that would allow designers to string together components and edit the available properties? No recompilation, no nothing, your designers can define completely new object types 100% outside sourcecode.

Now, it also takes little effort to expose the required functionality to a scripting language, that would allow you to define new component types in script. Now scripters could define new game logic, and immediately test this new logic without ever having to recompile the game.


It's good in theory. The only problem I'm having with this is that you are not diving to a low enough level to encompass an audience that can change their standards practically overnight. You are trying to make an ambiguous system with a bit of an obstacle course, including a serious danger of code bloat with those templates of yours.

I'm just skimming here so I may have missed it, but I do not see anything that protects the user from choosing non-existent components or properties. If you have that logic, fine, but one thing that's bugging me is that everything you are doing here can be pushed down to a series of byte arrays, which would be more future proof and slightly easier to visualize since you are just treating abstract data as just that... A mindless assortment of 1s and 0s. Each byte array is another object, and any queries to a certain component/piece/whatever would just be a certain number of bytes offset from the beginning of a certain array. At first glance it may require less of the headache of navigating through everything. Just a thought.

But I digress... I just feel this could be simpler. You could go more in the direction Autodesk Maya's attribute memory reminds me of. An entity can only be aware of so many of it's own properties in Update(), but others it can just be at the whim of to other objects in the scene.

#include <string>#include <map>#ifdef _UNICODEtypedef std::wstring property;#elsetypedef std::string property;#endifinline bool propCmp(const property& a, const property& b){	return a.compare(b) < 0;}typedef std::map<property,size_t,propCmp> Properties;/******************************************************************************ABC with property map.******************************************************************************/class Entity{	Properties m_Properties;public:			 Entity();	virtual ~Entity() { m_Properties.clear(); }	virtual bool Update( const float dt );	void SetProperty( const property& name, const size_t value ); // 	int  GetProperty( const property& name ) const; // Return -1 on non-existing};


Just take my word for it, the motivation you have is wonderful, but the approach your taking is so much like one I took that caused me so much grief in the end. I just feel I have to warn you not to try and grab so many "potentially useful" abstract options in a pool of... completely abstract options. It looks like you have a system with too few absolutes that prevent it from standing out in the environment it's in. That's just me. I'd wait to introduce a component like this until you actually have a SPECIFIC engine to attach it to, where you can be more certain of the rules to keep up with.

It's on you, though.

Hope this helps.
Cheers!
-Zyro
Quote:I'm just skimming here so I may have missed it, but I do not see anything that protects the user from choosing non-existent components or properties.

For the properties, if a property does not exist, it will be made. One could certainly throw an exception here, or log down that a new property was made, just so that it would be easier to detect and note down if this was unintended, but for a game of thousands of properties, this wouldn't run very well. It shouldn't be a problem to add this in case you would suspect there's such a bug in your system, or make an Entity Inspector that you can detect such properties at run-time.

if(it != properties.end())		return *static_cast< Property<T>* >(it->second);	Property<T> *property = (Property<T> *)factory.CreateProperty<T>(manager, propName);


For components, we throw and exception if the component you tried to add does not exist.

if(component)	{		components.push_back(component);		return component;	}	else		throw new CL_Exception("Unable to create component " + componentType);


When making a system like this, you can only hold a developer's hand so far. What you allow in script is a totally different matter imo, one should never be able to crash a running application with a script, and certainly not a server.

Quote:one thing that's bugging me is that everything you are doing here can be pushed down to a series of byte arrays, which would be more future proof and slightly easier to visualize since you are just treating abstract data as just that... A mindless assortment of 1s and 0s.


I didn't quite follow you on what you were saying here. Are we still at the struct level? I already told you why I didn't go that route (data-driven requirement), so I'm sure you're getting at something else. Please elaborate.

Quote:Just take my word for it, the motivation you have is wonderful, but the approach your taking is so much like one I took that caused me so much grief in the end. I just feel I have to warn you not to try and grab so many "potentially useful" abstract options in a pool of... completely abstract options. It looks like you have a system with too few absolutes that prevent it from standing out in the environment it's in. That's just me. I'd wait to introduce a component like this until you actually have a SPECIFIC engine to attach it to, where you can be more certain of the rules to keep up with.


I'm really glad that you are sharing your experience and that you warn me based on your own wrong-goings. This system I've listed here though, is already part of a playable, persistent online game, and we find this way to approach object definition works great for us.

One thing we most definately can agree on, is that you should be very critical to any third party source code you embed into your own project.

I've wanted to post this system publicly for a very long time, but I waited until we got further into production on our game, and until we started to actively use this system in our game. As I had expected, a lot of changes happened in this source code once we actually started using it, and there are probably more changes that will have to occure down the line as new requirements of features needs to be addressed and unforeseen problems arise.

Those things almost certainly will happen, but that's how programming works. Like I said in my last post, this is not a system that will work for everyone, it's not a coding style, or a way of approaching the problem, that will satisfy everyone, or that will meet the requirement of any project. Anyone who'd like to use this system would probably have to tweak the code a bit to fit their needs and requirements, that's a given.

I've went very wrong with component-based designs before, and I know how it feels to have a system failing that is really holding a lot of the foundation in a development framework, and where exchanging it, would really be the same as scraping the entire project and starting over again, more or less. I wouldn't post this piece of sourcecode if I hadn't found it useable in my own project first :)

[Edited by - Trefall on October 27, 2009 11:13:12 AM]
Commited a small update to the code that now throws an exception if you try to get a component that does not exist. This instead of returning NULL.
Have you considered adding a bit more exposition to this and submitting it to writers@gamedev.net as an article?
Quote:Have you considered adding a bit more exposition to this and submitting it to writers@gamedev.net as an article?


Oh, I never considered this worthy of being listed there, but that is a very intriguing thought! Thank you :)
Trefall, very interesting implementation. I haven't read every reply in this thread only the main post, so this may have been asked, but how do you manage update order of the components?

For instance, say you have a physics component and a graphic component. How would you insure the graphic component updated the visual representations position after the physics component updated the objects position?

Your post here has definitely given me something to think about for my implementation.
Sleep is for the weak, or at least that is what I tell myself every morning.
void ComponentContainer::UpdateComponents(int deltaTime){	std::vector<Component*>::iterator it;	for(it = components.begin(); it != components.end(); ++it)		(*it)->Update(deltaTime);}


As you can see, the components are not weighted. This is because we're not using the component system at that low level. The reason I really would discourage that low-level use, is that you create dependencies between components, which is really really bad. Of course, it wouldn't take much to add a simple weight system into the mix here and use an incrementor to iterate over the components by weight, so it's certainly doable, but to me, that's just not what I'd want to do with components.

The reason is that I'd rather put this logic into the base game object of my game engine. This is logic that will be so common between game objects, that having to specify this when defining a new object would just be tedious. Why would you bother having to add RenderComponent and PhysicsComponent to 99.99% of your game object definitions?

If you're very determined that you're going to need a RenderComponent and PhysicsComponent, then I'd put them to use a little bit differently from the rest. You have total control of your GameObject class which inherits from Entity (or ComponentContainer and PropertyContainer). It should be straight foreward to hardcode the two components to exist for the GameObject type, and explicitly call these two components' Update function. So maybe your GameObject's Update function would look something like this:

void GameObject::Update(int deltaTime){        physicsComponent.Update(deltaTime);	UpdateComponents(deltaTime);        renderComponent.Update(deltaTime);}


Personally I'd rather find out which components will be in use for most game objects and define hardcoded bodies for these to then add more components on top of. Examples of this could be ItemGameObject and ActorGameObject. Of course, there are a ton of ways to solve this as well. You could define an ActorGameObject in XML and allow other object definitions to inherit from it, for example...

There is a little bit of overhead in performance in using components though, but if data-driven object definition is something you'd like to have in your project, then I think it's worth it going this route. Whether you want to make RenderComponent and PhysicsComponent part of that data-driven object definition is really up to you.

This topic is closed to new replies.

Advertisement