Jump to content

  • Log In with Google      Sign In   
  • Create Account

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

#81 Thergothon   Members   -  Reputation: 160

Like
0Likes
Like

Posted 05 December 2007 - 05:13 AM

Can't you just do something like:


class observer {

public:

void notify(unsigned int event_type, entity* e);

};

class entity {

public:

void register_observer(unsigned int event_type, observer* o) {

m_observers[event_type]->push_back(o);

}

private:

std::map<unsigned int, std::list<observer*>*> m_observers;

};



Sure it's slightly oversimplified, but you could alter the map to be a map<unsigned int, map<observer*, void (*function)()> >, that way you get function pointers and only notify observers that care about certain event types. I'm probably overlooking something obvious though.

Sponsor:

#82 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 05 December 2007 - 06:22 AM

I really, really dislike the event_id + event_params + giant_switch_statement approach to event notification. It's brittle, it defeats compile-time checking, it's difficult to maintain, it leads to long-simmering bugs. If there are two different events, they should be different objects.


class Entity
{
public:
event<float> CharacterJumped; // float height
event<Color const&, PantsSize const&> CharacterBoughtJeans;


void Jump(float height)
{
...
CharacterJumped.Invoke(height);
}

void BuyJeans(Color const& color, PantsSize const& pantsSize)
{
...
CharacterBoughtJeans.Invoke(color, pantsSize);
}
}




The system I've shown here uses my custom event library, but there are plenty of others if you want something more widely adopted. The important thing is not to take the C route of tossing type-safety out the window the moment you step outside the procedural programming paradigm.

#83 HexDump   Members   -  Reputation: 219

Like
0Likes
Like

Posted 09 December 2007 - 12:04 AM

Just a thing. I´m following the subsystem abstract logic and component is just data idea.

I have wrote my first component RenderComponent, wich logic (I don´t have any lógic) is abstracted by RenderSubsytem. Subsystem also owns manages comopnent instances (creating and removing).

Now I would like to write a LightComponent, but I don´t know where to put them. Should I create a LightSubsystem (it seems a bit verbose), should I use the RenderSystem for it too? (I don´t see any logic in this components, I mean, RenderComponent, LightComponnet).

Any idea will be wellcomed, I´m a bit lost,
HexDump.

#84 Knarkles   Members   -  Reputation: 271

Like
0Likes
Like

Posted 09 December 2007 - 12:39 PM

Quote:
Original post by Lord_Evil
What about this approach:

The entity would be a container for data-components, i.e. a transformation component (position, orientation etc.), a visual component (mesh, textures etc.), ...
Subsystems take the role of behaviour-components but there's only a single instance. Entities are then processed in a loop which works on whatever list may be appropriate for the subsystem. Data that is needed per entity but only within the subsystem could be stored using the decorator pattern.


This is almost exactly what I used to do with my Python-based engine, though at the time I wasn't yet "enlightened" and tried to create a unified scene graph using this idea. It's actually quite elegant to implement in Python, since you can add attributes to objects dynamically, and overload the attribute-setting method.

I'm actually currently in the process of trying to design a new scene / entity / whatever management system for my game, whose engine is loosely based on my old engine. I have a couple of different ideas - all component-based - I want to design and evaluate further:

1. Very similar to what's described above, but on a higher level, ditching the unified scene graph idea.
2. Components define behaviour, while entities contain arbitrary components and their (possibly shared) data.
3. Entities are solely containers for components, while components contain both behaviour and data.

In all versions, communications would be done with signals (an implementation of the observer pattern with a twist), just the location of signals would change; either in the entity or in relevant components.


#85 Cycles   Members   -  Reputation: 187

Like
0Likes
Like

Posted 12 December 2007 - 08:20 AM

Hi, hopefully this thread hasn't died out too much.

I've just finished reading through the thread, and it sounds very interesting. I was aware of component based design before, but never thought about the idea of components attaching themselves, rather than explicitly attaching components to entities.

Recently I've been messing about making a little asteroids like game - for fun, seeing as I don't have much time now that I'm at uni. Now at the moment, it only has Asteroids that can fly around the screen and does not use a component based design - but I'd like to try and apply one now.

At the moment I have an Actor class which has position and velocity. Asteroid extends this Actor class, and adds a bit more information - Orientation, Health and Size.

Actors can also have controllers applied, and Asteroid has a ScreenWrappingController that makes sure that after the actor has updated (moved position by velocity) that the entity is within screen bounds.

As for rendering, I use a visitor pattern that visits everything that is IRenderable. When it is visiting an Asteroid, the visitor checks to see if it's drawn this Asteroid before. If it has /not/, it creates a quad on the fly (I know... should use batching, but that's another topic) and loads an Asteroid texture and forever on uses this quad to render with.

Now this is a rather simple example at the moment, but how would you tie all of this together? What I think, if I'm understanding correctly is: Asteroid is a composition of an ActorComponent (think SpatialComponent but with a bit logic?), and maybe a DamageComponent - and the actor component is controlled by a ScreenWrappingController (maybe later refactored to a BoundryRestrictingController). In terms of Rendering, the creation of an Asteroid causes an QuadGeometryComponent to be created, requesting an asteroid image from some type of ResourceManager (but only storing a somewhat weak reference to this).

I suppose now at render time a Visitor pattern is not required, here we would now just request the GraphicsSubsystem to render all of its components - but how does it know the location of these components? I understand that the SpatialSubsystem should listen to the GraphicsSubsystem to ensure the creation of a GraphicsComponent, so a component would definitley be created - but how does the QuadGeometryComponent actually get to this component, without becoming coupled with the SpatialSubsystem in some way?

I have a feeling this may be why people advocate simply throwing positional data into the Entity container...

Sorry if this post seems a bit of a ramble, I will hopefully be able to clean it up later with an edit :)

Oliver Charles (aka aCiD2) [ Cycles Blog ]

#86 SecurityException   Members   -  Reputation: 127

Like
0Likes
Like

Posted 12 December 2007 - 11:53 AM

I would be specifically interested in whether you also include very abstract concepts in your entity system, like a statistics object or a path planner, or do you use this system only for "real" game objects. While it might have some advantages to include everything, for example a GUI could react on a "take damage" message for a unit and display a warning, I think this might also get complicated.

I'm currently experimenting with such a system and would like to know what advantages or disadvantes of either approach you see. Thx!

#87 Cycles   Members   -  Reputation: 187

Like
0Likes
Like

Posted 12 December 2007 - 01:53 PM

Ok, I've gotten further with moving my design over to a component based one. Now I have a ResourceManager to request textures and geometry data. I have 2 main components at the moment: GeometryComponent, DamageComponent. GeometryComponent creates/requests the quad mesh and also loads the texture of my asteroid, and damage component is there to allow the asteroid to explode, whenever I get round to writing a particle system or something.

I also allow controllers to be placed on my Entity class, but I'm not sure this is the best approach. When creating my asteroids, this is what I'm doing:


for(int i = 0; i < amountOfAsteroids; i++) {
EntityDefinition asteroidDefinition = new EntityDefinition {
ComponentTypes = new List<Type> {
typeof(GeometryComponent),
typeof(DamageComponent)
},
Properties = new SpaceGame.Components.Properties()
};

Vector2 position = new Vector2( (float)(randomGenerator.NextDouble() * 800),
(float)(randomGenerator.NextDouble() * 600));
asteroidDefinition.Properties.Add("position", position);

Entity asteroid = CreateEntity(asteroidDefinition);
asteroid.Controller = new LambdaController(entity => entity.Position += new Vector2(1, 0));
}





Obviously my asteroids will do more than slide 1 unit to the left every second in the future, but this is just to test it works. But controller doesn't seem like it's going to be very useful in the future... I thought about making this some type of component, but then it wouldn't be very generic.

Also, how can I now enforce that the Entity is only within screen bounds? I think it may be best at the moment to write a controller specific to Asteroids - an asteroid moving controller that implements both movement, and screen wrapping - but this seems a little bit too complex for a small controller. The controller also needs some way to tell the renderer to render the mesh in 2+ locations to give the illusion that it's wrapping round the screen.



Edit:

Ok, so I've been playing around some more, and my self an Asteroid component and I think I like this a little more, but I'm still not 100% confident about it. The problem now is that the Asteroid component has data about how the Asteroid should be displayed - the rotation and scaling of the Asteroid.

Now Geometry components don't depend on Asteroid components, so creating an Asteroid when a Geometry component is created doesn't make sense. So the Geometry component can't really depend on the Asteroid at all, or it's useless for rendering say... items, nebulas, the players ship, etc.

I think the main, larger problem here is how can components co-ordinate an Entity's state? Every example I can contrive can be solved with events in some way, but for some reason I can't see a nice way to solve this problem.

I'll stop forum'ing now, and get some sleep, maybe the solution will come to me in the morning :)

Any ideas?

[Edited by - Cycles on December 12, 2007 8:53:50 PM]

Oliver Charles (aka aCiD2) [ Cycles Blog ]

#88 elFarto   Members   -  Reputation: 206

Like
0Likes
Like

Posted 12 December 2007 - 11:01 PM

I've been reading this thread hoping to learn something, but I think I'm more confused than when I started.

One thing I have noticed is the need for positional information in both the physics component and the rendering component. However no-one seems to have just merged the components and had the physics subsystem and the rendering subsystem act as database 'views' into the entity database (in my pseudo design my entities as basically HashMaps, and contain arbitrary State objects). Of course, I'm probably missing something important...

One thing I am confused about is how to control my space ship entity (it's click-to-move rather than direct control), since the physics system will control all movement, to get the ship to move you need to add a force somewhere. I just don't get how all the components fit together.

Regards
elFarto

#89 vicviper   Members   -  Reputation: 130

Like
0Likes
Like

Posted 13 December 2007 - 05:01 AM

hmm... don't you think this is going a bit too far? I mean, by now, we already have: entities, components, systems and subsystems, and with that, people is clearly having problems understanding mutual relationships between these different elements. I myself don't know very well in which direction to go, because, so far, many of the solutions presented here would not allow some things I want to do.

Remember, one of the theorical good things about componentized entities was to avoid the traditional, bloated hierarchical entities. And I think we should move in the direction to see how everything fits in a real scenario.

For example: many have said that only allow one instance of a given component type inside a single entity; but, given that a "weapon" would be a component, what if I want an entity to have multiple weapons?

In a game like Quake, I think a player entity could be broken in the next components:

-quake player entity
-spatial component (position, rotation)
-physics (velocities, collision detection) component
-user input component
-AI component
-3rd person character mesh component (mesh id, animation frame)
-3rd person weapon mesh component ( mesh id, character mesh bone)
-1st person character mesh component (mesh id, animation frame)
-1st person weapon mesh component ( mesh id, character mesh bone)
-health component (health value)
-weapon1 component (ammo)
-weapon2 component (ammo)
-weapon3 component (ammo)
-weapon4 component (ammo)
-Shield component (remaining time)
-cloaking device component (remaining time)
-Score component (num of kills, num of deaths, etc)

Somehow, I know I would need all that stuff for a quake like entity, but they're already quite a number of components for a single entity... and we're talking about the ultra simple old quake guy... modern games might need much more than that.

Agree? disagree? how to handle them? what happens if the guy receives a shot? who is notified first? the shield component if enabled? or the health component that looks for the shield component?, or should the mesh render components be aware that there's a "cloaking device" component enabled to render in a different way?

I think this is worth discussing.

[Edited by - vicviper on December 13, 2007 12:01:29 PM]

#90 smitty1276   Members   -  Reputation: 560

Like
0Likes
Like

Posted 13 December 2007 - 05:24 AM

I am jumping in here late, but a quick comment in response to Sneftel's OP.

Quote:
Original post by Sneftel
There have always been a few things I didn't like about the component-based model.

* Components have difficulty finding other components in the same Entity. Given an AnimationComponent which requires a PathfindingComponent to use, often it's necessary to manually iterate through the list of Components, dynamic_casting each one until you get a hit, or else to complicate the factory with details of what component needs to know about what, or the Entity with specific typed Component members for direct access.

* Components have difficulty finding the same components in other Entities, and their associated game-wide subsystems. The PathfindingComponent needs to know at least about other Entities' PathfindingComponents, or (more likely) a GameWidePathfindingSystem. The former can require a lot of iteration and dynamic_casting; the latter requires either some pretty serious arrow chains, or singletons/globals.


This isn't anything earth-shattering, but I thought I'd throw it in... We are using a component-based system where logic components publish named attributes, actions, and events, so you don't have to "find" other components, you just have to perform an operation using a name.

For example, if you want to adjust an entity's "speed", you don't have to find the component that publishes it because the entity itself knows how to deal with that:

float speed = entity->GetAttribute("ATT_SPEED");
entity->SetAttribute("ATT_SPEED", speed);

In our system, attributes kept to a small set of fairly primitive types (strings, vectors, ints, floats, bools, that sort of thing), but you could allow it to pass pointers around so that like components on different entities could locate each other.

We can subscribed to attribute changes...

entity->SubscribeToAttributeChange("ATT_SPEED", someFunctionPtrTypeThing);

...or to any published event.

In the system we use, there is actually a database of all published attributes, events, actions, etc., which allows for development tools to bring up lists of published names so that you can see what's available at dev time.

Then there are renderable components which can't publish attributes, but which look for published attributes. So if you wanted a component to draw a HUD, for example, you would need a HUD logic component to keep the details straight, and a renderable component to draw everything, pulling data from the logic component.

It's sort of an MVC component-based architecture.

#91 wild_pointer   Members   -  Reputation: 230

Like
0Likes
Like

Posted 13 December 2007 - 06:48 AM

Quote:
Original post by vicviper
hmm... don't you think this is going a bit too far? I mean, by now, we already have: entities, components, systems and subsystems, and with that, people is clearly having problems understanding mutual relationships between these different elements. I myself don't know very well in which direction to go, because, so far, many of the solutions presented here would not allow some things I want to do.

Remember, one of the theorical good things about componentized entities was to avoid the traditional, bloated hierarchical entities. And I think we should move in the direction to see how everything fits in a real scenario.

For example: many have said that only allow one instance of a given component type inside a single entity; but, given that a "weapon" would be a component, what if I want an entity to have multiple weapons?

In a game like Quake, I think a player entity could be broken in the next components:

-quake player entity
-spatial component (position, rotation)
-physics (velocities, collision detection) component
-user input component
-AI component
-3rd person character mesh component (mesh id, animation frame)
-3rd person weapon mesh component ( mesh id, character mesh bone)
-1st person character mesh component (mesh id, animation frame)
-1st person weapon mesh component ( mesh id, character mesh bone)
-health component (health value)
-weapon1 component (ammo)
-weapon2 component (ammo)
-weapon3 component (ammo)
-weapon4 component (ammo)
-Shield component (remaining time)
-cloaking device component (remaining time)
-Score component (num of kills, num of deaths, etc)

Somehow, I know I would need all that stuff for a quake like entity, but they're already quite a number of components for a single entity... and we're talking about the ultra simple old quake guy... modern games might need much more than that.

Agree? disagree? how to handle them? what happens if the guy receives a shot? who is notified first? the shield component if enabled? or the health component that looks for the shield component?, or should the mesh render components be aware that there's a "cloaking device" component enabled to render in a different way?

I think this is worth discussing.


Conceptually, I don't think of components as something you'd really want to be adding and removing at runtime. Practically, there tends to be a lot of overhead involved in doing so.

At any rate, there's no clear advantage to having weapons and ammo be components. If I were doing something like Quake I'd probably just have an "Inventory" component which contained weapons, ammo, shields, etc. Maybe another component to keep track of what's currently equipped and proxy onTakeHit and onFire events.



#92 elFarto   Members   -  Reputation: 206

Like
0Likes
Like

Posted 13 December 2007 - 06:50 AM

Quote:
Original post by vicviper
***snip large post***


Another thing I've noticed is a split between 'engine components' and 'game components'. So the first 4 on your list are engine components (they're directly related to a capability of the game engine). The next 4 should really just be one render component.

The rest however, are game components (i.e. implement game logic/content) and I still haven't figured out what to do with those.

Regarding the component/subsystem thing. I believe that they are 2 sides of the same coin. To me, a component means that the logic would be incorporated into each entity, where as a subsystem indicates there's just 1 instance of it (this also kinda implies that the entities are purely data).

Regards
elFarto

#93 Dave Hunt   Crossbones+   -  Reputation: 2475

Like
0Likes
Like

Posted 13 December 2007 - 08:50 AM

Quote:
Original post by vicviper
For example: many have said that only allow one instance of a given component type inside a single entity; but, given that a "weapon" would be a component, what if I want an entity to have multiple weapons?


In that case, you can have a WeaponCollectionComponent that maintains the list of WeaponComponents the player has, along with which WeaponComponent is currently active, etc.


#94 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 13 December 2007 - 08:53 AM

Quote:
Original post by vicviper
For example: many have said that only allow one instance of a given component type inside a single entity; but, given that a "weapon" would be a component, what if I want an entity to have multiple weapons?
Then "weapon" shouldn't be a component; "weaponry" should, or (perhaps too abstracted) "inventory".

Quote:

-spatial component (position, rotation)
-physics (velocities, collision detection) component
-user input component

"User input", IMHO, is sort of a strange thing to make into a component; I see it as an outside system sending calls to a particular entity.

Quote:

-3rd person character mesh component (mesh id, animation frame)
-3rd person weapon mesh component ( mesh id, character mesh bone)
-1st person character mesh component (mesh id, animation frame)
-1st person weapon mesh component ( mesh id, character mesh bone)

Blech! More likely, a single "graphics" component.

Quote:

-weapon1 component (ammo)
-weapon2 component (ammo)
-weapon3 component (ammo)
-weapon4 component (ammo)
-Shield component (remaining time)
-cloaking device component (remaining time)

See "weaponry"/"inventory" above.
Quote:

-Score component (num of kills, num of deaths, etc)

Somehow, I know I would need all that stuff for a quake like entity, but they're already quite a number of components for a single entity... and we're talking about the ultra simple old quake guy... modern games might need much more than that.

Your idea for components has much teenier granularity than mine. I don't think that taking all the character's weapons and all his stats and all his meshes and throwing them into a flat list of "components" is a good idea. A "component", to me, is an entity-specific delegate* for accomplishing a particular area of the entity's functionality. Components should have data structures that are as complex as they need to be. The complexity should be maintained within the component.


*I don't mean that in the bound-function-pointer sense of the word.

#95 thre3dee   Members   -  Reputation: 100

Like
0Likes
Like

Posted 13 December 2007 - 09:48 AM

a weapon is fluff on the side of the engine. the engine shouldnt know about things like multiple instance game items.

The game should be defining an 'item' or usable item class(s) which be kept in an item inventory.

class ItemInventory;

// Item base class
class ItemBase
{
private:
ItemInventory * parent;

public:
virtual void Use (void) = 0; // etc etc
const char * Name (void);
};

// item inventory
class ItemInventory : PropertyObject
{
private:
map <string, vector <ItemBase *>> item_categories;

void AddItem (string category, ItemBase * item);
};

// adds a weapon item and sets it to be the primary
ItemInventory inv;
Weapon * shotty = new Weapon ("shotgun");
inv.AddItem ("weapons", shotty);
inv.SetProperty ("prim-weapon", shotty); // from PropertyObject class






#96 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 13 December 2007 - 10:01 AM

Quote:
Original post by smitty1276
[ lotsa stuff ]

I like this approach. One thing that I notice about it, though, is that it is basically a reimplementation of OOP within C++, one which supports runtime-composed objects. My thought, therefore, is that the first thing to do is to come up with a good framework for runtime-composed objects in C++, independent of entities and components and whatnot.

More to come, gotta run.

#97 Cycles   Members   -  Reputation: 187

Like
0Likes
Like

Posted 13 December 2007 - 10:19 AM

In regards to smitty's post - it seems like a nice simple way to tackle the problem, but aren't you making a pretty big sacrifice: compile time checking? Maybe there is some real magic behind the scenes, but I can see that as being a predominantly runtime system...

My latest iteration of this is to now declare Entity's like this:


public class Asteroid : Entity
{
public Vector2 InitialVelocity { get; set; }

Controllers.ScreenWrappingController swc = new SpaceGame.Controllers.ScreenWrappingController(new System.Drawing.Size(800, 600));

public Asteroid(World manager) : base(manager)
{
AddComponent(typeof(Components.Damage));
AddComponent(typeof(Components.Geometry));
AddComponent(typeof(Components.MoveableComponent));
}

public override void Initialize()
{
Components.Geometry geometry = RequestComponent<Components.Geometry>();
geometry.CreateQuad(new Rectangle(-1, -1, 1, 1));

Components.MoveableComponent moveable = RequestComponent<Components.MoveableComponent>();
moveable.Velocity = InitialVelocity;
}
}



Then you just create an Asteroid as:

Entities.Asteroid asteroid = new SpaceGame.Entities.Asteroid(this)
{
Position = position,
Rotation = (float)(randomGenerator.NextDouble() * 2 * Math.PI - Math.PI),
Scale = (float)(randomGenerator.NextDouble() * 50 + 10),
InitialVelocity = velocity
};

InjectEntity(asteroid);


InjectEntity(Entity) is a method on my World class that simply notifies all component subsystems that an Entity has been added, and they then check the Entities requiredComponents as to whether they should act on the creating an Entity.

The Entity is then initialized, and is able to retrieve it's components as it needs then, and seed them appropriatley.

In this model, the Entity acts as a specific "coordinator", and would be responsible for handling certain messages (I.e. when a Damage component reports itself as depleted/destroyed), and routing them to other components as needed.

Any ideas on this?

Oliver Charles (aka aCiD2) [ Cycles Blog ]

#98 smitty1276   Members   -  Reputation: 560

Like
0Likes
Like

Posted 14 December 2007 - 02:27 AM

Quote:
Original post by Sneftel
I like this approach. One thing that I notice about it, though, is that it is basically a reimplementation of OOP within C++, one which supports runtime-composed objects. My thought, therefore, is that the first thing to do is to come up with a good framework for runtime-composed objects in C++, independent of entities and components and whatnot.


Quote:
Original post by Cycles
In regards to smitty's post - it seems like a nice simple way to tackle the problem, but aren't you making a pretty big sacrifice: compile time checking?


Yes and mostly yes.

One of the uses of the system is that it allows non-developers to create content... entire AI behaviors (of widely varying complexity) can be visually "scripted" for entities, using nothing more than basic operations on named attributes. So, yeah... it's definitely a way of exposing an OOP-like interface to non-developers who wouldn't even recognize the term. It definitely allows for class inheritance and that sort of thing from the non-developers perspective--entity types can be drived from others and inherit their attached components, etc.



#99 wild_pointer   Members   -  Reputation: 230

Like
0Likes
Like

Posted 14 December 2007 - 03:17 PM

I'm curious how people handle scenegraph integration with a component based system. Usually things like Effect, Position, Cameras, etc are part of a scene graph but they also make sense as components.

I was thinking of arranging my entities hierarchally such that the entities double as nodes. I found traversal difficult with this method.

The other approach I tried was maintaining separate hierarchies for things like world transforms.


class WorldTransform : public IComponent
{
public:
Matrix Transform()
{
Matrix t = transform_;
if(parent_)
{
t *= parent_->Transform();
}
return t;
}
private:
Matrix transform_;
boost::shared_ptr<WorldTransform> parent_;
};


This worked alright for world transforms but doesn't seem to apply cleanly anywhere else. For example, I have no idea how cameras, and effects might fit in.

#100 Sneftel   Senior Moderators   -  Reputation: 1781

Like
0Likes
Like

Posted 16 December 2007 - 04:38 PM

Quote:
Original post by Sneftel
Quote:
Original post by smitty1276
[ lotsa stuff ]

I like this approach. One thing that I notice about it, though, is that it is basically a reimplementation of OOP within C++, one which supports runtime-composed objects. My thought, therefore, is that the first thing to do is to come up with a good framework for runtime-composed objects in C++, independent of entities and components and whatnot.

More to come, gotta run.

I couldn't leave well enough alone.




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