Skip to content

Components

A component is some data that is attached to an entity. A component in isolation does not do anything, it must be attached to an entity to have any effect.

Adding Components to an Entity

Components can be added to an entity by using the Add method on an Entity.

#include "geometry/vector_2d.hpp"
#include "entity/entity.hpp"
#include "standard/2d/transform/transform.hpp"

using JamJar::Vector2D;
using JamJar::Entity;
using JamJar::Standard::_2D::Transform;

...

auto player = new Entity(messageBus);
player->Add(new Transform(Vector2D(0, 20)));

The above code will add a Transform component to the player entity, setting its position to x: 0, y: 20.

The component is added as a raw pointer, but behind the scenes this will be picked up by the engine and the memory managed - do not delete component pointers, let JamJar handle that.

System Entities

If interacting with entities inside a System the entities are normally wrapped in a SystemEntity which also contains an Add method which can be used in exactly the same way as on an Entity.

#include "geometry/vector_2d.hpp"
#include "entity/entity.hpp"
#include "standard/2d/transform/transform.hpp"

using JamJar::Vector2D;
using JamJar::Entity;
using JamJar::Message;
using JamJar::Standard::_2D::Transform;

...

void SomeSystem::OnMessage(Message *message) {
    MapSystem::OnMessage(message);
    switch (message->type) {
    case SOME_MESSAGE: {
        auto player = this->entities.begin()->second;
        player->Add(new Transform(Vector2D(0, 20)));
    }
    }
}

The above code will add a Transform to an existing SystemEntity when the SOME_MESSAGE message is received.

Interacting with Components

Most interactions with Components should occur inside a System and should be done through the SystemEntity - the Get method on the SystemEntity is a template function that takes a Component type and returns any Component associated with the entity. This makes use of the Component KEY static constant so it is important any custom component provides this.

#include "geometry/vector_2d.hpp"
#include "entity/entity.hpp"
#include "standard/2d/transform/transform.hpp"

using JamJar::Vector2D;
using JamJar::Entity;
using JamJar::Message;
using JamJar::Standard::_2D::Transform;

...

void SomeSystem::OnMessage(Message *message) {
    MapSystem::OnMessage(message);
    switch (message->type) {
    case SOME_MESSAGE: {
        auto player = this->entities.begin()->second;
        auto transform = entity.Get<Transform>();
        transform.position.x = 5;
    }
    }
}

Creating a Component

You can create your own component by extending the JamJar::Component base class, every component must also provide a unique KEY static constant which is used to distinguish between components.

#ifndef YOUR_COMPONENT_HPP
#define YOUR_COMPONENT_HPP

#include "component/component.hpp"

class YourComponent : public JamJar::Component {
  public:
    constexpr static uint32_t KEY = JamJar::hash("your_component");
    explicit YourComponent();
};

#endif

The code above is a header that describes a custom component, note the KEY static constant, this is required and should be a unique hash value.

#include "your_component.hpp"

YourComponent::YourComponent() : Component(YourComponent::KEY) {}

The code above is the implementation of the custom component.

Identifying a Component

A component is identified by an unsigned 32-bit integer, which should be largely unique. To help create unique identifiers a compile time hashing function is used, for example:

constexpr static uint32_t KEY = JamJar::hash("jamjar_transform");

This tries to create a pseudo unique identifier for the Transform component, however hash collisions could occur - this can be fixed by adjusting the string that is hashed slightly to produce a different result which will hopefully not collide.

Data

A component can hold any data, it could contain the position, scale, and rotation of an entity (Transform component), it could hold the number of lives a player has, it could hold sprite information for drawing an entity with details about color, texture, and shape.

The component is the single source of truth for this data, and multiple systems may read and write to the same component - this is a strengh of ECS, shared data can be read and modified by multiple systems.

Memory

When adding a new component using one of the Add methods on the Entity or SystemEntity these methods take a raw pointer to the component, then converts them to a unique pointer which JamJar manages. This means that the developer does not need to manage Component memory.

Where are Components Stored?

See the ComponentManager and EntityManager for details.