After many hours of confusion on how I should implement my
Entity System; it has finally been completed (at like 4 in the morning)

. This framework is based of the Java Entity System, Artemis, however it's a bit different with the way it is implemented, but you will find similarities. Unfortunately it is not entirely complete, I believe the only thing left to do is the SceneDirector, however, that's not really a required class in the framework, it's more of an add-on to help you manage Scene* objects.
*If you are familiar with the Artemis framework, a Scene is a World.
Features:- Unlimited component-types
- Fast retrieval of components
- Creation of components, entity-systems and entity-managers through a string
If you are going to try this framework out, please submit ANY bugs and feel free to add suggestions. I have put
issues in my
BitBucket repo available for the public, so please
submit bugs/suggestions there. There are no known bugs at the moment, I have resolved them.
NOTE:If you are unsure what an Entity System is, I recommend reading this basic tutorial and preferably a blog, such as
this one, before reading the tutorial, which describes what one is.
Basic Tutorial for usage:SceneWhat is a Scene?A
Scene resembles a
Scene, or the World in your game. It has multiple
Entity objects inside of it. It can
create,
destroy,
activate,
enable and
disable entities.
EntityWhat is an Entity?An
Entity is a game object, they describe objects within your
Scene. Such as a character, model, particle system, etc.
Creating an EntityTo create an
Entity, you must first have a
Scene object created.
Scene scene; // create a Scene
Now we can create one by calling the method
Scene.createEntity();
This method returns an EntityPtr, so we must store it in a variable (if we don't we'll actually get a leak, perhaps I should alter this behaviour):
EntityPtr entity = scene.createEntity(); // create an Entity
Now we may use this
Entity to resemble complex objects in our game.
Common
Entity functions:
-
enable() - enables the
Entity (entities are enabled by default, this will make
EntitySystem objects possibly process this
Entity)
-
disable() - disables the
Entity (this will make
EntitySystem objects
not process this
Entity)
-
kill() - kills the
Entity-
isEnabled() - returns true if the
Entity is enabled
ComponentsCreating custom Components to attach to our Entity objectA
Component is what represents data for an
Entity, this component may store transform information (position, rotation, scale, etc.), how fast the Entity may be going (it's velocity), collision data, or anything else you can think of.
To create a Component, you must publicly derive from the class
ac::es::Component, and you MUST define the macro
AC_ES_COMPONENT(classname) INSIDE your
Component. You may also define the optional macro,
AC_ES_REGISTER_COMPONENT(classname) (outside the namespace out of where the class is defined), this is used for instantiating Components through a string. For example:
namespace exampleNamespace
{
class VelocityComponent
: public ac::es::Component
{
AC_ES_COMPONENT(VelocityComponent) // this macro is REQUIRED
public:
vec3 velocity; // the velocity of the object
// constructors etc.
};
}
AC_ES_REGISTER_COMPONENT(exampleNamespace::VelocityComponent) // optional macro
The optional macro, as stated earlier, is used to instantiate components through a string. What I mean by this is:
Component* velocityComp = Component::Create("exampleNamespace::VelocityComponent");
I recommend if possible, creating a Component with the new operator, instead of through a string. I only made this possibly for loading/saving components to a file.
Modifying our Entity objectYou may modify the Entity object, by adding/removing components. After you have added/removed components from an Entity, you must activate it or refresh it (if activate has been called previously).
Adding a Component to an EntityTo add a Component to an Entity, simply call the method addComponent, through the Entity object.
For example:
entity->addComponent(new VelocityComponent(10, 0, 0)); // make it move 10 units in the x direction by default
An Entity may only have
one type of component attached; it is not possible to have multiple components of the same type attached to an entity.
Removing a Component from an EntityTo remove a Component from an Entity, simply call the removeComponent method in the Entity object. You may remove a Component by it's type or passing in the Component you wish to remove as a paramater. Example:
Removing a Component by type:
entity->removeComponent<VelocityComponent>();
Removing a Component by passing it in as a parameter:
entity->removeComponent(someComponentPtr);
Determining whether an Entity contains a Component
To determine whether an Entity contains a Component, you simply call the containsComponent method for the Entity, this is very much like the removeComponent method, as you may determine if a component exists by type OR by passing in a paramter. Example:
Determining if a Component exists by type:
if(entity->containsComponent<VelocityComponent>())
{
// ... do something with the velocity component
}
Determining if a Component exists by passing it in as a parameter:
if(entity->containsComponent(someComponentPtr))
{
// .. do something
}
SystemsWhat is a System?A
system is where all the magic happens, what I mean by magic is, it defines logic for how the game will play. For example, you may have a
MovementSystem which moves all
Entity objects with the
VelocityComponent and
PositionComponent components attached to it.
Defining a custom SystemMuch like defining components, you must derive from a base class and define two macros. The base class can either of:
EntitySystem - this lets you loop through all the entities to process (some of the entities you are processing may be null pointers)
EntityProcessingSystem - processes one Entity at at time (these entities that you process one at a time will NOT be pointers)
I suggest inheriting from the
EntityProcessingSystem whenever you can, as it's much simpler since you do not have to deal with the loop yourself, which will lead to less bugs.
The two macros that you must define are behave exactly the same as the
Component macros, except they are called:
AC_ES_ENTITY_SYSTEM(classname) and
AC_ES_REGISTER_ENTITY_SYSTEM(classname).
If you are processing the
EntitySystem class, in order to process entities, you must
override the
processEntities(const ac::es::EntityArray& entities) method, note that some of the Entities in the array may be a NULL pointer, so it's best to check before you do so. If you are inheriting from the
EntityProcessingSystem class, you must override the
process(ac::es::Entity& entity) method.
Filtering out ComponentsTo provide what type of Entity objects you would like to process, you set the
ComponentFilter for the
EntitySystem, you can do this in the constructor, by calling the parent's constructor and passing in a
ComponentFilter object.
For example, our
MovementSystem requires an
Entity with a
PositionComponent and
VelocityComponent:
namespace exampleNamespace
{
class MovementSystem
: public ac::es::EntityProcessingSystem
{
AC_ES_ENTITY_SYSTEM(MovementSystem)
public:
MovementSystem()
: ac::es::EntityProcessingSystem(ac::es::ComponentFilter::Requires<PositionComponent>().requires<VelocityComponent>())
{
}
protected:
virtual void process(Entity& e)
{
PositionComponent* positionComp = e.getComponent<PositionComponent>();
VelocityComponent* velocityComp = e.getComponent<VelocityComponent>();
// translate the object
positionComp->position += velocityComp.velocity;
}
};
}
// register the MovementSystem
AC_ES_REGISTER_ENTITY_SYSTEM(exampleNamespace::MovementSystem)