3
\$\begingroup\$

An Entity Component System works by grouping various components around a central ID, aka Entity. Entities and Components do not have any code, they are simply POD containers. Rather, the code lies in a system, which takes in a group of components and does work on them. I've written out an EntityManager class and a templated object pool(for Components). Is there anything I could do better with these?

EntityManager.h

#pragma once
#ifndef ACORNEMANAGER
#define ACORNEMANAGER


/*
    The EntityFactory is responsible for churning out valid Entities when needed. 
    The maximum number of Entities is the size of an unsigned long long int - or 2^64, which is roughly 18 quintillion.
    If one were to spawn 1000 entities per second, it would take 584 million years to use up every entity ID. Therefor, entity IDs do not need to be recycled - there will never be a collision.
*/


#include "DataTypes.h"
#include "Definitions.h"

#include <stdint.h>
#include <assert.h>
#include <vector>

namespace Acorn
{

    class EntityFactory
    {
        //Constructor and destructor
    public:
        EntityFactory() : currentID(0) {}
        ~EntityFactory() {}

        //Generation Methods
    public:

        //Generates a single Entity
        struct Entity Generate()
        {
            //Abort the program if currentID is somehow equal to ACORN_ULLI(maximum value for unsigned long long int) and then wonder how we realistically reached this value
            assert(currentID != ACORN_ULLI);

            //Create an entity with a UID of currentID
            Entity e{ currentID };

            //Increment currentID
            ++currentID;

            //Return the entity we created
            return e;
        }

        //Only allow for up to 65535 entities at a time
        std::vector<struct Entity> Generate(const uint16_t n)
        {
            //Make sure we aren't trying to generate 0 entites, because that's just silly!
            assert(n > 0);
            //The stack that will be returned
            std::vector<struct Entity> rStack;

            //Loop backwards(more efficient), creating and pushing a new entity each iteration
            for (int i = n; i > 0; i--)
            {
                Entity e = Generate();
                rStack.push_back(e);
            }

            //Return the stack
            return rStack;
        }

    private:
        //The current ID that will be spit out for an entity
        uint64_t currentID;
    };


}// End namespace Acorn


#endif

And my object pool

#pragma once
#ifndef ACORNLAZYPOOL
#define ACORNLAZYPOOL

/*
Acorn::Collection::LazyPool<T>
A LazyPool is an objectpool with lazy allocation, ie it will only allocate objects as needed.
Compared to a GreedyPool, which allocates all objects at once.
Object lookup in a LazyPool is done by looking at some ID belonging to the desired object.
The ID is the index of the sparse array, which holds the position in the compact array of the object.
EXAMPLE
ID Wanted: 5
Sparse Array
 1  2   3   4  5   6   7  8 
[3][2][NaN][0][1][NaN][4][5]
Compact Array
[ID=4][ID=5][ID=2][ID=1][ID=7][ID=8]
*/

#include "Definitions.h"
#include "Errors.h"


#include <stdint.h>
#include <vector>
#include <unordered_map>

namespace Acorn
{
namespace Collection
{

    template<class T>
    class LazyPool
    {
        //Constructor and Destructor
    public:

        //Getter/Setter methods
    public:

        //Adds a single object based off of the entity
        void Add(struct Entity e)
        {
            //Create some object t, and add it to the compact array
            T t;
            compact.push_back(t);
            //Set the lookup key to the entity UID, and set the value to the position of the element just added
            lookup[e.UID] = compact.size() - 1;
        }

        //Adds multiple objects based off the entity vector
        void Add(std::vector<struct Entity> es)
        {
            //Loop thru every entity in the es vector
            for (auto i : es)
            {
                //Create some object t, and add it to the compact array
                T t;
                compact.push_back(t);
                //Set the lookup key to the entity UID, and set the value to the position of the element just added
                lookup[i.UID] = compact.size() - 1;
            }
        }

        //Adds and returns the object generated
        T Generate(struct Entity e)
        {
            //Add the object
            Add(e);
            //Return the newly created object(will be the backmost object)
            return compact.back();
        }

        //Adds and returns a collection of objects generated
        std::vector<T> Generate(std::vector<struct Entity> es)
        {
            //The return vector
            std::vector<T> rVec;
            //Generate each object, and add it to the return vector
            for (auto i : es)
            {
                rVec.push_back(Generate(i));
            }

            //Return the vector
            return rVec;
        }

        //Deletes a single entity
        void Delete(struct Entity e)
        {
            //Only do a swap if the compact array is greater than 1
            if (compact.size() > 1)
            {
                //Set the compact array object at the ID you want to remove to the very back of the array 
                compact.at(lookup[e.UID]) = (uint64_t)compact.back();

                //Set the swapped object's lookup ID to it's new position
                lookup[compact.size() - 1] = lookup[e.UID];

                //Remove the lookup entry
                lookup.erase(e.UID);

                //Remove the last element of the compact array
                compact.pop_back();
            }
            //There is only 1 element in the pool
            else
            {
                //Remove the lookup entry
                lookup.erase(e.UID);

                //Remove the last element of the compact array
                compact.pop_back();
            }

        }

        //Deletes multiple objects based off the entity vector
        void Delete(std::vector<struct Entity> es)
        {

            for (auto i : es)
            {
                //Only do a swap if the compact array is greater than 1
                if (compact.size() > 1)
                {
                    //Set the compact array object at the ID you want to remove to the very back of the array 
                    compact.at(lookup[i.UID]) = compact.back();

                    //Set the swapped object's lookup ID to it's new position
                    lookup[compact.size() - 1] = lookup[i.UID];

                    //Remove the lookup entry
                    lookup.erase(i.UID);

                    //Remove the last element of the compact array
                    compact.pop_back();
                }
                //There is only 1 element in the pool
                else
                {
                    //Remove the lookup entry
                    lookup.erase(i.UID);

                    //Remove the last element of the compact array
                    compact.pop_back();
                }
            }

        }

        //Sets the compact array
        void Set(const std::vector<T> rVal)
        {
            compact.swap(rVal);
        }

        //Sets a section of the compact array
        void Set(const std::vector<T> rVal, const std::vector<struct Entity> es)
        {
            //For every entity, set its component
            for (unsigned int i = 0; i < es.size(); ++i)
            {
                compact[lookup[es[i].UID]] = rVal[i];
            }
        }

        //Sets a single object that corrosponds with a single entity
        void Set(const T t, const struct Entity e)
        {
            compact[lookup[e.UID]] = t;
        }

        //Gets the compact array
        std::vector<T> Get()
        {
            return compact;
        }


        //Gets a section of the compact array, based on the entities passed in
        std::vector<T> Get(std::vector<struct Entity> es)
        {
            //The return vector
            std::vector<T> rVec;
            //For every entity in the es array
            for (auto i : es)
            {
                //Add the corrosponding component to the return vector
                rVec.push_back(compact[lookup[i.UID]]);
            }

            //Return the return vector
            return rVec;
        }

        //Gets a single object, based on the entity passed in
        T Get(struct Entity e)
        {
            return compact[lookup[e.UID]];
        }

        //Information methods
    public:
        //Gets the size of the compact array
        uint64_t Size()
        {
            return compact.size();
        }

        //Private variables
    private:
        std::unordered_map<uint64_t, uint64_t> lookup;
        std::vector<T> compact;
    };

} //End namespace Collection
} //End namespace Acorn




#endif
\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.