Component Systems - Entity examples

Started by
12 comments, last by mightshade 14 years, 4 months ago
I'm currently writing a basic entity system (concentrating on the base classes for the ComponentSystem and the EntitySystem (resolving dependencies, etc...). This works like a charm in theory but while implementing, there were somethings I really couldn't solve with a component system yet: I want to create a space RTS (basically an equivalent of world in conflict) game that uses more "realistic" (as opposed to other games) physics. Can anyone give me some examples how to "glue" together entities that are usually together, but may be seperated as well? For example: Spaceship with several turrets/modules. I figure my ship consists of a physics component, graphics, etc/blablabla, but should those turrets be entities as well? I think they should because they have a graphical & physical component, but then again there's my problem. Those modules have additional mass (which may not be neglected in some cases) so they should somehow be bound to the "main" entity (ie. the spaceship). Do you have any ideas or preferably experience how to solve that kind of problem?
Advertisement
Quote:Original post by SiS-Shadowman
Can anyone give me some examples how to "glue" together entities that are usually together, but may be seperated as well?

You describe a hierarchy of entities. In your case, a ship entity is the parent of turret entities. You can implement this with a child entities component, which acts as a list of entities that should be attached to the parent. Add one to the main ship entity and make the turrets and modules children of the main ship. Rotation and position of the child entities then are measured relatively to the parent, for example.

Quote:Original post by SiS-Shadowman
Spaceship with several turrets/modules. I figure my ship consists of a physics component, graphics, etc/blablabla, but should those turrets be entities as well? I think they should because they have a graphical & physical component, but then again there's my problem. Those modules have additional mass (which may not be neglected in some cases) so they should somehow be bound to the "main" entity (ie. the spaceship).

Depends on your preferences and your component system model. If you allow more than one graphics component per entity, and your modules/turrets are just that, I think it's alright to save some memory and not make them entities; but if you strictly impose "one graphics only", you'd add child entities for all modules/turrets, of course.
For anything more complex, you have entities anyway. E.g. the turrets might rotate independently from the ship and fire. I don't recommend implementing that as a component, since an entity can already do it, and you'd only end up with two kinds of objects that implement the same behaviour.
So I could simply move entities from the "main" list of entities into a specific entity? I think I'm just not sure if this might be an elegant solution to the problem or not. After all, this component approach is there to not implement another hierarchy, but a system that decouples behavior into separate components. I have tried that hierarchy thing before, implemented as a tree (like you described), however it became pretty messy because I had to keep dynamic_cast'ing stuff (nodes in my previous approach), moving it, etc...

Isn't there something uncomplicated that would not introduce the same problems into a component system?
Quote:Original post by SiS-Shadowman
After all, this component approach is there to not implement another hierarchy, but a system that decouples behavior into separate components. I have tried that hierarchy thing before, implemented as a tree (like you described), however it became pretty messy because I had to keep dynamic_cast'ing stuff (nodes in my previous approach), moving it, etc...


Agreed. However, what I wrote about is a different kind of hierarchy which has nothing to do with the inheritance tree you mentioned. I should've been more specific. I did not propose a hierarchy of classes, but rather a hierarchy of entity objects which only tells which objects (temporarily) belong together in some way.

What you were basically asking for, if I understood you correctly, is some kind of "bones system", some way to tell "this object is part of that other object". That, too, forms a hierarchy. However, this is actually a desired one.

Perhaps I can explain it with another example. Say you have some smaller space ships, possessing attached gun turrets. These guns are "part of" the ship - there is no inheritance involved here, just the fact that the guns "somehow" belong to the ship, so they move together, get destroyed together, etc.
Now assume you have a bigger carrier ship, and several of your smaller ships can land on it. So now, the small ships belong to the carrier in the sense that they move together, since they are docked. But again, they just belong together temporarily, not by inheritance.

You have to express this hierarchy in some way, and what I proposed in my last post does just that. There are other solutions, of course; anything that applies to how to store tree structures does apply here, too.
Quote:Original post by mightshade
you'd add child entities for all modules/turrets, of course.
Of course, things aren't always that simple. For instance, if you are using a physics engine, then these relations often have to be joints, and the whole 'parent<-->child' notation doesn't work very well. Similarly in the fighter/carrier case, you need to make and break temporary attachments between docked fighters and the carrier.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Quote:Original post by mightshade
Agreed. However, what I wrote about is a different kind of hierarchy which has nothing to do with the inheritance tree you mentioned.


I didn't talk about an inheritance tree. I was talking about the dynamic part, ie. a Player would simply have a list of entities (called them nodes this time) that contained a gun-node, all items in the inventory, etc...
But this way I had to dynamic_cast those nodes all the time (like for example when a specific node has been removed, like the gun). I just didn't like the concept back then and it doesn't apply to me now either. However I don't see how I could express that concept of linked entities in a clean/simple fashion.
Quote:Original post by mightshade
Quote:Original post by SiS-Shadowman
After all, this component approach is there to not implement another hierarchy, but a system that decouples behavior into separate components. I have tried that hierarchy thing before, implemented as a tree (like you described), however it became pretty messy because I had to keep dynamic_cast'ing stuff (nodes in my previous approach), moving it, etc...


Agreed. However, what I wrote about is a different kind of hierarchy which has nothing to do with the inheritance tree you mentioned. I should've been more specific. I did not propose a hierarchy of classes, but rather a hierarchy of entity objects which only tells which objects (temporarily) belong together in some way.

What you were basically asking for, if I understood you correctly, is some kind of "bones system", some way to tell "this object is part of that other object". That, too, forms a hierarchy. However, this is actually a desired one.

Perhaps I can explain it with another example. Say you have some smaller space ships, possessing attached gun turrets. These guns are "part of" the ship - there is no inheritance involved here, just the fact that the guns "somehow" belong to the ship, so they move together, get destroyed together, etc.
Now assume you have a bigger carrier ship, and several of your smaller ships can land on it. So now, the small ships belong to the carrier in the sense that they move together, since they are docked. But again, they just belong together temporarily, not by inheritance.

You have to express this hierarchy in some way, and what I proposed in my last post does just that. There are other solutions, of course; anything that applies to how to store tree structures does apply here, too.


Agreed

Quote:
Of course, things aren't always that simple. For instance, if you are using a physics engine, then these relations often have to be joints, and the whole 'parent<-->child' notation doesn't work very well. Similarly in the fighter/carrier case, you need to make and break temporary attachments between docked fighters and the carrier.


Doubly agreed.

I am working on the same kind of game, i commented out my component system because im prepping the rest of the engine first.

The nice thing about physics in this kind of situation is that you have control over the "rules". Some things don't necessarily NEED to be functioning with the physics. I am just going to use reference points and "attach" my components (which are just other game objects) to my game objects. the key for me is making attachable things "game objects" like particle emitters, lights, and possible other generic type objects. When something is hit by a bullet or whatever and needs to break off, that can just be a function. My problem is that i need an editor just to attach things in the right spot. I use lua though and it is coming together pretty nicely.

For me the parent child thing really only applies to matrix transforms to maintain attachment, as well as the obvious mass that something adds to an object when it's attached.

It's still a work in progress but thats at least how i see doing it.

this is a good thread.

------------------------------

redwoodpixel.com

Quote:Original post by SiS-Shadowman
But this way I had to dynamic_cast those nodes all the time (like for example when a specific node has been removed, like the gun). I just didn't like the concept back then and it doesn't apply to me now either. However I don't see how I could express that concept of linked entities in a clean/simple fashion.
dynamic_cast automatically means that you are in fact using inheritance.

Why do you feel that Gun and InventoryItem need to be distinct types? The whole point of an entity/component system is that everything is the same class, Entity, with various components, i.e. Damage or SpaceTakeInInventory, that define their behaviour.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

I've made quite a bit of progress on mine so i'll share it with you. I'm abreviating everything because i'm typing it from memory. It's not perfect, i'm still in the process of refactoring a ton of core test code into an actual engine.

typedef boost::shared_ptr p;struct Entity : public ComponentContainer{  p&lt;Engine&gt; E;  p&lt;Scene&gt; S;    //base components, expected to be here and not in list  p&lt;Physics::BaseObject&gt; Phys;  p&lt;Graphics::Renderable&gt; Rend;  p&lt;Brain&gt; Brain;  virtual void Unregister()   {     if (S && Phys) S-&gt;Remove(Phys);    if (S && Rend) S-&gt;Remove(Rend);  }};static p&lt;Entity&gt; CreateCharacter(p&lt;Engine&gt; E, p&lt;Scene&gt; S  /*from data file here*/){  p&lt;Entity&gt; newEnt(new Entity(E, S));  S-&gt;Add(newEnt);  newEnt-&gt;Phys = CreateCharacterPhysicsObject(blah);  S-&gt;Add(newEnt-&gt;Phys);  newEnt-&gt;Rend = CreateCharacterModel(blah);  S-&gt;Add(newEnt-&gt;Rend);  //these are really shared ptrs again but i'm too lazy to type it  newEnt-&gt;AddComponent(new InputMap(E-&gt;InputManager));  newEnt-&gt;AddComponent(new VehiclePassenger);  p&lt;WeaponInventory&gt; w(new WeaponInventory);  w-&gt;Add(CreateRifle(E,S));  newEnt-&gt;AddComponent(w);  //todo health and damage  newEnt-&gt;Brain = CreateCharacterBrain(blah);  //entity is automatically checked for a brain when scene is updated  return newEnt;}static p&lt;Entity&gt; CreateRifle(p&lt;Engine&gt; E, p&lt;Scene&gt; S  /*from data file here*/){  p&lt;Entity&gt; newEnt(new Entity(E, S));  S-&gt;Add(newEnt);  newEnt-&gt;Rend = CreateRifleModel(blah);  S-&gt;Add(newEnt-&gt;Rend);  p&lt;EffectsComponent&gt; fx(new EffectsComponent);  fx-&gt;Add(CreateMuzzleFlash(E, S));  fx-&gt;Add(CreateMuzzleSmoke(E, S));  newEnt-&gt;AddComponent(fx);  return newEnt;}


K, getting tired of retyping code. Theres a lot that is not apparent here. Like how the brain internally will create a state machine and then check the entity for other components that it will use such as the input map and weapon inventory.

Also, these create functions are actually virtual members of an EntityDef class. That was you can pass around the "potential" to make an entity. For example, when adding the weapon to the weapon inventory, part of its initialization is to include an EntityDef for the shell. That way when you use the weapon it has a handle to the create function for spawning shells. When the shell create function is returned, the entity->Phys is used to set its spawn location.

[Edited by - bzroom on December 8, 2009 1:08:08 PM]
Here's a real example, for some reason i decided to have my Character inherit the brain and the entity.. theres absolutely no reason why so everywhere you see Character::Foo, should be CharacterBrain::Foo

and everywhere you see newEnt->someSpecificComponent other than the base types, it really shouldbe charBrain->someSpecificComponent.. i'm not too worried about that small fact though.

">Video
Pic

#include "Character.h" #include "CharacterGroundState.h"#include "CharacterMountedState.h"#include "CharacterMountTransitionState.h"#include "CharacterCam.h"#include "MountableComponent.h"#include "CameraComponent.h"#include "InputMapComponent.h"#include "WeaponInventory.h"#include "CollisionFlags.h"#include "Rifle.h"#include "EntityList.h"#include "LODEngine/Engine.h"#include "LODGraphics/Renderer.h"#include "LODGraphics/Camera.h"#include "LODGraphics/AnimationController.h"#include "LODGraphics/Model.h"#include "LODGraphics/Scene.h"#include "LODGraphics/ModelInstance.h"#include "LODGraphics/Geometry.h"#include "LODPhysics/World.h"#include "LODPhysics/Character.h"#include "LODInput/InputManager.h"#include "LODResources/ResourceManager.h"#include "LODCore/DebugMenu.h"#include "LODMath/Matrix.h"using namespace LOD::Graphics;using namespace LOD::Resources;using namespace LOD::Math;DEBUG_BOOL(Character_Draw_Basis, false);DEBUG_BOOL(Character_Draw_Bones, false);void FillMatrixLines(Lines *lines, Matrix *bones, int cnt, Matrix &basis){	float size = 0.05f;	lines-&gt;SetActiveLineCount(0);	for(int b = 1; b &lt; cnt; ++b)	{		Vector p = bones * basis * Vector(0,0,0,1);		if (b &lt; cnt-1) lines-&gt;PushLine(p, bones[b+1] * basis * Vector(0,0,0,1), Color::Yellow);		lines-&gt;PushLine(p, bones * basis * Vector(size,0,0,1), Color::Red);		lines-&gt;PushLine(p, bones * basis * Vector(0,size,0,1), Color::Green);		lines-&gt;PushLine(p, bones * basis * Vector(0,0,size,1), Color::Blue);	}}namespace LOD{	namespace Gameplay	{		boost::shared_ptr&lt;ResourceGetter&gt; CharacterDef::GetResources(boost::shared_ptr&lt;ResourceManager&gt; &rm /*pass in declaration file here*/)		{			//these will be read from a file at this point			boost::shared_ptr&lt;ResourceGetter&gt; getter(new ResourceGetter);			getter-&gt;Add(rm-&gt;GetResource&lt;Model&gt;(ResourceKey("Models/urban.xml")));			getter-&gt;Add(CharacterGroundState::GetResources(rm));			getter-&gt;Add(CharacterMountedState::GetResources(rm));			getter-&gt;Add(CharacterMountTransitionState::GetResources(rm));						boost::shared_ptr&lt;RifleDef&gt; rifleDef(new RifleDef());			getter-&gt;Add(rifleDef-&gt;GetResources(rm));			return getter;		}		boost::shared_ptr&lt;Entity&gt; CharacterDef::Create(boost::shared_ptr&lt;Engine&gt; &e, boost::shared_ptr&lt;Graphics::Scene&gt; &s)		{ 			boost::shared_ptr&lt;Character&gt; newEntity(new Character);			newEntity-&gt;MainBrain = newEntity;			newEntity-&gt;MyEngine = e;			e-&gt;m_Entities-&gt;Add(newEntity);			//create graphics			ResourceHandle&lt;Model&gt; model = e-&gt;m_Resources-&gt;GetResource&lt;Model&gt;(ResourceKey("Models/urban.xml"));			model-&gt;PrepareForAnimation();			boost::shared_ptr&lt;BasicAnimationController&gt; animation(new BasicAnimationController);			newEntity-&gt;Animation = animation;			animation-&gt;SetBones(model-&gt;GetBones());			newEntity-&gt;Renderable.reset(new ModelInstance(model, animation));			s-&gt;AddRenderable(newEntity-&gt;Renderable);			//create physics			Physics::CharacterCreationParams p(0.5f, 2.0f, 0.3f, Matrix(Vector(0,20,0)), BIT(COLLIDE_CHARACTER), collidesWith[COLLIDE_CHARACTER]);			newEntity-&gt;character.reset(new Physics::Character());			newEntity-&gt;Physics = newEntity-&gt;character;			newEntity-&gt;character-&gt;Create(p);			newEntity-&gt;character-&gt;DependentTransform = &newEntity-&gt;Renderable-&gt;Transform;			e-&gt;m_Physics-&gt;AddCharacter(newEntity-&gt;character);			//vehicle component			boost::shared_ptr&lt;MountPassenger&gt; passComp(new MountPassenger);			newEntity-&gt;AddComponent(passComp);			passComp-&gt;PotentialMountables.Results.reserve(10);			//camera component			newEntity-&gt;camComp.reset(new CameraComponent(&e-&gt;m_Renderer-&gt;GetCurrentViewable()-&gt;SceneCamera));			newEntity-&gt;AddComponent(newEntity-&gt;camComp);			//input component			newEntity-&gt;input.reset(new InputMapComponent(e-&gt;m_Input));			newEntity-&gt;AddComponent(newEntity-&gt;input);			//weapons component			newEntity-&gt;weapons.reset(new WeaponInventory(newEntity));			newEntity-&gt;AddComponent(newEntity-&gt;weapons);			boost::shared_ptr&lt;Weapon&gt; rifle(new Weapon);			boost::shared_ptr&lt;RifleDef&gt; rifleDef(new RifleDef());			rifle-&gt;weaponEntity = rifleDef-&gt;Create(e, s);			rifle-&gt;shellEntityDef.reset(new RifleShellDef);			rifle-&gt;muzzleAttachPtID = 1;			rifle-&gt;shellAttachPtID = 2;			newEntity-&gt;weapons-&gt;AddWeapon(rifle);			newEntity-&gt;weapons-&gt;AddSlot(1); //attach pt id			newEntity-&gt;weapons-&gt;Equip(0, 0);			//state machine			newEntity-&gt;stateMachine.reset(new State);			newEntity-&gt;groundState.reset(new CharacterGroundState(e, newEntity));			State::AddState(newEntity-&gt;stateMachine, newEntity-&gt;groundState);			boost::shared_ptr&lt;CharacterMountedState&gt; cms(new CharacterMountedState(e, newEntity));			State::AddState(newEntity-&gt;stateMachine, cms);			boost::shared_ptr&lt;CharacterMountTransitionState&gt; cmts(new CharacterMountTransitionState(e, newEntity));			State::AddState(newEntity-&gt;stateMachine, cmts);			newEntity-&gt;stateMachine-&gt;Start(CharacterGroundState::GetTypeIDStatic());			//debugging			newEntity-&gt;lines = new Lines(100, NULL);			newEntity-&gt;lines-&gt;SetActiveLineCount(0);			newEntity-&gt;lines-&gt;Material = e-&gt;m_Resources-&gt;GetResource&lt;Material&gt;(ResourceKey("Materials/debug.xml"));			boost::shared_ptr&lt;Lines&gt; li(newEntity-&gt;lines);			s-&gt;AddRenderable(li);			return newEntity;		}				bool Character::PreUpdate(float dt)		{			input-&gt;Update(dt);			stateMachine-&gt;PreUpdate(dt);			return true;		}				bool Character::PostUpdate(float dt)		{			camComp-&gt;Update(dt);			stateMachine-&gt;PostUpdate(dt);			weapons-&gt;Update(dt);			if (Character_Draw_Bones)			{				FillMatrixLines(lines, Animation-&gt;GetBonePallet(), Animation-&gt;GetBoneCount(), character-&gt;GetTransform());			}			return true;		}	}}


*it'd be cool if the source tags didnt break the forum layout....

This topic is closed to new replies.

Advertisement