Shuffling class responsibilities

Published January 24, 2016
Advertisement

[font=arial]In this update I'll talk about the latest release of dustArtemis, which changes a bit the component handling.[/font]




[font=arial]dustArtemis[/font]

[font=arial]dustArtemis is a fork of Artemis Entity System, which is a BSD-licenced small Java framework for setting up Entities, Components and Systems.[/font]

[font=arial]ComponentManager and the old ways[/font]

[font=arial]This class had quite a few things bolted on as time passed. As a recap, remember that dustArtemis needs a couple of things to link an entity to a component:[/font]


  1. [font=arial]Entity id[/font]


  • [font=arial]Component collection where to set the component.[/font]


  • [font=arial]Entity's component bitset that mark what component types an entity has.[/font]


  • [font=arial]You had two ways to add component to an entity:[/font]


    .

    // Manager reference.ComponentManager cm = world.componentManager();// Spatial mapper reference.ComponentMapper spatials = world.getMapperFor(Spatial.class);// New entity id.int id = world.createEntity();// We want to add this component to the entity.Spatial s = new Spatial(); // First way:cm.addComponent(id, s);// Second way:cm.addComponent(id, s, spatials);

    .

    [font=arial]The issues here aren't obvious, first way of adding a component internally does a HashMap lookup to fetch the index of the component's type. It works something like:[/font]


    .

    // Hash lookup Class ? index.int mapperIndex = indexOf(component.getClass());// Add the component to the entity.componentMappers[mapperIndex].add(entityId, component);// Now flip the appropiate bit in the entity's component bitset.componentBits[entityId].set(mapperIndex);

    .

    [font=arial]Great volumes of these lookups end up showing in the profiler. The worst case of this you can see in some ECS frameworks which use hash maps to link components with entities, which ends up in a hash lookup on each component access. So we want these to be as fast as possible.[/font]

    [font=arial]The second way is the fastest since the hash lookup is made when calling world.getMapperFor(type), which can possibly be made only once at initialization and just store it as a field. We also need a ComponentManager reference, so internally the ComponentMapper does something like this:[/font]


    .

    // Add the component to the entity, mapper knows its index.componentMappers[mapper.index].add(entityId, component);// Now flip the appropiate bit in the entity's component bitset.componentBits[entityId].set(mapper.index);

    .

    [font=arial]No hash lookup, but the user needs a reference to both the mapper and the component manager for it to work.[/font]

    [font=arial]The new way[/font]

    [font=arial]What we want is to have only one way to do this, so we push some load onto the ComponentMapper until it becomes the new and shiny ComponentHandler (terrible names I know).[/font]

    [font=arial]We know that in each system we will have the mappers of the components the system deals with. These get "injected" in an initialization step automagically by dustArtemis, you just have to declare it like:[/font]


    .

    // Field in some EntityObserver.private ComponentMapper spatials;

    .

    [font=arial]A proper instance gets injected into that field in your EntityObserver and all works. But the issue was that we also needed to fetch the ComponentMapper.[/font]

    [font=arial]What I did is simply to push a few things onto the ComponentMapper, renaming it to ComponentHandler now since it not only does mapping anymore. [/font]With our ComponentHandler we can do the following:


    .

    // Gets injected at runtime.private ComponentHandler spatials; // ... then latter in some entity observer method.int id = world.createEntity();Spatial s = new Spatial();// Just add the component using the handler itself.spatials.add(id, s);

    .

    [font=arial]The handler internally does something like this:[/font]

    .

    this.ensureCapacity(id);this.data[id] = component;this.componentManager.componentBits()[id].set(this.index);

    .

    [font=arial]It knows both its index and the component manager that "owns" it. It has to access the component bits through the manager instead of having an array reference so the manager internally can resize and initialize the bitsets as needed.[/font]

    [font=arial]The only way to get a ComponentHandler is to call world.getHandler(type), so there is no way to create many of them. And thanks to the Injector class, they'll most certainly will be placed in all places they're needed.[/font]

    [font=arial]The *other* new ways[/font]

    [font=arial]dustArtemis 1.1.0 has a couple of other things I'd like to talk (new World builder, the new Injector class, etc) but that will be on another entry, cya later![/font]

    [font=arial]EDIT: As always the editor is a piece of crap. Can't remove the italics and some line jumps get inserted randomly : |[/font]




    [font=arial]EDIT2: Friggin editing the BBCode by hand worked biggrin.png[/font]

    [font=arial]EDIT3: No it didn't. WHY it inserts new lines on the BBCode!? [/font]

    2 likes 0 comments

    Comments

    Nobody has left a comment. You can be the first!
    You must log in to join the conversation.
    Don't have a GameDev.net account? Sign up!
    Profile
    Author
    Advertisement
    Advertisement