Creating A System

Before You Begin

If you have not already cloned the repository and created a working branch, please refer to the getting started guide.

If you are unfamiliar with the Entity Component System (ECS) pattern, take a look at ECS

Requirements

For a new world system to be recognized by the engine correctly, you must:

  1. Create a C++ class with the System suffix, ex: PlayerSystem
  2. Inherit from world::WorldSystem found in WorldSystem.h
  3. Create a constructor that passes string name, World * world to the base WorldSystem constructor with the same arguments
  4. Override void Update(double & elapsed, RegionSet regions)
  5. Register the system in World.cpp like so:

    void World::RegisterSystems()

    {

           //----------------------------------------------------------------

           // Register world systems

           REGISTER_SYSTEM(PlayerSystem);

           REGISTER_SYSTEM(AiSystem);

           REGISTER_SYSTEM(AgentSystem);

           REGISTER_SYSTEM(TerrainSystem);

           REGISTER_SYSTEM(BuildingSystem);

           REGISTER_SYSTEM(MovementSystem);

           REGISTER_SYSTEM(CollisionSystem);

           REGISTER_SYSTEM(AgentFoodSystem);

           REGISTER_SYSTEM(TestSystem);

           REGISTER_SYSTEM(ActionSystem);

           REGISTER_SYSTEM(PlantSystem);

           REGISTER_SYSTEM(ItemSystem);

           REGISTER_SYSTEM(FoodSystem);

           REGISTER_SYSTEM(ResourceSystem);

           REGISTER_SYSTEM(ConstructSystem);

           REGISTER_SYSTEM(DeferredTaskSystem);

           REGISTER_SYSTEM(AtmosphereSystem);

     

           SM.InitializeSystems();

    }

    Don't forget to include your system's header file at the top of World.cpp
  6. Create a JSON configuration file at Config\World\System\[the name of your system class].json (without the brackets of course) This file must have an enabled and updatePeriod property at the very minimum
    { "enabled" : true, "updatePeriod" : 1 }

Example

The following example system implements a shoot mechanic for agents. Any agent that is holding an item and activates the "Debug" action, which is bound to 'v' for players, will shoot the item in their look direction.

Upon completion of the steps in the requirements section above, you should have the following files

#pragma once

#include "WorldSystem.h"

 

namespace world {

       class ShootSystem : public WorldSystem

       {

       public:

              ShootSystem(string name, World* world);

              // Inherited via WorldSystem

              virtual void Update(double& elapsed, RegionSet regions) override;

       };

}

 

#include "pch.h"

#include "ShootSystem.h"

 

namespace world {

       ShootSystem::ShootSystem(string name, World* world) : WorldSystem::WorldSystem(name, world)

       {

       }

 

       void ShootSystem::Update(double& elapsed, RegionSet regions)

       {

       }

}

{ "enabled" : true, "updatePeriod" : 1 }

Working with Entities

The following code implements this shoot mechanic through idiomatic use of engine facilities like thread synchronization blocks BeginSync/EndSync, BeginDefer/EndDefer and the World Entity Manager EM, which is used for creating, iterating, and deleting world entities

void ShootSystem::Update(double& elapsed, RegionSet regions)

       {

              BeginSync(

                     // This is just a unique name given to this task

                     "Shoot_Update",

                     // We are going to read data from the Agent component

                     Read(Agent),

                     // We are going to write data to the Agent component

                     Write(Agent),

                     // Get a set of region IDs that this task applies to

                     EM->GetIdSet(regions)

              )

                     // Create an empty entity cache for Agent components

                     auto entities = EM->NewEntityCache<Agent>(regions);

                     // Populate the cache with entities that have an Agent component

                     EM->UpdateCache(entities);

                     // Easily iterate over each entity in the cache

                     for (auto& entity : entities) {

                           // Get a reference to this entity's Agent component

                           Agent & agent = entity.Get<Agent>();

                           // Test if the agent has an item and is also performing the Debug action

                           if (agent.Item && agent.Actions & (ActionMask)Action::Debug) {

                                  /* Begin/End Defer blocks are similar to Begin/End Sync, but

                                  these tasks are performed asynchronously at the end of every update*/

                                  BeginDefer(

                                         "Shoot_ShootItem",

                                         0,

                                         EM->GetSignature(agent.Item) | Write(Movement),

                                         EM->GetRegionSet(agent.Item)

                                  )

                                         // Add a Movement component with a velocity in the direction the agent is looking

                                         EM->AddComponent(agent.Item, Movement(agent.Look * 5.f));

                                         // Held items have their collision disabled, so we have to re-enable it

                                         EM->GetEntity<Collision>(agent.Item)->Get<Collision>().Enabled = true;

                                  EndDefer

                                  // VERY IMPORTANT! - Don't forget to remove the item from the agent's grasp

                                  agent.Item = 0;

                           }

                     }

              EndSync

       }