Skip to content
This repository has been archived by the owner on Mar 12, 2021. It is now read-only.

Using the Library

Kyle edited this page Dec 20, 2016 · 12 revisions

This page will teach you the basics about on how to use the anax library. In this tutorial, we will be using the namespace anax, since that is where all the code for the anax library is located.

Worlds

A World is practically an entity system. It contains the entities and all systems within the entity system.

Creating a World

To create a World object, you simply create one as you would with any other normal variable.

// Allocation on the stack
World world_on_stack;

// Allocation on the heap
World* world_on_heap = new World;

Using a World Object

Now that you have a World, what can you do with it? Here's a brief list of the common functions which will you will most likely use:

  • refresh - Refreshes the World (you must call this method every frame)
  • createEntity - Adds an entity to the World
  • getEntity - Returns an entity by index (you have to pass an index, not a whole id, ie: getEntity(e.getId().index) )
  • killEntity - Kills an entity
  • addSystem - Adds a system to the World
  • removeSystem - Removes a system from the World
  • clear - Clears the World; removes all entities and systems attached to it (does not clean up memory for you)

NOTE

These functions will be described in greater depth further along this tutorial.

Entities

As described earlier, an Entity is a object resembles something inside your game. This could be a player character, NPCs, weapons, vehicles, a map; just about anything you can think of.

Creating an Entity

An entity can only be created through a World object. You must call the createEntity object and store the value. If you fail to store the value, that entity will only be able to be accessed by the ID (which you probably won't know). Therefore, it is suggested that you always store the return value of createEntity from a World object.

createEntity returns an Entity object, by value.

World world;
Entity entity1 = world.createEntity();
auto entity2 = world.createEntity();

NOTE

If you can, I reccomend using the auto keyword wherever you can when you create an entity. That way if in future versions the return value is changed, your code will not be harmed.

Using your Entity

Here are some common functions you will use with an Entity:

  • addComponent - adds a component to an entity
  • removeComponent - removes a component from an entity
  • containsComponent - determines whether an entity contains a component
  • activate - activates the entity, which ensures the entity will be added to any systems that require it
  • deactivate - deactivates the entity, which ensures the entity will be removed from any systems that it is added to
  • kill - Kills the entity (is equivalent to world.killEntity(*this))

NOTE

These functions will be described in greater depth further along this tutorial.

Killing Entities

To remove an entity from a world, you must kill it. You may kill an entity by calling kill on the entity or by calling killEntity on the world. Alternatively you may remove all components from the Entity, deactivate it and re-use it for another entity in your world.

If you call the kill or killEntity methods. The entity and it's components will not be destroyed until the refresh method of the World is called.

Checking Entity State with isValid and isActivated

An Entity object is really a wrapper with functions to work on the entity data stored in the World object. To make sure this wrapper is up to date and the underlying entity has not been killed since this wrapper was created, call isValid. isValid will return true only if the Entity object given has a valid reference to the data in the World object. This does not tell you anything about the state of the underlying entity.

The entities in anax can be deactivated or activated. To check if an entity is activated, call isActivated. This will return false if isValid would return false.

Components

A component is an object that stores data for an Entity, all components are inherited from the Component class.

Defining a Component

To define your own component, you must publicly derive from the class Component, where T is your component type. This follows the curiously recurring template pattern.

class VelocityComponent
	: public Component
{
public:

	// the velocity of the object
	vec3 velocity; 
};

The Component class is defined wthin the anax/Component.hpp header-file.

Using Components with your Entities

Adding Components to an Entity

Now that you have an entity, and you know how to create your own components, what you want to do now is: add components to the entity. Use the method addComponent to add a Component to an Entity.

World World;

Entity exampleEntity = World.createEntity(); // create the Entity

// add Components to it
exampleEntity.addComponent(new SomeComponentType);
exampleEntity.addComponent<AnotherComponentType>();

// activate the Entity, since we have all the Components we want on it
exampleEntity.activate();

Removing a Component from an Entity

To 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 parameter.

EXAMPLES

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. containsComponent has the same syntax as removeComponent method.

EXAMPLES

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
}

Activating an Entity

After you have added or removed components from an Entity, you must activate it (or re-activate it) by calling the activate method. Activating an Entity simply notifies every systems attached to your world. If an Entity is not activated it will not be processed by any systems attached to your world

// Add/remove components
entity.addComponent(new MeshComponent);
entity.addComponent(new PositionComponent);
entity.removeComponent<VelocityComponent>();

// re-activate the Entity
entity.activate();

Entities will not be attached to any other systems, until the next frame (when refresh is called).

Deactivating an Entity

Sometimes you might want to deactivate an Entity, to make objects disappear for some period of time. To deactivate an Entity, you simply call the method: deactivate(). Much like kill and activate, entities will not be deactivated until the next time refresh is called.

entity.deactivate();
// Now the entity's components will not be touched by any systems

Systems

A system is where all the magic happens; it defines logic for particular parts of your game. For example, a MovementSystem might move entities with Velocity and Position components.

There are no methods to update/process the entities within a system. A system is merely a "hard-coded container" that contains entities with specific components. You can of course, add custom methods within the system to update/process the entities (which you probably will want to do).

Defining your own System

To define a system, you must publicly derive from System<T>, where T is the system you are defining. This follows the curiously recurring template pattern.

class MovementSystem
	: public System<MovementSystem>
{
};

Filtering out Components

To provide what type of Entity objects you would like to process, you set the ComponentFilter of the system. You can only do this in the constructor, as it is read-only for all other classes (including the base class). Thus, you must call parent's constructor and pass a ComponentFilter object by value. A neat little typedef for the base system class is defined, it is called Base. This way you do not have to type System<X>(…) for every system you have.

For example, in our movement system it requires entities with position and velocity components, here's what it might look like:

class MovementSystem
    : public System<MovementSystem>
{
public:
    		
     MovementSystem()
         : Base(anax::ComponentFilter().requires<PositionComponent, VelocityComponent>())
     {
     }
			
    void update(double deltaTime)
    {
        auto entities = getEntities();
				
        for(auto i : entities)
        {
            process(i, deltaTime);
        }
    }

private:
    
    void process(Entity& e, double deltaTime)
    {
        PositionComponent& positionComp = e.getComponent<PositionComponent>();
        VelocityComponent& velocityComp = e.getComponent<VelocityComponent>();
        		
        // translate the object
        positionComp.position += velocityComp.velocity * deltaTime;
    }
};

Retrieving the Filtered Entities within the System

To retrieve the entities, if you didn't spot it in the previous example, you simply call the getEntities method.

Determing when an Entity is added and removed from a System

There are multiple overridable (virtual) methods, within the System class, to determine when an Entity is removed/added from a system. These methods are:

  • onEntityAdded - occurs when an Entity is added
  • onEntityRemoved - occurs when an Entity is removed

Adding a System to a World

To add a system to a World object, you simply call the method: addSystem from the world.

NOTES

  1. You can only attach one type of system to a world
  2. No memory is managed for you.

EXAMPLE

SomeSystem mySystem;
world.addSystem(mySystem);

Removing a System from a World

To remove a system from a world, simply call the method: removeSystem<T> from the world, where T is the type of system to remove.

EXAMPLE

World.removeSystem<SomeEntitySystemType>();

NOTES

There is no possible way to determine when a entity is destroyed/created by "notification". The reason behind this, is because I did not see any benefit from doing so. I may or may not add this in the future if it is really necessary. Or I may have it optional, just incase someone really needs it for a specific type of game. Even then, they themselves could do the signalling of when an entity is destroyed/created.