Jump to content

  • Log In with Google      Sign In   
  • Create Account

We're offering banner ads on our site from just $5!

1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Outboard component-based entity system architecture


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
106 replies to this topic

#21 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 10 September 2007 - 11:52 AM

One limitation of the design is that components are limited in the amount of state they can hold. Since they persist only when the Entity is in the World, they can't be seeded with any state as part of the creation process. Any data that the Component starts out with has to come from the Entity's preexisting state.

Sponsor:

#22 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 12:00 PM

Quote:
Original post by Sneftel
Remember that things can be set up such that the creation of a PhysicsComponent is the result of the creation of an AnimationComponent, not the creation of an Entity. So the AnimationComponent is available to PhysicsComponent at creation time, and Entities without AnimationComponents aren't visible to the PhysicsComponent. (Of course, if you wanted the PhysicsComponent to be able to attach to entities without AnimationComponents, you'd listen to World instead of AnimationSubsystem, and on Entity addition simply ask AnimationSubsystem if it had a component for that Entity.)

Ah yes, you mentioned about pairing components in your first post.
I'm still not too keen about the PhysicsComponent knowing about the AnimationComponent. IMO in the ideal component based system no component would rely on another.
You said that the PhysicsComponent feeds blending data to the AnimationComponent, is that were you stop the dependency chain?
A theoretical worst case scenario could be:
CollisionComponent -> PhysicsComponent -> AnimationComponent -> ScriptComponent -> UserInputComponent -> entity.setPosition(x, y, z);

If you wanted an entity without a ScriptComponent for example, then how does the AnimationComponent detect this so that it can feed its data into the UserInputComponent instead?

Alternatively you could have each component interact with a common block of state data - The entity itself is a good candidate for this, and infact you seem to imply you do something similar with positional data.
With this in mind the PhysicsComponent would cache it's output within the entity, the next component will access the data, blend it with it's output and then write the result back to the entity and so on. After each frame the cached data is set to some identity value.


Quote:
Personally, I have my doubts about the "blizzard of events" approach. I don't believe that it necessarily promotes loose coupling; on the contrary, I think it can promote strong coupling, by obfuscating the relationships between components.

Agreed.
Like the way that I don't want my component to be too coupled to each other I don't especially want them tightly coupled with the event system either.

Quote:
Quote:
Presumably there is still a factory of some description for informing (or firing off the adequate events to) each relevant subsystem that an entity requires a representitive for?
Sort of. That happens when the entity is added to the World, not when it is created; so the creation of Components is separated from the creation of Entities. The World contains an event for Entity adding, which each subsystem hooks to.

If subsystems just respond to an EntityAdded event then won't each entity will end up with every type of component whether it wanted it or not?
Is there some way to pick and choose with subsystems will respond to the event? I'm thinking it might be data-driven and accessible through the EntityAdded event itself so the event is queried by the subsystem as to whether it should respond?

Quote:
One thing I've considered, though, is making Subsystem (renamed, probably) into a superclass, since there's a decent amount of bookkeeping code there.

Just to throw in a thought, depending on the type of bookkeeping you're referring to, which makes the most sense: An is-a relationship via inheritance or a uses-a relationship via composition/aggregation.

#23 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 12:07 PM

Quote:
Original post by Sneftel
One limitation of the design is that components are limited in the amount of state they can hold. Since they persist only when the Entity is in the World, they can't be seeded with any state as part of the creation process. Any data that the Component starts out with has to come from the Entity's preexisting state.


I'm not all too clear on how you specifically manage events et-al, but would it be possible to pass this state within the EntityAdded event, will the subsystem recieve this event and so could then seed the component?
It does make the event itself rather a heavy-weight with it carrying all that data.

#24 LoneDwarf   Members   -  Reputation: 276

Like
0Likes
Like

Posted 10 September 2007 - 12:12 PM

Quote:
Original post by dmatter
Quote:
Original post by Sneftel
Remember that things can be set up such that the creation of a PhysicsComponent is the result of the creation of an AnimationComponent, not the creation of an Entity. So the AnimationComponent is available to PhysicsComponent at creation time, and Entities without AnimationComponents aren't visible to the PhysicsComponent. (Of course, if you wanted the PhysicsComponent to be able to attach to entities without AnimationComponents, you'd listen to World instead of AnimationSubsystem, and on Entity addition simply ask AnimationSubsystem if it had a component for that Entity.)

Ah yes, you mentioned about pairing components in your first post.
I'm still not too keen about the PhysicsComponent knowing about the AnimationComponent. IMO in the ideal component based system no component would rely on another.


In my system I handle these problems at construction time. All components are added to the object then coms can do discovery when onConstruct() is called on the object. For example, I might have a ComCamera1st that follows the head of the ComSkinMesh3d, if ComSkinMesh3d isn't found then it looks for a ComPhysics and if that isn't found it looks for a ComStaticFrame etc... I really can't see how else to do this sort of thing. Coms that do anything non-trivial pretty much have to talk to other coms.



#25 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 12:30 PM

Quote:
Original post by Tesshu
In my system I handle these problems at construction time. All components are added to the object then coms can do discovery when onConstruct() is called on the object. For example, I might have a ComCamera1st that follows the head of the ComSkinMesh3d, if ComSkinMesh3d isn't found then it looks for a ComPhysics and if that isn't found it looks for a ComStaticFrame etc... I really can't see how else to do this sort of thing. Coms that do anything non-trivial pretty much have to talk to other coms.

Perhaps that behaviour belongs elsewhere, such as the scene-graph, rather than the component system. You would have a camera node and attach to it an entity.

#26 LoneDwarf   Members   -  Reputation: 276

Like
0Likes
Like

Posted 10 September 2007 - 12:36 PM

Quote:
Original post by dmatter
Quote:
Original post by Tesshu
In my system I handle these problems at construction time. All components are added to the object then coms can do discovery when onConstruct() is called on the object. For example, I might have a ComCamera1st that follows the head of the ComSkinMesh3d, if ComSkinMesh3d isn't found then it looks for a ComPhysics and if that isn't found it looks for a ComStaticFrame etc... I really can't see how else to do this sort of thing. Coms that do anything non-trivial pretty much have to talk to other coms.

Perhaps that behaviour belongs elsewhere, such as the scene-graph, rather than the component system. You would have a camera node and attach to it an entity.


I don't see that as much of a solution since you still need to follow the head position.

#27 Sirisian   Crossbones+   -  Reputation: 1772

Like
0Likes
Like

Posted 10 September 2007 - 12:41 PM

Cool, I've always used a component system for game entities. I figured I wasn't the only one. I just treat game entities like I would my GUI. I create the basic components and the basic events. When an event affects a top level component (usually a container) it passes the events down to the components that are registered to them. Along with this components register themselves to be updated and are able to run event easily.

My event system is rather different in that I use a packet. Event ID followed by parameters serialized. So when an event gets activated it just passes the packet to the event manager which uses a FSM to break apart the packets from front to end.

It's much easier to the think of a tank as a an object that once the player gets in just runs the "get in" event and registers events to the keys and mouse. Very flexible at least.

Not sure how you guys handle the child components. But I give put them in an associative array which allows them to be accessed via their names. Plane2.GetComponent["weapon1"].GetProperty["ammo"]. Get's complex I guess, but for some reason I don't really see any speed decreases so far.

#28 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 12:51 PM

Quote:
Original post by Tesshu
Quote:
Original post by dmatter
Quote:
Original post by Tesshu
In my system I handle these problems at construction time. All components are added to the object then coms can do discovery when onConstruct() is called on the object. For example, I might have a ComCamera1st that follows the head of the ComSkinMesh3d, if ComSkinMesh3d isn't found then it looks for a ComPhysics and if that isn't found it looks for a ComStaticFrame etc... I really can't see how else to do this sort of thing. Coms that do anything non-trivial pretty much have to talk to other coms.

Perhaps that behaviour belongs elsewhere, such as the scene-graph, rather than the component system. You would have a camera node and attach to it an entity.


I don't see that as much of a solution since you still need to follow the head position.


Well it depends on exactly what you need, but a simple solution might be:
ROOT
|
\---Group Transform Node
|
\---Camera
|
\---SkinnedMesh

The camera is set up to hover about 4 meters above ground and to look-at the point where the mesh's head is known to be. If the transform node changes then the camera and the mesh both move together thus the camera follows the players head.

IMO, a camera isn't really a good candidate for the component system.

#29 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 10 September 2007 - 01:17 PM

Quote:
Original post by dmatter
I'm still not too keen about the PhysicsComponent knowing about the AnimationComponent. IMO in the ideal component based system no component would rely on another.
You said that the PhysicsComponent feeds blending data to the AnimationComponent, is that were you stop the dependency chain?
A theoretical worst case scenario could be:
CollisionComponent -> PhysicsComponent -> AnimationComponent -> ScriptComponent -> UserInputComponent -> entity.setPosition(x, y, z);
That seems rather contrived. The only time a component depends on a different component is when it requires that component's services. With that said, relationship chains like this are in the realm of bad OOP design in general, not specific to this architecture. Deciding that no component should depend on any other component because it makes chains like this possible would be throwing the baby out with the bathwater.

Quote:
If you wanted an entity without a ScriptComponent for example, then how does the AnimationComponent detect this so that it can feed its data into the UserInputComponent instead?
I'm not sure what you're asking here... though I also have no idea what AnimationComponent would have to say to UserInputComponent.

Quote:
Alternatively you could have each component interact with a common block of state data - The entity itself is a good candidate for this, and infact you seem to imply you do something similar with positional data.
Yes, this is the system as it stands. The only time components interact with other components is to carry out a specific action.

I think you have the wrong idea about how common component-component relationships are in my design. The physics/animation thing is literally the only place it happens. Component->outside subsystem relationships are much more common.

Quote:
Quote:
Presumably there is still a factory of some description for informing (or firing off the adequate events to) each relevant subsystem that an entity requires a representitive for?
Sort of. That happens when the entity is added to the World, not when it is created; so the creation of Components is separated from the creation of Entities. The World contains an event for Entity adding, which each subsystem hooks to.

If subsystems just respond to an EntityAdded event then won't each entity will end up with every type of component whether it wanted it or not?[/quote]
The entity certainly doesn't decide whether it gets these components or not. It's not responsible for them. The subsystem can choose to create a component or to ignore the addition event completely, based on its appraisal of whether that particular entity is interesting to it or not.

Quote:
Just to throw in a thought, depending on the type of bookkeeping you're referring to, which makes the most sense: An is-a relationship via inheritance or a uses-a relationship via composition/aggregation.
It's a very good point, and in a more civilized language composition would clearly be the way to go. In C++, however, the lack of lexically scoped inner classes make this sort of thing extremely annoying to do without direct inheritance.

#30 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 02:02 PM

Quote:
Original post by Sneftel
The only time a component depends on a different component is when it requires that component's services.
Quote:
I'm not sure what you're asking here... though I also have no idea what AnimationComponent would have to say to UserInputComponent.
Quote:
I think you have the wrong idea about how common component-component relationships are in my design. The physics/animation thing is literally the only place it happens. Component->outside subsystem relationships are much more common.

Yes, I hadn't realised this was the only occurance.

My understanding had been that the PhysicsComponent produces data affecting, for example, the entity's position; the AnimationComponent also has data affecting the position, thus to get the final net-position the PhysicsComponent passes its data directly to the AnimationComponent which then also has the responsibility of calculating the overal net-position.
I was only taking that to one (albeit contrived) situation where any component which contributes to the net-position needed to also be dependant on other components, which kind of defeats their purpose.

However as this is apparently a minority occurance then you wouldn't overly benefit from separating them, and it's hardly a big problem [smile].

Quote:
Quote:
If subsystems just respond to an EntityAdded event then won't each entity will end up with every type of component whether it wanted it or not?

The entity certainly doesn't decide whether it gets these components or not. It's not responsible for them. The subsystem can choose to create a component or to ignore the addition event completely, based on its appraisal of whether that particular entity is interesting to it or not.

I was a bit misleading there, I didn't mean to imply that the entities choose which components they're comprised of. You did give the answer I wanted though; that there is some mechanism to control which subsystems will respond to a new-entity event.

Quote:
Quote:
Just to throw in a thought, depending on the type of bookkeeping you're referring to, which makes the most sense: An is-a relationship via inheritance or a uses-a relationship via composition/aggregation.
It's a very good point, and in a more civilized language composition would clearly be the way to go. In C++, however, the lack of lexically scoped inner classes make this sort of thing extremely annoying to do without direct inheritance.
[grin]

#31 LoneDwarf   Members   -  Reputation: 276

Like
0Likes
Like

Posted 10 September 2007 - 02:05 PM

Quote:
Original post by dmatter
Quote:
Original post by Tesshu
Quote:
Original post by dmatter
Quote:
Original post by Tesshu
In my system I handle these problems at construction time. All components are added to the object then coms can do discovery when onConstruct() is called on the object. For example, I might have a ComCamera1st that follows the head of the ComSkinMesh3d, if ComSkinMesh3d isn't found then it looks for a ComPhysics and if that isn't found it looks for a ComStaticFrame etc... I really can't see how else to do this sort of thing. Coms that do anything non-trivial pretty much have to talk to other coms.

Perhaps that behaviour belongs elsewhere, such as the scene-graph, rather than the component system. You would have a camera node and attach to it an entity.


I don't see that as much of a solution since you still need to follow the head position.


Well it depends on exactly what you need, but a simple solution might be:
ROOT
|
\---Group Transform Node
|
\---Camera
|
\---SkinnedMesh

The camera is set up to hover about 4 meters above ground and to look-at the point where the mesh's head is known to be. If the transform node changes then the camera and the mesh both move together thus the camera follows the players head.

IMO, a camera isn't really a good candidate for the component system.


I guess this is a perfect example of how it's not always easy to determine what should be in the com system or not :)

I have a Sim_ComFollowBone that, well follows a bone on a skinned model. So I can use this com to put a weapon in a hand, make a halo appear over a head or make a camera follow a bone. Keep in mind that some of my coms, such as Sim_ComCamera, are really wrappers for more lower level systems. I really can't see why you wouldn't want it to be com. One of the features of the system is sharing code. Using this method I can create many different types of objects that can be used as a camera.




#32 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 02:32 PM

Quote:
Original post by Tesshu
I guess this is a perfect example of how it's not always easy to determine what should be in the com system or not :)

I have a Sim_ComFollowBone that, well follows a bone on a skinned model. So I can use this com to put a weapon in a hand, make a halo appear over a head or make a camera follow a bone. Keep in mind that some of my coms, such as Sim_ComCamera, are really wrappers for more lower level systems. I really can't see why you wouldn't want it to be com. One of the features of the system is sharing code. Using this method I can create many different types of objects that can be used as a camera.


It is certainly possible to have such things in either the component system or the scene-graph system and I'm sure there are pros and cons to both.

The way I tackle it is to have BoneSockets in the scenegraph that reference a specific bone in a skeleton so that other scene-graph entities can be attached to it:

ROOT
|
\---Group Transform Node
|
\---Camera
|
\---SkinnedMesh
|
\---RHandBoneSocket
| |
| \---SkinnedMesh (Weapon)
| |
| \---ParticleSystem
|
\---LAnkleBoneSocket
|
\---SkinnedMesh (Annoying clingy person that won't let go)


It's been on my todo list for some time to ponder whether it's possible to combine the scene-graph and component system together into a single concept, clearly there is cross-over between them. Failing that, I'd atleast like to change the paradigm of my current scene-graph, those BoneSockets were just an ad-hoc means to an end, I'd like to see a more elegant solution in my next code iteration.

#33 Sirisian   Crossbones+   -  Reputation: 1772

Like
0Likes
Like

Posted 10 September 2007 - 02:53 PM

I realize this was said a while ago:
Quote:
Original post by Sneftel
One limitation of the design is that components are limited in the amount of state they can hold. Since they persist only when the Entity is in the World, they can't be seeded with any state as part of the creation process. Any data that the Component starts out with has to come from the Entity's preexisting state.

Not sure what you mean there. Could you explain that?

I am really curious how you guys do your event systems. I go for a soft coded method that's very flexible at run-time (to the point scripts could be changed and loaded in at run-time to add events). I mean my event system revolves around simple input to output. Every form of input has a way to be linked to an event to produce any output.

A good example is that having a packet allows you to produce extremely dynamic effects. Like clicking on an object can run a packet ID that spawns a GUI window with choices and fills in event ID's and information dynamically. Good for interacting with the world.

I figure you guys do something similar would I be correct in saying that?

A big part is passing down the events to the registered componenents that have focus over the input. (Again thinking of the game entities like a GUI is a perfect way to look at it).

#34 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 10 September 2007 - 03:28 PM

Quote:
Original post by Sirisian
I realize this was said a while ago:
Quote:
Original post by Sneftel
One limitation of the design is that components are limited in the amount of state they can hold. Since they persist only when the Entity is in the World, they can't be seeded with any state as part of the creation process. Any data that the Component starts out with has to come from the Entity's preexisting state.

Not sure what you mean there. Could you explain that?

Consider that a few months down the line, I decide that every Entity should have a WeightComponent, which tracks the entity's weight, increasing as it overeats and decreasing as it diets and exercises regularly. Suppose that previous to this, the Entity had no concept of weight or mass at all. The difficulty I would run into (assuming that I stored the weight in the WeightComponent) is that since Entity has no member called "weight", I would have no way of knowing what its initial weight should be. Likewise, if for some reason I temporarily removed a particular Entity from the simulation and then added it back in, the weight would no longer be the same; the original WeightComponent would have been destroyed and a new one created. What this all means is that outboard components aren't useful for adding state, only for adding behavior.

#35 Sirisian   Crossbones+   -  Reputation: 1772

Like
0Likes
Like

Posted 10 September 2007 - 04:13 PM

The way I have mine set up is that if I wanted I can use any types in the properties. I plan to revise my system more so that it will allow most properties, but under the circumstance that a component needs another property then it could be added dynamically to the object. Albeit the access of that property would have a little more overhead, but not enough that it wouldn't be useful.

I'd end up accessing it in my events. So like the onload event I might have to go through each object that needs it and edit it so that the new variable is created and able to be accessed (via the map of properties).

For a large upgrade where it would require all entities to have the update then I'd just rebuild the engine with it.

Currently I just list out each component and event such to allow for the dynamic creation of "any" object. Breaking everything up into the simplest ideas is the hard part. I mean the actual actions aren't very important and are pretty much "scripted" via event packets. It's the variables and rendering/boundary/data type info that tends to change. I work in 2D, so the implementation is probably different for 3D.

Like say I was making a tank. You'd break it down into the base, the two treads, 4 particle emitters for the front and back treads, the turret with the barrel, and the particle emitter at the end of the barrel.

So you have 5 particle emitter components, 2 animations (treads, the barrel is a translation based on the firerate, but is just a texture), 1 vehicle component(base) and a rotating component (turret) and finally a texture outside of the tank where the player pushes g to get into the tank so 1 texture. So like 11 components. Not bad, but the interactivity between them is high. Like textures are basically 1 frame animations with a rotation point and beginning translation. The vehicle base has tons of event handlers that can register keybinded input to events as said before. The animations register with the main container since they may only update every 50-200 ms and such. Events tell the tread animations whether or not to play and the list goes on.

Also, for clarification when I said, "multiGameEntity" I mean a container. It's the root of the object to which all components register events. When events are handled they are passed to it for a quick cull of events so components don't worry about what they don't need.

Due to my insane FSM system though the basic premise is again a flexible dynamic entity creation system. (I should probably make an editor...).

Still wondering how you guys handle events for your system. Maybe there's a more flexible way than mine. (I am hardcoding after all).

#36 pancakedice   Members   -  Reputation: 159

Like
0Likes
Like

Posted 10 September 2007 - 09:31 PM

Quote:
Original post by Sneftel

Quote:
Quote:
Quote:
Presumably there is still a factory of some description for informing (or firing off the adequate events to) each relevant subsystem that an entity requires a representitive for?
Sort of. That happens when the entity is added to the World, not when it is created; so the creation of Components is separated from the creation of Entities. The World contains an event for Entity adding, which each subsystem hooks to.

If subsystems just respond to an EntityAdded event then won't each entity will end up with every type of component whether it wanted it or not?

The entity certainly doesn't decide whether it gets these components or not. It's not responsible for them. The subsystem can choose to create a component or to ignore the addition event completely, based on its appraisal of whether that particular entity is interesting to it or not.


Is that done with a dynamic_cast<> within the given subsysten? How would the subsystem determine which entity needs what?

#37 dmatter   Crossbones+   -  Reputation: 3255

Like
0Likes
Like

Posted 10 September 2007 - 10:06 PM

Quote:
Original post by dingojohn
Is that done with a dynamic_cast<> within the given subsysten? How would the subsystem determine which entity needs what?

What would you do with dynamic_cast?

A data-driven approach might see each subsystem with a string of meta-tags that it is interested in. When an entity is added to the world it too is given a string of meta-tags by the user.
Upon receiving an EntityAdded event, the subsystem queries the entity for it's meta-tags and searches it to see whether it's interested creating a representitive for this entity.

The meta-tags can be explicit: std::string("Physical, Spatial, Visual");
They can be implicit: std::string("Antelope");
Or a combination of both: std::string("Vehicle, AI"); -or- std::string("Vechicle, UserControl")

#38 pancakedice   Members   -  Reputation: 159

Like
0Likes
Like

Posted 11 September 2007 - 02:42 AM

Quote:
Original post by dmatter
Quote:
Original post by dingojohn
Is that done with a dynamic_cast<> within the given subsysten? How would the subsystem determine which entity needs what?

What would you do with dynamic_cast?

A data-driven approach might see each subsystem with a string of meta-tags that it is interested in. When an entity is added to the world it too is given a string of meta-tags by the user.
Upon receiving an EntityAdded event, the subsystem queries the entity for it's meta-tags and searches it to see whether it's interested creating a representitive for this entity.

The meta-tags can be explicit: std::string("Physical, Spatial, Visual");
They can be implicit: std::string("Antelope");
Or a combination of both: std::string("Vehicle, AI"); -or- std::string("Vechicle, UserControl")


I cannot believe I did not think of that. Thanks.

#39 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 11 September 2007 - 06:19 AM

Actually, dingo's hit on one of the other aspects of my Entity system: I am using inheritance to some degree. That is, there is a small but significant inheritance tree hanging off of Entity, and one of the main criteria subsystems use to decide whether they care about an Entity is that Entity's dynamic type (determined via dynamic_cast). This allows me to add extra properties to certain types of objects, at the cost of some of the flexibility that the standard component-based system allows.

#40 Negs   Members   -  Reputation: 122

Like
0Likes
Like

Posted 15 September 2007 - 11:31 PM

It sounds like I'm doing something that is somewhat different.

In data file for each different entity type that can be created I have a GameObjectType definition that contains a list of components and for each one a bunch of things that set variables in the component. A small example:

Monster
{
Physical
{
size = 4
}
Moving
{
speed = 10
}
}

Any variables you don't set get default values.
You can also derive from one of these classes

FastMonster : Monster
{
Moving { speed = 30 }
}


For each component I have something deriving from ComponentStatic and Component.

At load time when you parse these files for each type we create a list of ComponentStatic objects that belong to that type. A component static has functions for accepting variable definitions and a list of components that it requires services from. Moving for example requires Physical to be present (You need to be able to update the position of the object in order to move it). If you list a component and you don't include its dependency then the Type will fail to load and an error gets reported.

For each dependency you store a ComponentRef which is really just the index of the component for easy look up later.

A GameObjectType also remembers the sizeof for the non-static components corresponding to the static ones it contains and the sum of all of these.

So now each type of object we want has a ComponentStatic that contains dependancy indices and all of the type specific data that each component needs.

When we instantiate a GameObjectType as a GameObject we allocate all of the memory for all of the non static components in one call and use placement new to call of of the constructors for each game object. Each non static component has a reference to its static cousin so that it can easily access all of the object type specific data it needs.

The GameObject has a function that takes a ComponentRef and returns a pointer to the game object that you want. It does this by using a lookup table in the GameObjectType that contains the offsets of each Component for this type.

This may sound a little complex but it means that creating new objects is now pretty fast. You allocate all of the components in one go and this has the additional benefit that all of the components are located next to each other in memory to improve caching performance.

We also have a quick way of getting references to other components. All of the dependencies the component needs are stored as ComponentRef's so getting a pointer is just a constant time array lookup.

There is one other thing needed here though, and that is a way to publish messages to components that require info back from you. For example, you need a way of notifying any components that need to the fact that the object has stopped moving for example (required for animation).

I considered some kind of callback system and implemented some of it but it was fairly complex. So I settled with a much simpler solution. Each component has a ReceiveMessage virtual function and when a ComponentStatic is instantiated all of the components that it requires get an entry added to say that it is interested in messages from those components.

When a component wants to send a message it calls a function that will send messages to all of the other components that registered as listeners. So our animation controller lists the moving component as a dependancy and now it gets notified of any events that it needs to.

There are a few other issues I have not discussed here. The first one that is not yet implemented fully the ability to be able to load GameObjectTypes faster using a binary game object type format that contains a blob of binary data that each component can memcpy directly into a little inline struct during loading. You can compile all of the game object type files down into this format, number all of the components and then very quickly load a game object type. This is for distribution as it requires a locking down of the available components and you can't really edit the type files after that.

The other issue is network serialisation. I have a system that allows each component to be serialised and put into a network stream and come out whole on the other end, and a "dirty component" thing that allows components to say that they have changed and then only components that require changes to be serialised get sent (with a bitfield at the start to say which components the client needs to load.

That mechanism has a whole other list of strange things to do with Components being marked as Client, Server or Shared. Only Shared components get state serialisation and Client / Server components only actually exist on the sides that they need to.

All of the Shared components come first in the physical object to preserve that the component indices are always the same on both the client and server. (We don't send the indices in network traffic but its easier to get your head around sometimes)

I'm not totally happy with the networking part of the system honestly. It can be hard to say sometimes what should be serialised as part of the object state and what should be sent as a standard message packet. I've been considering some kind of Object -> Object network message thing but haven't implemented it yet. That would be so that the server can notify the client of events without actually sending it as object state.

Anyway, this ended up being a pretty long post. Any suggestions and comments welcome.






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS