Skip to content

Stateful Systems

A stateful system is a system that keeps track of entities and components. The stateful system is able to filter out which entities and components it is tracking by including some criteria that they must match to be tracked, this is referred to as an evaluator.

Stateful systems group an entity and their components together by using a system entity which is a wrapper around the entity and components attached to it. The system entity includes some useful helper methods to allow easy addition, retrieval, update, and deletion of entities and components.

The StatefulSystem class is abstract, meaning that there are derived classes that provide specific ways to handle storing the system entities, for example in a vector, or a map.

System Entity

A system entity is a wrapper around an entity and any components that are attached to it. The system entity allows for easier management of an entity and its components, with addition, retrieval, updating, and removal of components, while also allowing easy access to the entity itself.

Implementations

JamJar provides a couple of standard stateful system implementations that you can use.

Vector System

The VectorSystem stores the system entities in a std::vector.

Accessing Entities

Vector system entities can be accessed by using the entities std::vector.

for (auto &entity : this->entities) {
    ...
}

Evaluator

A vector system must include an evaluator, this is a function that filters which entities and components the vector system should track. The evaluator takes as arguments an entity and a vector of all of its components, and must return true if it should be tracked as a system entity, or false if it should not be tracked.

An example of a vector system evaluator looks like this:

bool JamJar::Standard::_2D::SpriteSystem::evaluator(Entity *entity,
                                                    const std::vector<JamJar::Component *> &components) {
    bool hasSprite = false;
    bool hasTransform = false;
    for (const auto &component : components) {
        if (component->key == JamJar::Standard::_2D::Sprite::KEY) {
            hasSprite = true;
        }
        if (component->key == JamJar::Standard::_2D::Transform::KEY) {
            hasTransform = true;
        }
        if (hasSprite && hasTransform) {
            return true;
        }
    }
    return false;
}

This evaluator is used in the SpriteSystem to make sure that only components that have a Sprite component and a Transform component are tracked.

Map System

The MapSystem stores the system entities in a std::map that maps an entity ID to a system entity.

Accessing Entities

Map system entities can be accessed by using the entities std::map, with the key being the ID of the entity.

auto a = this->entities.at(entityID);

Evaluator

The MapSystem uses the same evaluator as the VectorSystem.

Single Entity System

The SingleEntitySystem tracks only a single entity using the std::optional structure, this could be useful for camera operations when a game has a single camera to manipulate.

The SingleEntitySystem will track a single entity, and will be initially set to no value for the optional entity, if an entity is created that matches it will assign that entity to the optional value, if another entity is registered that matches it will override the initial value (unless the exceptionOnOverwrite flag is passed, in this case a SingleEntitySystemOverwriteException will be raised). If the entity no longer matches then the optional will be set back to no value.

Accessing Entities

The system's single entity can be accessed by using the entity std::optional.

To check if there is a value set:

if (this->entity.has_value()) {
    ...
}

To access the value:

auto camera = this->entity.value();

Evaluator

The SingleEntitySystem uses the same evaluator as the VectorSystem.

Bucket System

The BucketSystem stores the system entities in a std::map of std::vector - with the map keys being any arbitrary uint32_t that the developer can choose, for example keeping track of entities with a camera and player entities in two separate buckets.

Accessing Entities

Bucket system entities can be accessed by using the bucket key to look up the bucket, then the entities in the bucket can be iterated over since they are in a std::vector.

auto players = this->entities.at(PLAYER_BUCKET);

for (auto& player : players) {
    ...

Evaluator

A bucket system must include an evaluator, this is a function that filters which entities and components the bucket system should track. The evaluator takes as arguments an entity and a vector of all of its components, and must return either a bucket key as a unit32_t or an empty optional value to mark that the entity should not be tracked.

An example of a bucket system evaluator looks like this:

std::optional<uint32_t> CustomSystem::evaluator(JamJar::Entity *entity, const std::vector<JamJar::Component *> &components) {
    for (const auto &component : components) {
        if (component->key == JamJar::Standard::_2D::Camera::KEY) {
            return std::optional<uint32_t>(CustomSystem::CAMERA_BUCKET);
        }
        if (component->key == Player::KEY) {
            return std::optional<uint32_t>(CustomSystem::PLAYER_BUCKET);
        }
    }
    return std::optional<uint32_t>(std::nullopt);
}

This evaluator tracks entities that have either a Camera or a Player component, splitting them into two buckets.

Bucket Map System

The BucketMapSystem stores the system entities in a std::map of std::map - with the outer map keys being any arbitrary uint32_t that the developer can choose, for example keeping track of entities with a camera and player entities in two separate buckets, and the inner map keys being entity IDs.

Accessing Entities

Bucket system entities can be accessed by using the bucket key to look up the bucket, then the entities in the bucket can be accessed using the inner std::map which is mapped to entity IDs.

auto projectiles = this->entities.at(PROJECTILE_BUCKET);

auto collisionProjectile = projectiles.at(collision.aID);

Evaluator

The BucketMapSystem uses the same evaluator as the BucketSystem.