Wednesday, April 15, 2020

TECS - Tight Entity Component System

Hi readers!

 

I have just finished my Master's so I had a little extra time to work on side projects...

 

It's been a while since my last post regarding BitEngine. The framework is still in development, but I found a few things that I wanted to improve before resuming development. That is a new Entity Component System.

 

Today I present Tight Entity Component System (TECS), a small header-only library that manages entity and components in a memory-efficient way. This development of this library was motivated by the new design decisions made for BitEngine, which includes decoupling more from the standard OOP approach and take a more data-oriented design. 

 

Before implementing TECS I studied how other implementations work, especially EnTT and EntityX and read many articles about the subject around the internet. There are many libraries out there, however, many seem no be now dead or they use resources in a different way from my current intentions. Also, some libraries have coupled the entity and component management with systems, which I also don't like. EnTT is an awesome library, but it has the same memory allocation problem, although there was even a discussion about the subject (here), I don't think it was implemented yet. I could probably just used one existing library and find workarounds for it to work in my case, or even modify the libraries so they would work with arena allocations. However, by doing that I would be missing part of the fun on implementing such an interesting system!

 

The library requires the user to provide a pointer of memory and size and specifies a few other parameters such as entity and component limits and a TypeProvider to enumerate the component types. The library has a type-safe interface, however, it also aims to expose an API to be used without types. Making it capable of dynamic run-time component manipulation without known types.

 

Some interesting features:

    • All allocations are constrained to a tight memory arena. Want to clear everything? just drop the arena and initiate the ECS again. This also helps in keeping your ECS working across boundaries.
    • Component references are guaranteed to be valid, independently if you add or remove more entities. Of course, if the entity or the component is removed, that reference no longer makes sense (you can still write data to it, but it might affect other entities) or components.
    • Components are tightly packed in memory (as much as possible) in order to be cache-friendly when iterating over them.
    • Compile-time type-safe API. Run-time types are planned to be supported as well.

     

    The library is currently functional and I have a few test cases to ensure it's behaviour. My initial goal was to make it Arena Allocator friendly and later performance efficient. Hopefully, it is already fast enough for relevant cases but I still need to perform some actual benchmarks to assess that.

     

    The library is very simple to use, first, we need to include the library and define some components:


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    #include <iostream>
    
    #include <tecs/tecs.h>
    
    struct Position {
        float x;
        float y;
    };
    
    struct Velocity {
        float x;
        float y;
    };
    
    CREATE_COMPONENT_TYPES(TypeProvider);
    REGISTER_COMPONENT_TYPE(TypeProvider, Position, 1);
    REGISTER_COMPONENT_TYPE(TypeProvider, Velocity, 2);
    

     

    Then we just need to allocate some memory and start using the ECS:


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    int main()
    {
        // Feel free to allocate/manage the memory anyway you want.
        constexpr u32 MEMORY_SIZE = 1024 * 1024 * 8;
        char* memory = new char[MEMORY_SIZE]; // Allocate 8MB
    
        const u32 maxEntities = 1000;
    
        tecs::Ecs<TypeProvider, 8> ecs(tecs::ArenaAllocator(memory, MEMORY_SIZE), maxEntities);
    
        auto e = ecs.newEntity();
        ecs.addComponent<Position>(e) = {1, 1};
        ecs.addComponent<Velocity>(e) = {1, 1};
    
        e = ecs.newEntity();
        ecs.addComponent<Position>(e) = {1, 1};
        ecs.addComponent<Velocity>(e) = {2, 2};
    
        ecs.forEach<Position, Velocity>([](tecs::EntityHandle e, Position& pos, Velocity& vel) {
            pos.x += vel.x;
            pos.y += vel.y;
        });
    
        // Show entity positions:
        ecs.forEach<Position>([](tecs::EntityHandle e, Position& pos) {
            std::cout << "Entity: " << e << ", Position: " << pos.x << ", " << pos.y
                      << std::endl;
        });
    
        delete[] memory;
    }
    

     

    The next step is to replace the current Entity System used in BitEngine by TECS.

     

    Library on GitHub: MateusMP/TightECS

     

     

     

    Saturday, February 22, 2020

    Guerra² - A 2D Multiplayer Platform Shooter


    So, I have been very busy with my Master's degree and haven't had much time to update the blog (not that the updates were frequent anyways...)

    But recently I realized that one of the coolest projects I developed in the past is not here! So I'm republishing it!

     


    Guerra² would be read as "War²" in English. The name is a joke with the words "War" and "Squared", which relates to the game art style (since most elements are... Squares.)

    The gameplay was inspired in the game Soldat. And I developed this around 2011 if I remember correctly. This was one of my first, from the ground up, implementations of an Online Multiplayer game. At the time I had a PhP server hosting a lobby of rooms to play. But that server is now probably dead. It is still possible to play with CPUs or connect via IP to other players.

    You can change your game looks with a set of predefined skins and colors with a total of 590.625 different combinations!!!

    There are a few standard game modes, such as Capture The Flag, Death Match and Team Death Match. You can also choose to play by score or timed games.

    In order to make maps for the game, I also developed a Map Editor, which allows the creation of maps for all game styles. If you join a server that is currently using a map that you don't have installed, the game will download the map for you.

    Game controls are:
    WASD - Character Movement (W jump)
    Mouse - Aim and click to shoot
    Space Bar - Granade, hold to aim
    1~7 - Change weapon
    F4 - To play in full-screen

    Use the flags in the menu to change the game language.

    Guerra² Download

    Guerra² Map Editor Download






    Thinking of maybe doing a remake of this game...?