Aller au contenu

Composants

Un composant est un type de données enregistré dans le monde. Il représente un état par entité ; une valeur unique pour le monde doit plutôt être une ressource.

ECS_COMPONENT_DECLARE(Position, {
float x;
float y;
});
/* Une seule définition dans un fichier C. */
ECS_COMPONENT_DEFINE(Position);
ECS_COMPONENT_REGISTER(Position);

Les types C++ déclarés avec ECS_COMPONENT_DECLARE réutilisent l’identifiant C et son descripteur. Un struct C++ natif est enregistré automatiquement lors de sa première utilisation.

Les composants sont copiés dans une instance héritée par défaut. Déclarez un composant shared lorsque toutes les instances doivent lire la même valeur de la base :

ECS_COMPONENT_DEFINE(Material,
.inheritance = EcsInheritShared
);

Pour un composant C++ natif, utilisez l’option d’enregistrement équivalente :

ecs::component<Material>({
.inheritance = EcsInheritShared,
});

La politique est fixée lors de l’enregistrement du composant. Le mode owned copie la valeur effective pendant ecs_is_a() et ne la synchronise plus avec la base. Les champs inherited shared restent exclus des requêtes d’écriture.

ecs_set(entity, Position, { .x = 1.0f, .y = 2.0f });
Position *position = ecs_get(entity, Position);
position->x += 1.0f;
Position *maybe = ecs_try_get(entity, Position);
if (maybe != NULL) maybe->y += 1.0f;

ecs_get() et get<T>() supposent que le composant existe. ecs_set() et set() ajoutent le composant s’il est absent.

ecs_add(entity, Selected);
if (ecs_has(entity, Selected)) {
ecs_remove(entity, Selected);
}

Retirer un composant absent est sans effet. Ajouter ou retirer un composant est une modification structurelle : l’entité peut changer de table et les pointeurs de champs courants ne doivent pas être conservés à travers cette opération.

Disabled exclut une entité des requêtes normales. Abstract marque une base utilisée par l’héritage. Pour exposer les champs d’un type C++ à la réflexion, utilisez reflected(...) ; un type sans métadonnées reste tout de même stockable et interrogeable.

Les descripteurs dynamiques peuvent utiliser struct_desc pour fournir les métadonnées Sireflect nécessaires à l’explorateur REST et à la sérialisation.

ecs_with(component, required) et ecs::component_requires<Component, Required>() déclarent qu’un composant entraîne l’ajout d’un autre composant. Cette règle concerne les ajouts futurs et les cycles sont invalides.

Les hooks on_add, on_set et on_remove s’exécutent autour du cycle de vie du composant. Consultez la référence API pour les signatures et les durées de validité des pointeurs.

Disabled est un composant intégré. Lorsqu’il est ajouté à une entité, celle-ci est exclue des requêtes par défaut :

ecs_add(entity, Disabled);

Les requêtes, systèmes et observateurs ignorent les entités désactivées. Pour les inclure explicitement, demandez Disabled dans la requête :

ecs_query({
.components = { ecs_in(Position), ecs_filter(Disabled) },
});

Utilisez ecs_with() lorsqu’un composant doit entraîner l’ajout d’un autre :

ecs_with(ecs_id(Renderable), ecs_id(Transform));
ecs_add(entity, Renderable);
/* Transform est ajouté avant Renderable. */

La règle concerne uniquement les ajouts futurs et ne modifie pas les entités existantes. Les cycles d’exigences sont invalides.

Pour du code générique, enregistrez un composant avec un descripteur puis utilisez les fonctions _cid :

typedef struct { float x, y; } Position;
ecs_component_t position_id = ecs_component({
.name = "Position",
.size = sizeof(Position),
});
ecs_set_cid(entity, position_id, &(Position){ .x = 1.0f, .y = 2.0f });
Position *position = ecs_get_cid(entity, position_id);

Les macros de composants typés enregistrent les métadonnées utilisées par sireflect, sijson et l’explorateur REST :

ECS_COMPONENT(Position, {
float x;
float y;
});

Un composant enregistré avec un descripteur brut reste stockable et interrogeable, mais ses champs ne sont visibles par REST que si struct_desc référence une description Sireflect valide.

on_set reçoit la nouvelle valeur et la valeur actuellement stockée. Cette dernière reste inchangée jusqu’au retour du hook. on_add reçoit le stockage initialisé et on_remove la valeur qui va être retirée.

static void on_set_position(
ecs_entity_t entity,
ecs_component_t component,
const void *new_value,
void *current_value
) {
(void)entity;
(void)component;
const Position *incoming = new_value;
Position *stored = current_value;
printf("old=%f new=%f", stored->x, incoming->x);
}