Component-based Entities using Properties

Started by
23 comments, last by owl 14 years, 5 months ago
Quote:
There's no reason templates have to come into your entity class. All I'm talking about is literally a component type ID.

// getComponent() definition, overloaded once per concrete component typetemplate <typename ComponentType>ComponentType* getComponent() {    // Use the class's static id value which you've defined as something unique    IComponent* c = this->componentMap.get(ComponentType::id);    return static_cast<ComponentType*>(c);}


Why haven't I thought of that? That's a great idea. All that is left then is to generate a unique ID for every component type. That should be fairly easy to come up with.
Advertisement
I'd do a dynamic_cast on thatone, at least in debug builds, else there's no way to catch a null pointer exception if the type is wrong.

I'm not using templates on the components. I found that I never really did GetComponents too often anyway, but on the properties I had this issue.

Property<String> someProp = Property<String>("SomeProp", String());entity->AddProperty(someProp);Property<int> prop = entity->GetProperty<int>("SomeProp"); //Runtime crash!!


When you get 100+++ components, the chances that you'll try to get on a wrong type is highly likely. Yeah, it's easy to track in the Callstack, but it's a lot more graceful if you can handle the exception. That's my opinion anyway :)

I must add, though, that I'm working on scripting towards the game server at run-time, which is why we decided to afford the dynamic_cast in our release builds as well.

[Edited by - Trefall on November 16, 2009 4:43:54 AM]
Nice post! Ultimately, what these component frameworks do is what dynamic languages like Python or Smalltalk have always done naturally: Being able to add and remove attributes to an object at runtime, which is nothing more than a glorified dictionary.

Anyway, have you thought to use function pointers/delegates, in order to connect components with properties, without having to do a string lookup and a cast every time you access one? Let me show what I mean with some C#:

class RenderingComponentInputs{    public Func<Vec3> PositionGetter;}class RenderingComponentOutputs{}class RenderingComponent:Component{    public RenderingComponentInputs In=new RenderingComponentInputs();    public RenderingComponentOutputs Out=new RenderingComponentOutputs();}class PhysicsComponentInputs{    public Func<Vec3> PositionGetter;}class PhysicsComponentOutputs{    public Action<Vec3> PositionSetter;}class PhysicsComponent:Component{    public PhysicsComponentInputs In=new PhysicsComponentInputs();    public PhysicsComponentOutputs Out=new PhysicsComponentOutputs();}...static void CreateEntity(){    Entity ent=new Entity();    ent.AddProperty<Vec3>("Position");    PhysicsComponent phys=new PhysicsComponent();    phys.In.PositionGetter=()=>ent.GetProperty<Vec3>("Position");    phys.Out.PositionSetter=(value)=>ent.SetProperty<Vec3>("Position",value);    RenderingComponent rend=new RenderingComponent();    rend.In.PositionGetter=()=>ent.GetProperty<Vec3>("Position");        ent.RegisterComponent(phys);    ent.RegisterComponent(rend);}...


I think if you create some PropertyDescriptor class(or use the ones provided if you're using C#) which creates direct setters and getters to the property, you can bypass the string lookup completely. Something like:

PropertyDescriptor positionProp=ent.GetPropertyDescr("Position");
phys.Out.PositionGetter=(value)=>positionProp.SetValue<Vec3>(value);

Also, this way you can plugin outputs and inputs in your GUI designer, and can detect dependencies so you update components in the right order.
If I understood your text correctly, this is pretty much what I do already. Every component will hold a direct reference to the properties they care about and won't look up on the string key of the property in Entity's. So actual map lookups occure seldomly. This has worked really well for us so far, and exposing properties to lua also has worked really well so far.
Quote:Original post by mazelle
Quote:
There's no reason templates have to come into your entity class. All I'm talking about is literally a component type ID.

// getComponent() definition, overloaded once per concrete component typetemplate <typename ComponentType>ComponentType* getComponent() {    // Use the class's static id value which you've defined as something unique    IComponent* c = this->componentMap.get(ComponentType::id);    return static_cast<ComponentType*>(c);}


Why haven't I thought of that? That's a great idea. All that is left then is to generate a unique ID for every component type. That should be fairly easy to come up with.


String interning could also be used for such end.
[size="2"]I like the Walrus best.

This topic is closed to new replies.

Advertisement