Sign in to follow this  
TheComet

Wrote an Entity-Component Framework

Recommended Posts

I wasn't happy with existing frameworks so I quickly rolled my own. I'm posting this here in the hopes of someone finding it useful.
https://github.com/TheComet93/ontology
 
Feature highlights are:

  • Unique filtering ability. Systems can declare what components they support, making sure they only receive entities that actually have those components.
  • Compact and comprehensive method for creating components and systems.
  • Doxygen documented code.

The top-level interface can look like this:
 

int main()
{
    // The world holds everything together
    Ontology::World world;

    // register various systems with the world
    world.getSystemManager()
        .addSystem(new MovementSystem,
            Ontology::SupportsComponents<
                Position,
                Velocity
                >())
        .addSystem(new RenderSystem,
            Ontology::SupportsComponents<
                Position,
                Sprite
                >())
        .addSystem(new InputSystem)
        .addSystem(new MainLoop,
            Ontology::SupportsComponents<
                Ontology::None
                >())
        .initialise()
        ;

    // create an entity in the world
    world.getEntityManager().createEntity("Player")
        .addComponent(new Position(0, 0))
        .addComponent(new Velocity(0, 0))
        .addComponent(new Sprite("res/player.png"))
        ;

    // run game
    world.getSystem<MainLoop>().execute();

    return 0;
}

The syntax is very boost-esque.

Share this post


Link to post
Share on other sites

I'd say the biggest issue with this system is that you're storing components on entities without a decent wrapper for how to allocate those components.

Ideally, each component would be contiguous in ram, as opposed to disjoint as your example code will generate (via "new Position(...)").  I'd like to see a better wrapper that hides the allocation scheme of the components to allow for better memory access patterns.

 

It's not ideal to iterate over entities to reach components when you already know something like the RenderSystem only cares about the union of the sets for Position and Velocity that share entity ids.  If the union is sparse on either component list, or sparce among all entities then you're wasting a lot of time iterating over every entity to find out if it has the matching set of components.

Edited by KulSeran

Share this post


Link to post
Share on other sites

Thanks for the feedback!

 

 


I'd say the biggest issue with this system is that you're storing components on entities without a decent wrapper for how to allocate those components.
Ideally, each component would be contiguous in ram, as opposed to disjoint as your example code will generate (via "new Position(...)").  I'd like to see a better wrapper that hides the allocation scheme of the components to allow for better memory access patterns.

Very good point, I'll see if I can get that to work. Is there a common technique when trying to store varying types in contiguous memory? I can think of two immediate ways, such as maintaining n number of std::vector objects where n=number of types, or doing some allocation trickery since the types are known at compile time. [EDIT] Actually scratch the first suggestion, there's guaranteed to only ever be one instance of every component.

 

 

 


Passing a unique pointer instead of a raw pointer is also better if you are giving up ownership

I actually removed that because I found it makes its usage a little unwieldy and harder to read. I know that shouldn't be an argument...

Edited by TheComet

Share this post


Link to post
Share on other sites


Unique filtering ability. Systems can declare what components they support, making sure they only receive entities that actually have those components.

 

I wouldn't really call that a "unique" feature, I implemented the same exact concept in my ECS based on seeing it in several other ECS's I studied for ideas. Also, sorry to be pedantic, but it doesn't appear as though the systems are declaring anything about what components they support, the call to addsystem() is providing that information externally, which isn't quite the same thing. You could create a system that only knows how to work on a component of type A, and call addSystem() and tell the systemmanager it works on components of type B, and it would be quite happy to sit there idle I'm guessing?

Share this post


Link to post
Share on other sites


Very good point, I'll see if I can get that to work. Is there a common technique when trying to store varying types in contiguous memory? I can think of two immediate ways, such as maintaining n number of std::vector objects where n=number of types, or doing some allocation trickery since the types are known at compile time. [EDIT] Actually scratch the first suggestion, there's guaranteed to only ever be one instance of every component.

 

I think the point is that you *wouldn't* (or shouldn't) store varying types in contiguous memory. You might have one component of each type per entity, but you have many entities with components of the same type. So you store each type of component together in its own list -- all the PhysicsComponents in one list, all the GraphicsComponents in another list, all the AIComponents together, and so on. You associate each component with the entity it belongs to, but there's no need to store the components for a given entity next to each other. When you want to handle your physics simulation, for example, you iterate over all the PhysicsComponents in your game world which is fast because they're all in contiguous memory and you don't need any branches to skip over entities that don't have a PhysicsComponent.

Share this post


Link to post
Share on other sites


there's no need to store the components for a given entity next to each other

Thanks Starfarer42, that's really what I was trying to get at.  My issue with your implementation TheComet is that it puts too much emphasis on allocating and attaching things to an Entity.  That provides flexibility, but costs performance.  An ideal entity component system focuses more on the components / systems than it does on the "entity" part, because it should be rare that you ever need to check what other components an entity has.  Components can be stored in a single array per-type, like Starfarer42 suggests.  If you keep them sorted by entity id (or in a binary tree pattern), it's possible to quickly traverse one or more component ('attribute') lists in parallel paired off by id for the rare cases where you need multiple attributes together to perform a calculation.

Share this post


Link to post
Share on other sites

it doesn't appear as though the systems are declaring anything about what components they support, the call to addsystem() is providing that information externally, which isn't quite the same thing.

The information eventually ends up inside the base class of System. The reason I didn't want to do it through System's constructor was for three reasons:

  • It would force the user to write a constructor every time he derives from System just to pass in the necessary information.
  • Adding future options/more arguments to addSystem would cause every derived class' constructor to be re-written -> unnecessary.
  • addSystem() is more capable of making default decisions when arguments aren't supplied because the class it belongs to has access to all systems (such as execution reordering).

You could create a system that only knows how to work on a component of type A, and call addSystem() and tell the systemmanager it works on components of type B, and it would be quite happy to sit there idle I'm guessing?

What Starfarer42 said and you'll get a runtime error if you try to fetch a component from an entity that doesn't exist.
 
@ Starfarer42 & KulSeran

Thanks for the explanation, now I get where you're coming from. I've added it to my todo list.

 

Two other things I'm working on are:

  • Each system to precompute a list of entities it supports instead of comparing every time world.update() is called.
  • A multithreaded approach at processing entities.

Share this post


Link to post
Share on other sites


The information eventually ends up inside the base class of System. The reason I didn't want to do it through System's constructor was for three reasons:

  • It would force the user to write a constructor every time he derives from System just to pass in the necessary information.
  • Adding future options/more arguments to addSystem would cause every derived class' constructor to be re-written -> unnecessary.
  • addSystem() is more capable of making default decisions when arguments aren't supplied because the class it belongs to has access to all systems (such as execution reordering).

 

You don't have to use a constructor for this, but I would also advice towards keeping this information inside the system. You can have a virtual method in the system base class:

class System
{
public:

     virtual SupportedComponents GetSupportedComponents(void) const = 0;
}

every system will have to overwrite this and supply the components it wants to use. You can all this method e.g. directly inside "addSystem", which poses almost zero overhead but keeps this information in the systems declaration.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this