Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Different approach to ECS pattern

Started by wcassella Feb 3, 2015 at 11:08 PM 2 replies 7.7k views
Original Post
wcassella
wcassella

Hi,

Since the game engine I'm working on is largely useless compared to the more "professional" engines out there, I'm taking the opportunity to experiment with the architecture for it. The design I'm working on is a slightly different take on the Entity Component System, based off ideas I've had from working with both Unity and UE4. Anyway, here goes:

The root type of object in a scene is a GameObject, which has the following features:

Transform - Describing the position/rotation/scale/parent of the GameObject. In a pure ECS design this would be a component unto itself, but there's very few cases where you don't want one, and I'd rather have the guarantee that it exists.

Behavior Components - This is a linear array of components that are responsible for manipulating the GameObject that owns them. This is where the bulk of game logic will be attached, including networking and physics behavior. Basically anything that fits under the concept of "Manipulation" should go here.

Display Components - Unlike Behavior Components, this is a tree rather than an array. Anything that fits under that concept of "Description" should go here - meshes, particle emitters, sound emitters, and more. Each display component has a transform of its own, to describe a physical offset from its parent (the base case of which is the GameObject itself).

Behavior components would mainly be manipulating the state of the GameObject and its display components each frame, while the display components would pretty much just sit around to be enumerated by their respective subsystems (ie, the particle system could poll for the transform of every particle emitter in the scene).

One large advantage to this system is that what would have been constructed as a parent/child hierarchy of objects in Unity can now be boiled down into one GameObject with several display components.

This only exists on paper right now, and I'm still not entirely satisfied with the design. One problem with the ECS pattern as a whole is that it doesn't provide many guarantees about objects (knowing at compile time which features exist on any given GameObject). I suppose I could allow the 'GameObject' class to be extended, promoting components to actual fields when it's really integral to the behavior of the class (i.e some class must have a StaticMeshComponent or something, which gets manipulated in a special way) but that could get messy. One thing I do like about this design is it makes a clear distinction between "behavior" and "data"; and a shallow inheritance hierarchy like this not only allows the engine to be more data-driven, but makes reasoning about memory usage and runtime behavior much easier.

Has anyone worked on a design similar to this one, and if so: what were issues/benefits of the design in practice? Am I over thinking this? Bottom line is this: I'd like to have the guarantees of a more object-oriented approach, with the flexibility of an ECS. I'm a little stumped on how to do that, but I wouldn't be writing my own engine if I weren't willing to get stumped sometimes.

Thanks!

L. Spiro
L. Spiro

One problem with the ECS pattern as a whole is that it doesn't provide many guarantees about objects (knowing at compile time which features exist on any given GameObject).

You are describing a hybrid approach to ECS, which is likely the best approach for any indies/hobbyists, etc.
Partly because of compile-time guarantees that certain components are there (as you mentioned), but largely because of the main advantage of the ECS design that indies/hobbyists don’t have the technology to exploit.

Almost the only real advantage to ECS is that it can be easy to add and remove components, changing the properties of objects on-the-fly or at least easily via tools or scripting (even if not on-the-fly but rather only once).
Most of the time you just construct your objects once and let the game run, which means the biggest way to exploit ECS is to have both a tool-chain that allows you to easily add components and a scripting language to be easily able to add custom systems (and also more components).

If you don’t have a good tool chain and a scripting language, you have virtually no reason to go pure ECS (in fact you are really just making your life harder).

So your choice to modify your approach to ECS is the correct choice.

Transform - Describing the position/rotation/scale/parent of the GameObject.

If there is any component that would be hard-coded onto objects, it would be this one.

Behavior Components - This is a linear array of components that are responsible for manipulating the GameObject that owns them. This is where the bulk of game logic will be attached, including networking and physics behavior. Basically anything that fits under the concept of "Manipulation" should go here.

#1: Components should be structures of arrays, not stored as linear arrays of components.
#2: Networking is an entirely separate section of the engine and has nothing to do with ECS.
#3: Mass, velocity, etc., might be components of an object, but actual physics is a separate part of the game engine and is unrelated to ECS.

It is not clear what your line of thinking here is. It sounds as though this is just your custom term for “Systems” but somehow also containing “Components”.

Display Components - Unlike Behavior Components, this is a tree rather than an array. Anything that fits under that concept of "Description" should go here - meshes, particle emitters, sound emitters, and more. Each display component has a transform of its own, to describe a physical offset from its parent (the base case of which is the GameObject itself).

Once again it seems as though you are mixing ideas that should be separate.
You already have a transform on every type of game object, so why would you have another set of transforms here? If you don’t understand scene graphs clearly, read this. The hierarchy should already be implicit within your GameObject’s.

Why would this only apply to drawables? The scene graph (your tree structure) and graphics are completely unrelated topics, which makes it further difficult to dissect your description here, since your decision to call it “display components” certainly begs a discussion on renderables/displayables/drawables/etc., but there is just nothing to discuss.

All objects are to be in a tree, called a scene graph, regardless of any discussions on rendering. On the topic of rendering, there is nothing to say except to ask, “How is rendering related to trees/scene graphs?”.


One large advantage to this system is that what would have been constructed as a parent/child hierarchy of objects in Unity can now be boiled down into one GameObject with several display components.

That’s a disadvantage. The parent/child hierarchy (a scene graph) is a necessary part of any game engine even at the hobby level. It is supposed to be there.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
wcassella
wcassella

Thanks for the reply!

Behavior Components - This is a linear array of components that are responsible for manipulating the GameObject that owns them. This is where the bulk of game logic will be attached, including networking and physics behavior. Basically anything that fits under the concept of "Manipulation" should go here.

#1: Components should be structures of arrays, not stored as linear arrays of components.
#2: Networking is an entirely separate section of the engine and has nothing to do with ECS.
#3: Mass, velocity, etc., might be components of an object, but actual physics is a separate part of the game engine and is unrelated to ECS.

It is not clear what your line of thinking here is. It sounds as though this is just your custom term for “Systems” but somehow also containing “Components”.

You're right, I haven't entirely figured out how I want integrate systems like Networking and Physics into the engine. I guess some kind of external system that operates by observing the scene rather than via components would work best, but I haven't gotten there yet.

What do you mean by 'structures of arrays'? I'm thinking the class definition for a GameObject would look something like this:


class GameObject
{
    //////////////////
    ///   Fields   ///
public:

    Transform transform;

    /** I'd obviously want to encapsulate this better, but you get the point */
    Array<BehaviorComponent*> behaviorComponents;

    // Other stuff (name, ID, DisplayComponents...)
};

An update loop might then look like:


for (GameObject* object : this->objects)
{
    for (BehaviorComponent* component : object->behaviorComponents)
    {
        component->update();
    }
}

Once again it seems as though you are mixing ideas that should be separate.
You already have a transform on every type of game object, so why would you have another set of transforms here? If you don’t understand scene graphs clearly, read this. The hierarchy should already be implicit within your GameObject’s.

Why would this only apply to drawables? The scene graph (your tree structure) and graphics are completely unrelated topics, which makes it further difficult to dissect your description here, since your decision to call it “display components” certainly begs a discussion on renderables/displayables/drawables/etc., but there is just nothing to discuss.

All objects are to be in a tree, called a scene graph, regardless of any discussions on rendering. On the topic of rendering, there is nothing to say except to ask, “How is rendering related to trees/scene graphs?”.

With this idea I was trying to emulate Unreal Engine 4's concept of components ("sub-objects"), where one GameObject has ownership over several meshes/lights/whatever (separate from its children in the scene graph). These form their own little "scene" within the GameObject itself, complete with transforms and a parent/child hierarchy.

Anyway, based on what you've said and some more thought of my own, I think I'm going to go with an OO/ECS hybrid; utilizing classes for systems of behavior that don't work so well when split up, and supporting components for attachable/detachable behavior where it makes sense (for example, power ups in a racing game would be a great candidate for utilizing components, while the actual driving mechanics might not be).

Basically: Components when you want them, classes when you need them. The reflection system I'm working on has nice support for dynamic downcasting/sidecasting, so that will be helpful with the class system as well.

L. Spiro
L. Spiro

Anyway, based on what you've said and some more thought of my own, I think I'm going to go with an OO/ECS hybrid; utilizing classes for systems of behavior that don't work so well when split up, and supporting components for attachable/detachable behavior where it makes sense (for example, power ups in a racing game would be a great candidate for utilizing components, while the actual driving mechanics might not be).

This is exactly the correct approach. If you scour the forums you will find many cases where people have been exposed to ECS (typically through Unity 3D) and immediately just go full-on ECS without really considering what its real benefits are, especially given the gap between them and the makers of Unity 3D.
If there is anything a programmer needs to know about programming, it’s that there are indeed many approaches to any given system, but there are no single best answers. Every solution should be taking the benefits from one approach and the benefits from another approach.


What do you mean by 'structures of arrays'?

http://www.gamedev.net/page/resources/_/technical/game-programming/implementing-component-entity-systems-r3382
Specifically the “Entities” section.


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.