Jump to content
  • Advertisement
Sign in to follow this  
l0k0

Unity Component Entity Model Without RTTI

This topic is 2319 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm writing my own component entity model in C++. At the moment, it is somewhat similar to the framework used for Unity Scripting (particularly in C# with templates). Since I'm still in the early phases of the project, I just used type info to handle the type based retrieval of components like so:


template<class T> T * getComponent() {
for (int i = 0; i < componentList.size(); ++i) {
if (typeid(*componentList) == typeid(T)) {
return componentList;
}
}
return NULL;
}


However, RTTI (along with exceptions) is typically best left disabled in frameworks and engines if possible. What would you recommend for a minimalist way to get this functionality for components only? I'm willing (and hoping) to use the preprocessor to declare classes and assign them a "type id" kept inside the components themselves. The tricky issue is that this must also support subclasses. So if I call getComponent on a base class it must also return a subclass if it is found.

Is a 32 bit type id determined at compile time my best option here? Any tips from people who have implemented the thing themselves?

Share this post


Link to post
Share on other sites
Advertisement
What I have typically seen is that there is a unique numeric value assigned to each family of components. You can assign this value either by doing it at compile-time in your code or by using some factory/class registration mechanism that handles the assignment at run-time.

Share this post


Link to post
Share on other sites

#define ADD_RTTI(BASE_TYPE, ID) public: enum { kTypeId = ID; }; bool isDerivedFrom(uint32_t nodeType) const { return nodeType == kTypeId ? true : BASE_TYPE :: isDerivedFrom(nodeType); }

class Base
{
public:

enum { kTypeId = 0; }

virtual bool isDerivedFrom(uint32_t nodeType) const { return nodeType == kTypeId; }

template<typename T>
T* asType()
{
return isDerivedFrom( T::kTypeId ) ? (T*)this: 0;
}
template<typename T>
const T* asType() const
{
return isDerivedFrom( T::kTypeId ) ? (const T*)this: 0;
}
};

class Foo : public Base
{
ADD_RTTI(Base, 1);
};

class Bar : public Foo
{
ADD_RTTI(Foo, 2);
};
void rttiTestBar(Base* obj)
{
Bar* bar = obj1->asType<Bar>();
if(bar )
{
std::cout << "obj is Bar\n";
}
else
{
std::cout << "obj is not Bar\n";
}
}
void rttiTestFoo(Base* obj)
{
Foo* foo = obj1->asType<Foo>();
if( foo )
{
std::cout << "obj is Foo\n";
}
else
{
std::cout << "obj is not Foo\n";
}
}
int main()
{
Base* obj1 = new Foo();
Base* obj2 = new Bar();
rttiTestFoo(obj1);
rttiTestBar(obj1);
rttiTestFoo(obj2);
rttiTestBar(obj2);
delete obj1;
delete obj2;
return 0;
}

Share this post


Link to post
Share on other sites
I use this solution:


inline unsigned int new_id()
{
static unsigned int previous_id = 0;

++previous_id;
return previous_id;
}

template< class Type >
class id_container
{
public:
const static unsigned int value;
};
template< class Type >
const unsigned int id_container< Type >::value = new_id();

template< class Type >
unsigned int type_id()
{
return id_container< Type >::value;
}


Keep in mind though that this won't work across dll boundaries, and you should make the new_id() function thread safe if you want to use type_id in mutliple threads.

Share this post


Link to post
Share on other sites
Simliar to GorbGorb's solution:

Code:

template < typename T >
inline unsigned int GetRTTI()
{
static char s_rtti;
return &s_rtti;
}


Pro:
One of the quickest implementations ever. Don't have to write much at all.
Fast comparisons.
Low memory overhead

Con:
RTTI values are not absolute and will vary with memory layout. Tracking bugs between different code executions may be difficult.
May want more info other than a memaddress.

Share this post


Link to post
Share on other sites

class TypeId {
public:
virtual unsigned getTypeId() const = 0;

template<class Type>
bool isInstanceOf() const {
return getTypeId() == Type::getClassTypeId();
}
};

template<class Type, class GUID>
class AcquireTypeId : public virtual TypeId {
public:
unsigned getTypeId() const {
return GUID;
}

static unsigned getClassTypeId() {
return GUID;
}
};

//
// Elsewhere!
//

class Component : public virtual TypeId {
/* Stuff */
};

class SpecificComponent
: public Component
, public AcquireTypeId<SpecificComponent, 0xF5B30CDF> {
};


Advantages:
+ Identifiers are static
+ No macros
+ Virtual inheritance allows for components to be written in script

Disadvantages:
- Unable to determine parent-child relationships
- Virtual inheritance
- No macros and no names; just a bunch of numbers
o V-table lookup required

Though my design can be improved by embedding the type-info with the object; thus avoiding the virtual inheritance and v-table lookup...

Share this post


Link to post
Share on other sites
This is my implementation of your entity class:


template< class Type >
void destruct_function( void *obj_mem )
{
static_cast< Type* >( obj_mem )->~Type();
}

class entity
{
public:
~entity()
{
for( auto it = components.begin() ; it != components.end() ; ++it )
( *it->first )( it->second );
}
template< class Type >
Type *query()
{
auto it = components.find( &destruct_function< Type > );
if( it != components.end() )
return static_cast< Type* >( it->second );
else
return 0;
}
private:
std::map< void (*)( void * ) , void * > components;
};

Share this post


Link to post
Share on other sites
Just for some food for thought: it's entirely possible to implement a component/entity system without even providing a "get child component by type" function whatsoever wink.gif -- getting a component by type is just one way that you can choose use a component/entity system, with it's own robustness/predictability/maintenance pros/cons.

Share this post


Link to post
Share on other sites

Just for some food for thought: it's entirely possible to implement a component/entity system without even providing a "get child component by type" function whatsoever wink.gif -- getting a component by type is just one way that you can choose use a component/entity system, with it's own robustness/predictability/maintenance pros/cons.

Would you mind clarifying on that point? Would you instead get a component by instance id? Is what you're suggesting use tables in someway?

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!