Game Object Component System

I thought such remarks were beneath you? No need to be so patronizing. You might miss my point? What I’m saying is that I like the functionality in the component, as opposed to the system. I already wrote quite some code around these Entities and Components (and Systems as of today) and it certainly feels like a database - in the current code, you can already simply grab all entities that have component X, without plowing through all the entities. Similairly, you can grab all components in an entity, without the entity having a list of children. The implementation can both be a DB, or a hacked alternative using lots of java.util.Maps to quarantee nearly instant access to ‘columns’.

Currently I have code like:
`
EntityContainer container = new MySQLContainer();

Entity entity1 = new Entity();
container.add(entity1);
container.register(entity1, HealthComponent.class); // creates HealthComponent
container.register(entity1, MovementComponent.class);

Entity entity2 = new Entity();
container.add(entity2);
container.register(entity2, HealthComponent.class); // creates HealthComponent
container.register(entity2, DecayComponent.class);

HealthSystem healthSystem = new HealthSystem()
{
public Class type()
{
return HealthComponent.class;
}

public void perform(Entity entity, HealthComponent health)
{
// there is a 1:1 relationship between the entity and health object.
// fields like health.min, health.max, health.current are ‘bound’ to this entity

     if(health.isDying()) // this is code in the component - bad
     if(this.isDying(health)) // this is code in the system - better?
        // remove entity from the container
    else
        health.recover(); // this is code in the component - bad
        this.recover(health); // this is code in the system - better?

}

// like this... ?
private boolean isDying(HealthComponent comp) 
{
     return comp.health < 10;
}

}

// and now we feed the Container a HealthSystem, which
// iterates over all Entities with a HealthComponent
// and invokes system.perform(entity, health)
container.execute(healthSystem);
`

Maybe this code clearly shows that I’m missing your point. (?) Maybe, but I’m fairly sure I’m going in the right direction.

What I assume, is that the Component classes/types are like different tables, as you have a fixed amount of columns/fields per {Health|Decay|Movement}Component. Components are like records in a these tables: they only contain data. To connect everything, you have a table like:
(entity_id INT, comp_type INT, comp_id INT UNIQUE)
So that you can instantly find all types that have the same comp_type, and JOIN one of the Component tables depending on what that comp_type actually is. the comp_id is the foreign key used to join the entities to the components.
Either that, or a foreign key (entity_id) in each Component type table.
(Is there a more efficient way that you worked out?)

The System behaves like a stored procedure for a specific component type (like Health).

It just takes a while to get used to, and my preference for the code-in-component might just as well change sooner or later.

I suppose that when I have a problem that isn’t totally easily solvable with OOP I’ll look to something else to solve it. So far though in about 30 years the only problem I’ve found that is dealt with better in another language is SQL. It’d be nice to mix the two a bit more.

Cas :slight_smile:

the more I think of its design the more I like it (I think that I will try to use such architecture starting from scratch for my next demo and than I hope I will learn a lot from this experience), but I am probably still a lot wrong on how I have understanded it… so … below is how for now:

let’s say I want to create a simple game where gameobject can have ( at the start of my game design ) a position,appearance,health and want to store all of that in a DB

I will then create three SQL table like the following (EUID stands for Entity Unic Identifier):

POSITON fields : EUID X Y Z
APPEARANCE fields : EUID SHAPENAME
HEALTH fields : EUID HEALTHVALUE

I have three components that I can mix together to create my real gameobjects

So if I want to create a new object that have a Position and an Apperance I will get a new EUID and do two insert in POSITION & APPERANCE tables.

INSERT INTO POSITION (with inded EUID the same in both table)

and, if I want to remove I simply do an SQL delete (for all components/tables):

DELETE FROM … where EUID=‘XXXX’

now if I want to add FIRE to my game I will just add a FIRE table with those fields : EUID, GUN_TYPE

in fact I clearly see that entity doesnt have anywhere a real OO meaning, this is just an identifier, but components seems to maps OO objects , no ?

second, does the above is completly wrong or no ?

EDIT :
POSITION => mean the Moveable component
APPERANCE => mean Viewvable component
HEALTH => mean somethingable component
etc…

Well, that’s what I make of it, and it certainly can be implemented that way, as I currently have it just like that.

One nice thing is that you can have a ‘ram container’ and a ‘sql container’ and use the one that best fits your Component type, specified in a ‘hybrid container’.

`
EntityContainer sql = new SQLContainer();
EntityContainer ram = new RAMContainer();

EntityContainter hybrid = new HybridContrainer();
hybrid.setDefaultStrategy(ram);
hybrid.setStrategy(PositionComponent.class, ram);
hybrid.setStrategy(HealthComponent.class, ram);
hybrid.setStrategy(DecayComponent.class, sql)

Entity entity = new Entity();
hybrid.add(entity);
hybrid.register(entity, HealthComponent.class);
// etc
`

That way you can have instant access (RAM) if you need it , and can use slower access (SQL) for systems with a lower ‘tick rate’, or simply have less entities.

Sorry - wasn’t intended to be patronizing, I just badly phrased it: it was intended literally: don’t touch an ES in that case - it will be more trouble to you than it’s worth. But if you find yourself shouting “FSCKING OOP!” at your monitor one day … that would be a good time to revisit this subject, and see if you feel differently about it (I supect you would).

By “code should be in the system”, I mean that the physical location of your functions that contain that code is “inside classes that are internal to the system, not inside classes that define the java/OOP objects that are used as game-entities”.

Depending upon how you call the perform method, it could count as either. If you made it explicitly static, then it could only be the latter - but you’d also be throwing away the on-the-ground benefits of OOP as a generic programming paradigm for writing those funcitons.

BUT … from your code snippet, I think you fully understand what I’m trying to say here, so I’ll shut up :).

No, that convinces me that you’ve got it. In the light of this, I don’t understand your previous statements where you said you wanted to revent to code-in-component, OOP style?

Have you tried writing a system where you write all the code in the system, not in the components? maybe attempting to do that will make it clearer what you don’t like (or why you do, in fact, like it :))

I found that I hadnt’ realiased I had 2 or 3 different mutually incompatible “types” of OOP object in my architecture - there were the game-entities, the behaviour of metacode, and the internal represetnations WITHIN The systems of the PLACEHOLDERS for game-entities that the system was TEMPORARILY referrring to while executing.

(the latter type being like template-programming in C++)

Have I made it totally and utterly obscure yet? :slight_smile:

Yes!

YES YES YES! :slight_smile:

OK.

Don’t forget that you have experience under your belt, and I have not. I bet it was like that for you too, initially you were trying to figure stuff out by trial and error, and refactored your code time and time again.

So don’t be too harsh when others are making ‘silly mistakes’, while this is only natural when starting something new.

No. I’ve only been coding on this for a few hours. The API is - more or less - done, but using it is something totally different, and will often show the bugs, weaknesses and the things that were misdesigned/misinterpreted.

Quite, but I’ll just stumble through all my mistakes and see what’s best.

Wow, I must be on the right path understanding this, as I was sitting at work staring out the window, i also spotted the whole entity is just an ID thing, and figured derby in ram might be a nice way to address all this. Want to know what entities are near you?, query the position components retrieving the entity IDs :). Not sure sql is all that fast mind, but it does mean that the posibilities for searching your game world are huge.

So do I also understand correctly that the components (or their classes) contain no logic?, they contain data, and the system contains the logic?

Endolf

Well, that’s pretty much it.

There are indeed some problems: it’s not easy to get a subset of the Entities with a specific Component, but then again, didn’t we always have to code that somewhere else? Quadtrees, octtrees…

You can fake with with Components, sure… but that seems to be totally abusing the system (not the System).

In SQL, you can however refine your WHERE clause - but how to do that transparantly from inside the System is not easy. When you’re working in RAM, you’d implement it with an EntityFilter->accept(Entity entity) but then you’d be iterating over all Entities anyway - not much use.

Code like:
entityContainer.execute(spatialSystem, "WHERE x BETWEEN 50 AND 63");
feels evil.

Blahblahblahh, maybe you also have figured a solution for this, or will we simply use the existing code to deal with sptial algorithms? Doing everything in SQL simply won’t cut it, when you’re checking ‘who is near me’ hundreds/thousands of times per frame, at 60fps.

I read Mick’s article and your article.(Still waiting for the next installement :wink: ) Does Scott Bilas have an article on this, other than his GDC slides? I went through his slides, but would like to read something in more detail if he has it.

on the way of loving this design (if I can manage to understand it well …:)) but still lost some times on simple cases :

let’s say I plan to have two kind of unit : spiders & ants

I want them to walks and have an apperance so they will both have the “CanBeDisplay” component with the right value for each, but what about the walk ?

I mean they walk are differents : what would be better between one components “CanWalkComponent” that handle differents case and two components “CanWalkAsSpiderComponent” + “CanWalkAsAntComponent” ?

how to decide “the deepness” of the components ? and heu… does that question make sense ?

You mention a complex example.

You could just have a WalkComponent, and then have it contain all sorts of leg combination and movement of those legs. You can contain all this behaviour within one component.

E.g., my WeaponsComponent has Targeters, WeaponControllers, and Weapons.

Each targeter has a list of weaponControllers, and each weaponController has a list of weapons. Each weapon fires a projectile of a certain type.

[quote]You could just have a WalkComponent, and then have it contain all sorts of leg combination and movement of those legs. You can contain all this behaviour within one component.
[/quote]
thanks, that was also my first thought, maybe easier to maintain

EDIT:
just though that maybe the answer can be found in the data, like if I handle same data (let say speed) than == same component but if I handle each legs states as they have not both the same number => then == different components ?

Once upon a time in a kingdom far, far away there was a King who was fed up of having his toast burnt every morning by the cook. So he sent far and wide across his land for subjects who might be able to provide him with decent toast. After months of searching, finally two people turned up in his court - a software engineer and a hardware engineer.

“Tell me your solutions!” spake the King. And the hardware engineer said, “My lord, we could have a toasting device rigged to the mains, which has a dial on the side of it. You wind the clockwork dial which unwinds slowly, and that determines the length of time the bread remains in the toasting device, for when the dial stops, the toast pops up. After a couple of goes we’ll figure out the perfect point to set the dial for you and draw a mark on the dial face.”

“And what of you?” enquired the King of the software engineer. The software engineer replied, “My lord, instead of a simple clockwork timer, we employ a 4-bit microcontroller, which is programmed by tapping two buttons on the side of the toaster to increase or decrease the shade of toast required between 16 shades. The microcontroller uses a lookup table of millisecond times to determine how long to keep the toast in the toaster. We can program the millisecond timer using a simple Bluetooth interface using TCP/IP to send an XML document containing the millisecond values. Just think, my lord! A different shade of toast for any occasion!”

The king wisely had the software engineer executed, and the land lived happily ever after.

Cas :slight_smile:

It certainly feels like a system that’s totally over engineered, but… maybe we first have to head into a dark world before we see the light?

:persecutioncomplex:

It’d largely depend how complex your game is I guess, I think I’m just this side of it being overengineering - but were I to start adding in network play or game replays then it’d probably be worth it. On the other hand something like Dwarf Fortress would probably be impossible with a traditional Big Entity Hierarchy.

I have no idea what actually drives DF internally, but it’d be interesting to find out.

hehe pretty fun :), the story doesn’t say if the toasts was good ?

I dont really like stuff as XHTML/W3C/Collada/etc… basically all those new standards that claim they handle about everythings and works better, give a lot of promess, and finally make that your website is only viewable by browser fully CSS12 compliant and not by the most popular browser… and make you wensite missed by 80% users… , but about those ES/components system it seems for me to better match the reality (as far as I understand it :persecutioncomplex: )

You can see what I’m doing already with Galactic Commander:
http://private.is/arni/gcom/

The XML file is getting bigger and objects are more complex:
http://private.is/arni/gcom/gocs.xml

Overall this is going very well.

edit: I’ll try to add more complex and different game objects for demonstration purposes.

Has anyone looked at LINQ in .NET? It seems to be a way to do SQL like queries on datasets that is in memory.

I’ve rewritten the simulation editor I’m working on to using a component system. I’ve found that many components are just wrappers for other objects. An example: I have a Text3DComponent that just a wrapper for a Java3D Text3D. The actual data is stored in the Text3D object. The component exposes the data (font, text, size etc) as a list of properties. This list is used for serialisation and in the editor ui. It also has methods to set and get all properties to make it easer to use the component from scripts and other components. But here comes the “dirty” part that I’m uncertain about. Text3DComponent extends a SimulationComponent. It has an attachedToWorld and detachedFromWorld method that are invoked when the component is attached or detached. Text3DComponent uses those callbacks to attaches its Java3D node to the scenegraph. Is this wrong? Should the Text3D scenegraph object be stored in the component or the system. Shoulde the attaching be done in the system instead?

I’ve been lightly following this line.
Using your walk component. Putting all possible leg movement seems a bit excessive. I would think that you need to have an abstract leg movement class, then have classes that extend that for specific leg movement. Being that a given unit would only have a specific leg movement and you would add the leg movement that you need.
The abstract class would have the common methods, like walk(), which would be used throughout the game. The only object that should care (per say) the specific movement is the unit.
Yes you would have a lot of classes for each type of movement, but if you come up with a new movement you just add a new class, not change the existing one.
I hope I explained that well.