Unity Component Entity Model Without RTTI

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

Recommended Posts

I'm just curious as to why one would purposefully handicap themselves

One reason is simplicity. You have what you see. Not an unknown quantity which may be anything. It reduces the code size several fold. Also consider that C has no RTTI. On another end of scale is IoC and DI, both proven to scale to arbitrarily large projects by eschewing traditional typing.

by not taking advantage of even the most basic of OO techniques[/quote]
It's not necessary to abandon any OO techniques, it's about design. All of the above can be identically applied in any language.

Other reason is performance. You mentioned "1/1000th of a millisecond". That would be 1 microsecond, or on the order of several thousand CPU cycles. In that time, one can transform several thousand vertices or one model. In reality, the dynamic dispatch is considerably less (either no penalty due to branch prediction or only several cycles), but it adds up. Hence it's completely missing in GPU programming, where completely other techniques are used.

but its almost impossible to avoid OO in c# and therefore avoid RTTI.[/quote]
LINQ. Entity Framework. OO has been abandoned in .Net and just about any other framework. It simply doesn't scale management-wise.

Obivously, C# being designed on top of object-based VM means that syntax still uses Java-like constructs, but application design has moved completely away from OO. It's similar to how SOLID principles are commonly used in C applications despite language not supporting OO at all.

Performance plays a role here again, but in a different way. With emphasis being on web development, the latency of receiving data is simply too high. If dynamic dispatch costs only a cycle or two on CPU, equivalent design requires up to several seconds over internet. Hence the introduction of SPDY, the studies into async loading of assets in browsers, the preference of JSON vs. XML, the designs of web APIs. OO simply doesn't work over internet as Sun's falacies teach. One of biggest examples of such failures is CORBA.

Share on other sites
Oh, i completely agree with you on what you've said. I guess its just a difference in personal philosophy. I tend to favor flexibility over performance and it seems you're vice-versa. Don't take that to mean I'm against writing for performance, i just tend to go towards building a flexible framework first, then speed it up as needed.

Share on other sites

I tend to favor flexibility over performance and it seems you're vice-versa.

Not really. I find that I get considerably more flexibility and simplicity. Performance is not the crucial factor, but alternate designs are good fit for hardware realities.

Share on other sites
Often the most performant and flexible systems turn out to be extremely simple

Share on other sites

However, RTTI (along with exceptions) is typically best left disabled in frameworks and engines if possible.

Sorry, I'm late to the discussion and the answer to my question may have already been provided. I'm also developing my own component entity system for my C++ Engine and I'm really curious as to where the above statement comes from. I use RTTI in my current system with essentially a base Component class and a one level of inheritance to derive Subsystem Components classes. I'm not using templates. Whats wrong with using RTTI?

Share on other sites

Whats wrong with using RTTI?

The topic of the thread is
Component Entity Model Without RTTI

I think thats all thats wrong with it.

Share on other sites
Whats wrong with using RTTI?
You can do better.

If you were to implement it yourself, would you do it like this?struct TypeItem { const char* name; TypeItem* parents; TypeItem* next; }; std::map<void*, TypeItem> g_types; bool Match( TypeItem a, TypeItem b ) { if( strcmp(a.name, b.name)==0 )//cache miss return true; else if( b.next && Match(a, *b.next) )//cache miss, recursive return true; else if( b.parents && Match(a, *b.parents) )//cache miss, recursive return true; else return false; } bool Match( void* objectA, void* objectB ) { void* vtableA = *(void**)objectA; void* vtableB = *(void**)objectB; return Match( g_types[vtableA], g_types[vtableB] ); }...because you shouldn't be surprised if your compiler implements RTTI in such a horribly-performant manner.

And if you knew that type-comparisons involved calling a function this ugly, would you think twice about calling this type-comparison function?

Also, as discussed earlier, there's no need to have a [font=courier new,courier,monospace]entity->GetComponent<T>()[/font] function in the first place...

Share on other sites

[quote name='T e c h l o r d' timestamp='1326690288' post='4903126']Whats wrong with using RTTI?
You can do better.

If you were to implement it yourself, would you do it like this?struct TypeItem { const char* name; TypeItem* parents; TypeItem* next; }; std::map<void*, TypeItem> g_types; bool Match( TypeItem a, TypeItem b ) { if( strcmp(a.name, b.name)==0 )//cache miss return true; else if( b.next && Match(a, *b.next) )//cache miss, recursive return true; else if( b.parents && Match(a, *b.parents) )//cache miss, recursive return true; else return false; } bool Match( void* objectA, void* objectB ) { void* vtableA = *(void**)objectA; void* vtableB = *(void**)objectB; return Match( g_types[vtableA], g_types[vtableB] ); }...because you shouldn't be surprised if your compiler implements RTTI in such a horribly-performant manner.

And if you knew that type-comparisons involved calling a function this ugly, would you think twice about calling this type-comparison function?

Also, as discussed earlier, there's no need to have a [font=courier new,courier,monospace]entity->GetComponent<T>()[/font] function in the first place...
[/quote]
I don't know how the compiler implements RTTI and I honestly don't think my implementation would out perform it. I would like to believe that the compiler scientists identified weak RTTI performance and implemented an optimization, after all one would have to go thru the compiler to compile a home grown implementation. I also don't use templates but, I can rationalize why there's no need to have a [font=courier new,courier,monospace]entity->GetComponent<T>()[/font]. I'm still in the process of refactoring and haven't fully worked out how the communication between components.

I use RTTI in my current system with a base Component class and a one level of inheritance to derive Subsystem Components classes. I'm personally not a big fan of the extra typing involved with using static_cast<derived*>(base) and dynamic_cast<derived*>(base) keywords. Which is my only grief. Other than that I still have seen any data validating the claim RTTI is bad for performance. Can someone please point me to some documentation that supports this claim? If necessary, I rather change the implementation now than later. Thanks in advance.

Share on other sites
I don't know how the compiler implements RTTI and I honestly don't think my implementation would out perform it. I would like to believe that the compiler scientists identified weak RTTI performance and implemented an optimization, after all one would have to go thru the compiler to compile a home grown implementation.
Just think about the feature that it's implementing -- it's mighty complex.
Try and think about how you might implement such a general system so that this kind of magic is possible:struct A {}; struct B {}; struct C : public A, public B {}; struct D : public C {}; D data; B* ptrBase = &data; D* ptrDerived = dynamic_cast<D*>( ptr );No matter how much you optimise it, it's still going to be mighty complicated. You can't optimise out that complexity, because the complexity of working in every inheritance hierarchy is the core feature that it provides. Also, yes, it is often implemented using string-comparisons and linked-lists, because the compiler authors know that no-one who cares about performance is going to use [font=courier new,courier,monospace]dynamic_cast[/font] and then complain when it's slow...

The reason that you can easily beat this, is because you don't need that much complexity! If you only need to be able to perform exact type comparisons (no hierarchy testing) on a specific, small set of types, then you can roll an ID system in two lines:struct TypeIdBase { protected: static int NextId() { static int i = 0; return i++; } }; template<class T> struct Type : TypeIdBase { public: static int Id() { static int id = NextId(); return id; } };and implement type-comparisons in one line, with the ability to store type-id's in [font=courier new,courier,monospace]int[/font] members if needs be:if( Type<int>::Id() == Type<float>::Id() ) printf("what?");
However, it's extremely rare to find a use for [font=courier new,courier,monospace]dynamic_cast[/font]ing that's justifiable in the first place. There's usually a simpler, cleaner, more maintainable way of doing things.
In what situations do you use [font=courier new,courier,monospace]dynamic_cast[/font]?

Share on other sites
I use pointers to the base component class to work with derived component classes. I only use RTTI casting when a base class pointer needs to access a member of the derived class. My initial reason for this approach was make my life easier wrapping objects from 3rd Party libraries like Ogre::Mesh into a Component. A Derived Component inherits from both the Base Component and Ogre::Mesh Classes or contains members to work Ogre::Meshes directly. I'm relatively new to C++ and still trying to wrap my head around a components implementation.

Share on other sites
For my assignment of entities to components i use an array of arrays to contain components by type. When a component is added to the framework, a type Id is established. This is nothing more than assigning an incrementing integer to a static int in the component. Since its static, each and every component of that type will report the same exact type Id (i also perform an early registry when a system is established to ensure a type id is created as well). so when i need to retrieve component x from entity y i use x.id to index into the array of arrays to pull out the applicable component array, then use the y.id (entity) to retrieve the relevant component. (i.e., no type comparisons are ever required other than checking for index safety).

now of course this also adds some memory overhead, but the trade-off is access performance. of course my implementation uses RTTI to access the type id, as its generally accessed through an interface. Now if i could get a way to do that without having to go through an interface but still remain agnostic in implementation thats faster than an assembly table lookup (from my digging around, that is approximately how interfaces are implemented by the compiler), i'm all ears Or will i need to loose the agnostic approach?

Share on other sites
Well, hrm... now that i think of it, i could make the component be a mapping container, where it holds a type id and then another mapping id which would say which index I would want in a dedicated array for that data type. The component manager and mapper would just use another layer of lookup to get/set the data needed. I would need to use something like a switch to direct it to the correct retrieval mechanisms, but would this be faster than an interface?

The component would be something like this:

 class Component { int entityId; int componentType; int componentId; } 

though, the componentId and entityId could be merged to do the indexing if you dont mind an entity only able to own one component of any type. If you did that, you could remove the need for a class entirely as long as you knew the entity id and component type desired.

But with that approach, you would need a unique retrieval mechanisms for each and every component type to be defined in a component manager/mapper, even if you reduced that class down to a global constant...

At that point you no longer have an agnostic framework though =/

Share on other sites

Well, hrm... now that i think of it, i could make the component be a mapping container, where it holds a type id and then another mapping id which would say which index I would want in a dedicated array for that data type. The component manager and mapper would just use another layer of lookup to get/set the data needed. I would need to use something like a switch to direct it to the correct retrieval mechanisms, but would this be faster than an interface?

Speed is not primary concern, at least not when it comes to design.

You talked about SQL-like mapping. Apply the same principle here. Take two systems, perhaps a physics one and perhaps a pickup trigger. We have this data:Vector3 position[]; Vector3 orientation[]; Vector3 velocities[]; TriggerRadius triggers[]; ItemTemplate templates[];Physics requires position, orientation and velocities. Quest trigger requires position triggers and templates.

The logic that manipulates these would be akin to select statement (SELECT position, orientation, velocities ... and SELECT position, triggers, templates ...).

There is no presecribed way to express such relation or how items are connected. At most basic level, you could indeed to this:class RigidBody { Vector3 position, orientation, velocities; };Then design listeners and whatnot to update these instances as they change. Then propagate them to pickup system, for example.

Alternatively, you can express all of these as pure code.void stepPhysics() { // do stuff with velocities, orientation, position } void checkForPickups() { for every item that has a trigger check for trigger radius if in_range of trigger use item based on template }

How to map these things? Whatever is most convenient. One might use 1:1 indices. So that position, orientation and velocity belong to same entity. For triggers, there might be a a separate map that contains indices mapping between needed values.

Such approach lends itself well to cases where there is many things and there is expected to be many things. It's not worth complicating for one-off objects.

For UI, we know we'll have many buttons and frames. We also know that each button has associated action. So perhaps something like this:Rect uiElements[]; delegate void UIAction(...); // note the abstract handler UIAction actions[]; void onClick(int x, int y) { find which element was clicked in uiElements, located at index i invoke corresponding handler }

At each level, there's absolute minimum amount of data stored. An UI element doesn't need to know its type at first pass, it's just a rectangle that can be matched to a click. Later, we can differentiate between buttons, listboxes, .... Also notice the use of "complex" delegate. On each click, we'll invoke one element (or perhaps a few on multitouch).

Now consider that we end up with many UI elements and clicking is slow. No problem, all we have is a bunch of rectangles. Maybe build a quad tree over them and use that to query. Also, for simplicity, examples use arrays. These can be any container, List<>, Map<> whatever is most suitable. For performance sensitive work, they'd be either aligned arrays or in C# arrays/lists of structs.

Share on other sites
Figure i would write a minor test case, just to see how much performance i am loosing using the most basic of interface usage. i wrote this to test:

 namespace InterfaceTest { class Program { static void Main(string[] args) { Test test = new Test(); test.runTest(); } } class Test { private IInterface interfaceClass = new ExtendedClass(); private NormalClass normalClass = new NormalClass(); private ExtendedClass extendedClass = new ExtendedClass(); private int test; private long interfaceStart, interfaceFinish, normalStart, normalFinish, extendedStart, extendedFinish; public void runTest() { interfaceStart = DateTime.Now.Ticks; for (int i = 0; i < 100000000; i++) { test = interfaceClass.getInt(); } interfaceFinish = DateTime.Now.Ticks; normalStart = DateTime.Now.Ticks; for (int i = 0; i < 100000000; i++) { test = normalClass.getInt(); } normalFinish = DateTime.Now.Ticks; extendedStart = DateTime.Now.Ticks; for (int i = 0; i < 100000000; i++) { test = extendedClass.getInt(); } extendedFinish = DateTime.Now.Ticks; Console.WriteLine("interface duration: " + (interfaceFinish - interfaceStart)); Console.WriteLine("normal duration: " + (normalFinish - normalStart)); Console.WriteLine("extended duration: " + (extendedFinish - extendedStart)); Console.Read(); } } } 

inteface:

 namespace InterfaceTest { interface IInterface { int getInt(); } } 

extended:

 namespace InterfaceTest { class ExtendedClass : IInterface { int i; public int getInt() { return i; } } } 

and normal

 namespace InterfaceTest { public class NormalClass { int i; public int getInt() { return i; } } } 

interface: 2964005
normal: 312000
extended: 312001

so, its roughly 9.5 times slower than a direct call. interesting.

edit: realized i had a type conversion from int to long in there, its fixed now.

Share on other sites
interestingly enough, using an abstract class vs an interface results in a about 1716003 ticks, or ~5.5x as long as a direct class call and an internal call costs the same as a direct class call. So when in doubt, go abstract over interface... Interesting things to ponder...

Share on other sites

interestingly enough, using an abstract class vs an interface results in a about 1716003 ticks, or ~5.5x as long as a direct class call and an internal call costs the same as a direct class call. So when in doubt, go abstract over interface... Interesting things to ponder...

I don't really want this to be about synthetic microbenchmarking, it's more about showing what happens if you turn your world completely upside down and throw away all existing notions of how a solution should be structured.

There's also another test that is missing above: public class NormalClass { int i; public void testInt(int n) { for (int i = 0; i < n; i++) { test = i; } } } 
It's considerably more representative of real world difference. Original test is a NOP, so it's measuring the overhead of pointless assignment. The changed example moves the separation line from outside of interface into implementation itself.

Ironically, this version is better OO than other tests. Getters break encapsulation.

Share on other sites

I use pointers to the base component class to work with derived component classes. I only use RTTI casting when a base class pointer needs to access a member of the derived class. My initial reason for this approach was make my life easier wrapping objects from 3rd Party libraries like Ogre::Mesh into a Component. A Derived Component inherits from both the Base Component and Ogre::Mesh Classes or contains members to work Ogre::Meshes directly. I'm relatively new to C++ and still trying to wrap my head around a components implementation.

A cleaner solution would be to treat the ogre mesh as a property of your derived component and through an interface, keeps the Ogre API out of the ECS framework.

 /* Base mesh object */ class IMeshObject { }; /* Ogre mesh object */ class OgreMeshObject : public IMeshObject { }; /* CryEngine mesh object */ class CryMeshObject : public IMeshObject { }; /* Base component */ class IComponent { }; /* Renderable Component */ class IRenderComponent : public IComponent { }; /* Mesh component */ class MeshComponent : public IRenderComponent { public: void setMeshObject(IMeshObject* meshObject); IMeshObject* getMeshObject(); }; 

If you wanted to keep it simple and refactor later, you could avoid wrapping the ogre API if you wanted and simply use get/set methods for the Ogre::MeshPtr object; however, I certainly would not inherit from any render API classes when creating my ECS components.

Share on other sites

It also has the problem of enforcing the there can only be one philosophy onto the user -- each entity can only have one transform and one health component -- which may seem like a trivial burden at the time of writing, but at some point your users will find a reason for there to be two of something, and will be forced to construct ungracious work-arounds involving several entities cooperating to function as one "entity".

My current system uses said system without that restriction like so:

 template<class T> void getComponents(T *components[], int32 &n) const { n = 0; uint32 i; for (i = 0; i < componentList.size(); ++i) { if (componentList->asType<T>()) { components[n++] = static_cast<T *>(componentList); } } } 

I think you make a great point about the hidden dependencies though. Accessing the pointers directly is also faster than a linear search/hash table look up when it matters too.

• 43
• 11
• 17
• 11
• 13
• Similar Content

• Hello fellow devs!
Once again I started working on an 2D adventure game and right now I'm doing the character-movement/animation. I'm not a big math guy and I was happy about my solution, but soon I realized that it's flawed.
My player has 5 walking-animations, mirrored for the left side: up, upright, right, downright, down. With the atan2 function I get the angle between player and destination. To get an index from 0 to 4, I divide PI by 5 and see how many times it goes into the player-destination angle.

In Pseudo-Code:
angle = atan2(destination.x - player.x, destination.y - player.y) //swapped y and x to get mirrored angle around the y axis
index = (int) (angle / (PI / 5));
PlayAnimation(index); //0 = up, 1 = up_right, 2 = right, 3 = down_right, 4 = down

Besides the fact that when angle is equal to PI it produces an index of 5, this works like a charm. Or at least I thought so at first. When I tested it, I realized that the up and down animation is playing more often than the others, which is pretty logical, since they have double the angle.

What I'm trying to achieve is something like this, but with equal angles, so that up and down has the same range as all other directions.

I can't get my head around it. Any suggestions? Is the whole approach doomed?

Thank you in advance for any input!

• By devbyskc
Hi Everyone,
Like most here, I'm a newbie but have been dabbling with game development for a few years. I am currently working full-time overseas and learning the craft in my spare time. It's been a long but highly rewarding adventure. Much of my time has been spent working through tutorials. In all of them, as well as my own attempts at development, I used the audio files supplied by the tutorial author, or obtained from one of the numerous sites online. I am working solo, and will be for a while, so I don't want to get too wrapped up with any one skill set. Regarding audio, the files I've found and used are good for what I was doing at the time. However I would now like to try my hand at customizing the audio more. My game engine of choice is Unity and it has an audio mixer built in that I have experimented with following their tutorials. I have obtained a great book called Game Audio Development with Unity 5.x that I am working through. Half way through the book it introduces using FMOD to supplement the Unity Audio Mixer. Later in the book, the author introduces Reaper (a very popular DAW) as an external program to compose and mix music to be integrated with Unity. I did some research on DAWs and quickly became overwhelmed. Much of what I found was geared toward professional sound engineers and sound designers. I am in no way trying or even thinking about getting to that level. All I want to be able to do is take a music file, and tweak it some to get the sound I want for my game. I've played with Audacity as well, but it didn't seem to fit the bill. So that is why I am looking at a better quality DAW. Since being solo, I am also under a budget contraint. So of all the DAW software out there, I am considering Reaper or Presonus Studio One due to their pricing. My question is, is investing the time to learn about using a DAW to tweak a sound file worth it? Are there any solo developers currently using a DAW as part of their overall workflow? If so, which one? I've also come across Fabric which is a Unity plug-in that enhances the built-in audio mixer. Would that be a better alternative?
I know this is long, and maybe I haven't communicated well in trying to be brief. But any advice from the gurus/vets would be greatly appreciated. I've leaned so much and had a lot of fun in the process. BTW, I am also a senior citizen (I cut my programming teeth back using punch cards and Structured Basic when it first came out). If anyone needs more clarification of what I am trying to accomplish please let me know.  Thanks in advance for any assistance/advice.

• Hi , I was considering this start up http://adshir.com/, for investment and i would like a little bit of feedback on what the developers community think about the technology.
So far what they have is a demo that runs in real time on a Tablet at over 60FPS, it runs locally on the  integrated GPU of the i7 . They have a 20 000 triangles  dinosaur that looks impressive,  better than anything i saw on a mobile device, with reflections and shadows looking very close to what they would look in the real world. They achieved this thanks to a  new algorithm of a rendering technique called Path tracing/Ray tracing, that  is very demanding and so far it is done mostly for static images.
From what i checked around there is no real option for real time ray tracing (60 FPS on consumer devices). There was imagination technologies that were supposed to release a chip that supports real time ray tracing, but i did not found they had a product in the market or even if the technology is finished as their last demo  i found was with a PC.  The other one is OTOY with their brigade engine that is still not released and if i understand well is more a cloud solution than in hardware solution .
Would there  be a sizable  interest in the developers community in having such a product as a plug-in for existing game engines?  How important  is Ray tracing to the  future of high end real time graphics?

• Good day,

I just wanted to share our casual game that is available for android.

Description: Fight your way from the ravenous plant monster for survival through flips. The rules are simple, drag and release your phone screen. Improve your skills and show it to your friends with the games quirky ranks. Select an array of characters using the orb you acquire throughout the game.