1
\$\begingroup\$

I want to implement ECS in my game. The more I read the more insecure I get.

The current available Components for a Entity processed by the RenderSystem are:

  • RenderComponent (mesh, texture, normalmap, etc.)
  • TransformationComponent (position, scale, rotation)
  • LightComponent (light color, brightness)
  • CameraComponent (view Matrix)

Let's assume that my EntityManager has a methode: getEntitiesByComponents(Class[] components): List and is a singleton.

Here is my pseudocode with the issues I have:

cclass RenderSystem {

    EntityManager em = EntityManager.getInstance();
    Renderer renderer = new Renderer();

    update(float dt) {

        //This one contained only  RenderSystem related components
        RenderSystemComponents rsc = em.getRenderSystemComponents();

        // Try to get the Camera
        [CameraComponent,TransformationComponent][] cameraArray = rsc.getComponentByType(TRANSFORMATION,CAMERA);
        // Check if Camera is avaible 
        if(cameraArray.length != 1) {
            // Do some error Stuff here 
            // Not sure if I should allow more then one Camera 
            // e.g. MiniMap 
        }


        // lights 
        [TransformationComponent,LightComponent][] lightArray = em.getComponentByType(TRANSFORMATION, LIGHT);

        // 'default' entities 
        [TransformationComponent,LightComponent][] entityArray = em.getComponentByType(TRANSFORMATION, RENDER);

        renderer.render(cameraArray,lightArray,entityArray);
    }

    public enum ComponentType {
        RENDER, TRANSFORMATION, LIGHT, CAMERA
    }
}

This implementation could work if my method getEntitesByComponents is fast enough. But the floor is literally the floor. The Terrain has seen from the component's point of view no difference but the RenderEngine should know that it is processing an Entity ( box, tree, player, mob, ... ) or a terrain (different shaders). This isn't a only RenderSystem problem even the Collision detection has to know if it's a terrain or an entity.


This is my first problem understanding how I detect differences. The second problem is the method getEntitesByComponents. Current this peace of code doesn't exists in a way I would use it. The basic concept I current use is the one here. The Structure is a Map<String, Map<Id, Component>>. The String is the Component class, which maps to a Map with the EntityId as key and the actual component as value.

if you want to get the components from an entity you ask map.get(Component.class).get(Entity#getId()) but you can't direct ask this structure to get all entities who refer to certain components without looping throw the map. So I need a proper management for the objects.

My first idea is to modify the entity class with a new attribute map and give each Component a unique static Id. So you can easily loop throw the entities and check with something like map.keySet().conatains(x) and y without checking one entity multiple times. But this seems not to be a common implantation. I often see a 'ComponentManager' but unfortunately the article I read doesn't describe the inner datastructure.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Have you considered looking at how other component-based engines handle this (strict ECS or otherwise)? It's very common to have a Light component separate from your Render component (not all rendered meshes have lights, not all lights have rendered meshes, so these are orthogonal concepts as far as your rendering system is concerned) \$\endgroup\$
    – DMGregory
    Jun 3 '20 at 0:36
  • \$\begingroup\$ Not rellay on a working engine (complete implementations) more in tutorials or abstracts but there aren't really helpful in detail issue's. Can you provide me with same links? In my current understanding the system only looks for components the entity has so I would add a new component and let the RenderSystem do the logic? \$\endgroup\$
    – Meeresgott
    Jun 3 '20 at 0:49
  • \$\begingroup\$ Why not? What problem does that introduce that you need help overcoming? (Also, consider that your RenderSystem might not be one monolithic thing. You might have one pass for all shadowcasting lights to build shadow maps, one pass for all opaque objects to render the G-Buffer, one pass for each light to render illumination & shadows, one pass for transparent objects with sorting, etc...) \$\endgroup\$
    – DMGregory
    Jun 3 '20 at 0:51
  • \$\begingroup\$ The Problem is the sorting. Current I have a method that returns a List of Entities with Component X,y,z and In the system I have a loop who do actions on the entity. Now I have to sort out. Begining with all lights so I loop through the List an look for a LightComponents and if they have one I'll calculate the shadow map etc. I haven't tried it yet but I'm afraid for the calculation costs. Do you know an efficient way to go here ? \$\endgroup\$
    – Meeresgott
    Jun 3 '20 at 6:39
  • \$\begingroup\$ Sounds like you should edit your question above to ask that specifically. \$\endgroup\$
    – DMGregory
    Jun 3 '20 at 10:53
0
\$\begingroup\$

By fetching entities in a system, you are working against the idea of ECS. ECS is more than just an alternative to polymorphism of game objects. It allows you to clearly define dependencies between Components (data) and Systems (actions), and store data in an optimized manner.

Systems should iterate over Components, not Entities. That way your render system only looks at components it needs (Render, Transformation, and Camera). Currently, there is nothing preventing a System from querying additional Components outside of its normal scope. Since the renderer likely only needs data in the components, accessing the entities violates the Law of Demeter.

How Entities are actually stored is a complex topic. Most of the optimizations are related to the layout of the entities in memory (Structure of Arrays vs Array of Structures). One of the benefits of iterate over Components is that all Components of a specific type can be stored in contiguous memory. Iterating over an array is way faster than accessing a Map. Either way, from your pseudo code it looks like you are using Reflection in Java, which is almost always slow.

Once you have systems with clear dependencies (which data it needs and which systems should be run in what order), you can start to do things like multithreading. For example, if you have two systems that work on two separate sets of components, there is no risk of conflicts or deadlocks when running them concurrently in different threads.

https://csherratt.github.io/blog/posts/specs-and-legion/ This article isn't a how-to on implementing ECS, but compares and contrasts two different ECS usable by the Amethyst. This its just two approaches to optimizing iteration over components, and they're definitely more. Optimizations are going to be tied to the language you are working in, and the level of control you have over memory.

\$\endgroup\$
5
  • \$\begingroup\$ when the System should nothing know about an entity how does it know that a specific RenderComponent is related to TransformationComponent? <br> \$\endgroup\$
    – Meeresgott
    Jun 4 '20 at 8:00
  • \$\begingroup\$ I'm not certain, that I could implement the right law of demeter in a good way. Even if I had a component manager that could give me some type of structure lets say a wrapper class for all components the rendersystems needs in a List. What prevents the CollisiondetectionSystem to call this method? Do I need some kind of authentication method? Or am I overthinking this problem? I'll update my pseudocode \$\endgroup\$
    – Meeresgott
    Jun 4 '20 at 8:18
  • \$\begingroup\$ @Meeresgott To your first question: Components should have an EntityId. If your RenderComponentList has a component with Id 1 and TransformationComponentList has a component with Id 1, then your RenderSystem should be able to work with it. If one of those is missing, your RenderSystem should ignore it -> dont render it. \$\endgroup\$
    – PSquall
    Jun 4 '20 at 13:37
  • \$\begingroup\$ @Meeresgott Second Question: your hypotherical ComponentManager does not need to know, what a specific system needs. If the RenderSystem wants a Transformation-, Render- and SoundComponent, then it should deliver those in lists, wrapper class, whatever. So, your ComponentManager only needs a function "give me the components A, B and D". And since you develop those systems, you dont need to secure this access. \$\endgroup\$
    – PSquall
    Jun 4 '20 at 13:45
  • \$\begingroup\$ Controlling access to Components is more related to code discipline than security, so an authorization system may be overkill. But as code grows, you will define rules like X system runs before Y system. If both systems both use component C, then you can make statements like Y system expects all updates to C be finished before it runs. But if you add Z system later that also uses C, and its explicit about it, you can update your system order. But if system Z never explicitly says it depends on C, and just gets it from an entity, it can easily break system Y without obvious errors. \$\endgroup\$
    – mobo
    Jun 4 '20 at 21:55
0
\$\begingroup\$

Well, if system needs to distinguish something, you either have a setting in your component (string ShaderOverride / enum RenderStyle), or just use different components. Latter is preferable, as it's more SOLID (no scope bloat).

Here is an example with multiple components.

  • MeshComponent simply points to a mesh file.
  • TerrainComponent stores terrain data in-line, which may be modified within game editor.
  • RenderingOptionsComponent provides additional parameters and overrides that are common for all mesh-like objects: shader override, shader parameters, visibility state, LOD bias and other performance filters.
  • LightComponent provides light information.
  • RenderingSystem asks other rendering sub-systems to provide data for rendering.
  • MeshRenderingSystem knows how to provide data for all MeshComponents on a map.
  • TerrainRenderingSystem knows how to provide data for all TerrainComponents on a map.
  • RenderingOptionsSystem knows how to override already provided data for given entity.
  • LightRenderingSystem provides light information from all LightComponents on a map.
  • PhysicsSystem asks other physics sub-systems provide data for simulation.
  • MeshPhysicsSystem knows how to get collision data from all MeshComponents on a map.
  • TerrainPhysicsSystem knows how to get collision data from all TerrainComponents on a map.

It's not necessary to split big systems into sub-systems, but makes mental model more clear, I hope.

ECS is pure database approach. There is no inheritance anymore, no OOP, just tables of data. Think like this: every aspect of every feature is either Component or System, sometimes both or even few of these. Want to render spline navigation for rendering purposes — ok, time to make SplineComponent, SplineNavigationComponent, SplineNavigationDebugRenderingSystem — or which of those you don't have yet.

\$\endgroup\$
3
  • \$\begingroup\$ The Idea is great to have multiple RenderSystem, but how can I define a SystemUpdate Order. I thought the clue from ecs are independent systems? \$\endgroup\$
    – Meeresgott
    Jun 16 '20 at 21:01
  • \$\begingroup\$ @Meeresgott Not ordering systems may cause all sorts of issues, ranging from subtle (like visuals lagging 1 frame behind physics) to serious (characters navigating into invalid tile). Regarding "how": it is already answered. I'd go with "declarative" approach, Unity uses it too (UpdateBefore, UpdateAfter, etc). \$\endgroup\$ Jun 16 '20 at 23:08
  • \$\begingroup\$ God it. So the rendering sub-systems are stages from a system. Great Idea I'm sure I can use it very well \$\endgroup\$
    – Meeresgott
    Jun 18 '20 at 10:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.