Architecture ECS

Par DimitriLecture 3 min.

ECS

L'ECS est une architecture logicielle permettant de découpler la donnée des objets qui la portent.

E : Entity

C : Component

S : System

Entity

Une entité est similaire à une entité du Domain Driven Design. C'est quelque chose qui possède une identité unique et un cycle de vie. A l'inverse d'une valeur discrète comme une monnaie ou une distance. Dans cette architecture, une entité est très souvent un ID unique.

Example

let entity = commands.spawn_empty().id();

Comme dit plus tôt, une entité est juste un ID, ce qui définie sont identité. Mais il faut plus que des lists d'ID pour construire une application. C'est pourquoi à cet ID, on peut attacher des Composants.

Component

Les composants sont des conteneurs de données. Des valeurs qui definissent l'état actuel d'une entité. Par exemple, une entité pourrait contenir :

  • Position
  • Vitesse
  • Accélération

Avec ces trois composants, si nous sommes dans une simulation 2D, la position pourrait être un vecteur 2. Ou dans le cas d'un système de géolocalisation on pourrait envisager une position (latitude, longitude) ou encore des coordonnées polaires.

Example

let entity = commands.spawn((
    Name::new("Je suis une entité avec un composant Nom"),
    Position(Vec2::new(0.0, 0.0)),
    Vitesse(Vec2::new(0.0, 0.0)),
    Acceleration(Vec2::new(0.0, 0.0))
)).id();

Cette entité contient les composants nécessaires au traitement souhaité lors de simulations physique basiques.

La donnée, loin d'être inutile, sert à apporter du comportement aux entités qui les détiennent via des Systèmes.

System

Les systèmes sont le coeur du fonctionnenment de cette architecture, sans eux, pas de changements. Ils ne sont ni plus ni moins que des fonctions qui prennent des listes de composants et effectuent des opérations dessus.

Par exemple, avec les composants plus haut on peut envisager un système qui prend la liste de tous les tuples (position, vitesse) et ajoute la vitesse à la position en fonction du temps pour faire se déplacer l'entité qui porte ces composants. Un autre système pourrait intégrer l'accélération à la vitesse avant qu'elle ne soit ajoutée. De cette manière, chaque système est spécialisé sur une interaction entre composants.

Exemple

pub fn integrate_speed(mut query: Query<(&mut Position, &Vitesse)>) {
    for (mut position, vitesse) in query.iter_mut() {
        position.0 += vitesse.0;
    }
}

Ici integrate_speed() est une fonction qui prend en paramètre une Query (un itérateur), qui liste TOUTES les entités qui possèdent les composants Position et Vistesse.

Ici le système souhaite modifier le composant Lifetime, pour le faire avancer du tick qui sépare deux frames, donc on a besoin de l'indiquer comme mutable (mais c'est une spécificité de Rust, pas de l'ECS).

Attention

Le code ici est à prendre avec des pincettes, lors d'une intégration de vitesse dans une position, dans le cadre d'une simulation en temps réel, il faut aussi prendre en compte le temps écoulé entre 2 passages du système et éventuellement avoir un système qui va stabiliser dans le temps le nombre de passages du système, c'est ce qu'on appelle une Fixed Timestep

Conclusion

Note

Cette architecture peut être pensée comme une base de donnée colonnaire en temps réel. Les entités ont autant de composants que l'on souhaite, et les systèmes traitent composants par composant sans s'occuper directement des entités qui les portes.

Cette architecture est notamment utilisée dans le traitement en temps réel et par extension dans les jeux vidéos qui sont des systèmes en temps réel.

L'alternative à l'ECS dans les moteurs modernes c'est souvent d'avoir des objets qui possèdent tous une fonction update() (cf godot et unity) appelée chaque frame pour mettre à jour l'objet qui la possède.


Mise en ligne

Par DimitriLecture 2 min.

Pourquoi ?

Quelques jours après m'être dit qu'il fallait que je documente pour mon futur moi (et mon futur enfant), j'ai été confronté au choix de l'outil de publications d'articles.

J'avais déjà essayé Hugo mais envie de nouveauté donc avec Zola étant écrit en Rust, l'hésitation n'a pas été longue.

Mise en ligne

La documentation de Zola nous présente les principaux fournisseurs pour déploiment de sites statiques. J'en ai testé trois et demi:

Vercel

J'ai déjà utilisé Vercel donc étant satisfait de l'outil c'est le premier vers lequel je me suis orienté. Or, leur gestion du build rend le déploiement de versions récentes de Zola inutilement complexe.

GitHub Pages

GitHub Pages c'est payant pour un repository privé, dommage.

Cloudflare Worker

Cloudflare fait l'apologie de leur nouveau service supplantant Cloudflare Pages. Mais Wrangle, leur nouvel outil de déploiment est loin d'être clé en main pour déployer un simple site statique. Sans doute très puissant pour le reste, cela dit.

Cloudflare Pages

Finalement, après avoir initialement galéré sur Pages, puis Worker, je suis retourné sur Pages avec une super astuce!

Le problème

Vercel et Cloudflare ont le même problème, ils n'ont pas la bonne version de la GLIBC pour installer Zola. Plusieurs solutions sont proposées sur le site de Zola mais aucune n'a fonctionnée pour moi.

Solution

Note

Cette solution devrait aussi fonctionner avec les autres alternatives de déploiement, si tant est qu'elles proposent d'exécuter une commande personnalisée.

La meilleure solution que j'ai trouvé est de tricher la commande de build de zola pour y suffixer un téléchargement du binaire à jour de ma version de Zola et enfin build correctement.

curl -L https://github.com/getzola/zola/releases/download/v0.21.0/zola-v0.21.0-x86_64-unknown-linux-gnu.tar.gz | tar xz && ./zola build

Une fois le build terminé, Cloudflare Pages gère très bien le reste et upload les fichiers sans problèmes.

Conclusion

C'était un peu trop long à mon goût pour mettre en ligne un site aussi simple mais avec un peu de patience, de jus de cerveau et de qwant fu, on fini par s'en sortir.