Aller au contenu

Systèmes et ordonnancement

Un système est un callback nommé attaché à une requête. ecs_progress() exécute les systèmes activés selon leurs phases.

static void Move(ecs_iter_t *it) {
Position *positions = ecs_field(it, 0);
const Velocity *velocities = ecs_field(it, 1);
for (uint32_t i = 0; i < it->count; i++) {
positions[i].x += velocities[i].x;
}
}
ecs_system({
.name = "Move",
.phase = EcsOnUpdate,
.query.components = { ecs_inout(Position), ecs_in(Velocity) },
.callback = Move,
});

ecs_progress() et ecs::progress() exécutent les phases de démarrage, de mise à jour et de rendu. ecs_run_phase() exécute une phase précise et ecs_run_system() un système précis.

Dans une phase, utilisez after ou .after() pour déclarer une dépendance :

ecs_system_id_t integrate = ecs_system({
.name = "Integrate",
.phase = EcsOnUpdate,
.query.components = { ecs_inout(Position), ecs_in(Velocity) },
.callback = Integrate,
});
ecs_system({
.name = "Sync",
.phase = EcsOnUpdate,
.query.components = { ecs_in(Position), ecs_inout(Transform) },
.callback = Sync,
.after = { integrate },
});

Les dépendances doivent rester dans la même phase. Les cycles sont invalides.

Modifier une valeur dans le lot courant est prévu. Ajouter ou retirer un composant peut déplacer l’entité et invalider les pointeurs du lot ; différez ces mutations ou effectuez-les en dehors de la boucle quand le contrat de l’opération ne garantit pas leur sécurité.

Un système peut être créé désactivé avec .disabled = true ou .disabled(). Cela est différent du composant Disabled, qui exclut seulement les entités des requêtes normales.

Les ressources sont des valeurs uniques stockées dans le monde. En C, lisez-les avant la boucle sur les entités :

static void move_system(ecs_iter_t *it) {
const Time *time = ecs_get_resource_read(Time);
Position *positions = ecs_field(it, 0);
const Velocity *velocities = ecs_field(it, 1);
for (uint32_t i = 0; i < it->count; i++) {
positions[i].x += velocities[i].x * time->dt;
}
}

Une ressource n’est pas un terme de requête et ne consomme pas d’index de champ.

ecs_progress() exécute tous les systèmes activés :

ecs_progress();

Les phases sont exécutées dans l’ordre suivant :

Phase Usage courant
EcsPreStart Préparation avant le démarrage, une seule fois.
EcsStart Initialisation, une seule fois.
EcsPostStart Travail après l’initialisation.
EcsOnLoad Chargement ou bootstrap.
EcsPostLoad Travail après le chargement.
EcsPreUpdate Préparation de la simulation.
EcsOnUpdate Logique principale.
EcsPostUpdate Nettoyage et état dérivé.
EcsPreRender Préparation du rendu.
EcsOnRender Travail lié au rendu.
EcsPostRender Nettoyage après le rendu.

Une phase ou un système peut aussi être exécuté explicitement :

ecs_run_phase(EcsOnUpdate);
ecs_run_system(Move);

L’exécution manuelle d’une phase EcsStart ne suit pas la règle “une seule fois” de ecs_progress().

Les phases imposent l’ordre global. Dans une même phase, utilisez after pour indiquer qu’un système dépend d’un autre :

ecs_system_id_t Integrate = ecs_system({
.name = "Integrate",
.phase = EcsOnUpdate,
.query = { .components = { ecs_inout(Position), ecs_in(Velocity) } },
.callback = integrate_system,
});
ecs_system({
.name = "SyncTransform",
.phase = EcsOnUpdate,
.query = { .components = { ecs_in(Position), ecs_inout(Transform) } },
.callback = sync_transform_system,
.after = { Integrate },
});

Les dépendances doivent viser des systèmes de la même phase. Les cycles et les dépendances entre phases provoquent une assertion en debug.

Les systèmes sont activés par défaut :

ecs_system_id_t Damage = ecs_system({
.name = "Damage",
.phase = EcsOnUpdate,
.query = { .components = { ecs_inout(Health) } },
.callback = damage_system,
.disabled = true,
});
ecs_system_enable(Damage);
ecs_system_disable(Damage);

Un système désactivé est ignoré par ecs_progress(), ecs_run_phase() et ecs_run_system(). Cela ne désactive pas les entités portant Disabled.

Modifier les valeurs du lot courant est prévu. En revanche, ajouter ou retirer un composant peut déplacer une entité vers une autre table et invalider les pointeurs du lot courant. Gardez ces mutations en dehors de l’itération, ou utilisez le différé lorsque le contrat de l’opération l’exige.

typedef struct {
const char *name;
ecs_query_desc_t query;
void (*callback)(ecs_iter_t *);
uintptr_t user_data;
void (*user_data_dtor)(uintptr_t user_data);
ecs_phase_t phase;
ecs_system_id_t after[ECS_SYSTEM_AFTER_CAPACITY];
bool disabled;
} ecs_system_desc_t;
Champ Signification
name Nom facultatif utilisé pour le debug.
query Composants parcourus par le système.
callback Fonction appelée pour chaque lot non vide.
phase Phase utilisée par ecs_progress() et ecs_run_phase().
after Systèmes de la même phase qui doivent passer avant.
disabled Crée le système sans l’exécuter.