Journal Découverte de l'Entity Component System avec Bevy

Posté par  (site web personnel, Mastodon) . Licence CC By‑SA.
Étiquettes :
16
3
oct.
2023

Je me suis amusé dernièrement avec Bevy engine, un moteur de jeu plutôt bas niveau "piloté par les données". C'est-à-dire qui utilise le "Entity Component System".

Bevy

Belle découverte pour ma part ! En quelques mots :

Entity Component System

Coder en ECS c'est, ne pas coder de manière procédurale : On fournit au moteur d'ECS des données (que l'on identifie comme des ressources, des composants, etc) ainsi que des procédures (ex. une fonction) que l'on veut qu'il exécute selon certaines conditions.

Par exemple, je lui donne :

  • Une liste de Personnes, où chaque Personne est composé d'un Nom et d'un Email.
  • Une ressource EnvoyeurDeMail
  • Une procédure qui prend en entrée EnvoyeurDeMail ainsi que la liste des Personnes. Procédure qui devra être exécutée toutes les 24h

Le moteur ECS se débrouille ensuite pour que les procédures s'exécutent selon les conditions demandées en parallélisant le tout et assurant le partage correct des ressources (verrous, etc).

Dans le cadre d'un jeu, comme avec Bevy, cela est très agréable à utiliser. Car les conditions associées aux procédures pourront être des fréquences, des collisions entre objets, des évènements gameplay… Le tout se répartissant de manière optimale sur les différents cœurs de CPU.

Mon test

J'ai joué avec Bevy pour produire un explorateur de monde 2D généré aléatoirement (avec un bruit de Perlin) dont seul les données à afficher sont récupérés et affichés. Sur deux niveaux de représentations (vue normale et carte) :

Démonstration de bevy

Le code source est ici : https://github.com/buxx/neoroll

Conclusion

L'ECS semble un peu ésotérique quand on l'approche la première fois. Mais il prend tout son sens lorsque l'on écrit un programme dont le déroulement est très dynamique. Et les performances sont optimales, car seules l'utilisation des ressources identifiées comme n'étant pas partageable donnent lieu à des temps d'attente. Le reste étant parallélisé.

Pour ce qui est de Bevy, c'est un très bon moteur de jeux ! Ce n'est pas du tout aussi complet que Godot par exemple, mais c'est un très bon moteur de jeu "bas niveau".

Et j'aime toujours autant le Rust. Plus chaque jour 💕🦀

  • # procédurier

    Posté par  (site web personnel) . Évalué à 7. Dernière modification le 03 octobre 2023 à 18:15.

    Sans vouloir te faire un procès dur, tu dis que ce n'est pas codé de manière procédurale, mais que c'est un jeu plein de procédures avec de la génération procédurale ?

    Le post ci-dessus est une grosse connerie, ne le lisez pas sérieusement.

    • [^] # Re: procédurier

      Posté par  (site web personnel, Mastodon) . Évalué à 2.

      Haha très bonne remarque.

      Pour préciser au lecteur de passage, quand je parle de programmer le jeu de manière non procédurale, il est question de l'agencement des parties du code qui gère le gameplay, la physique, etc.

      🦀🐍 http://github.com/buxx 🖥 https://algoo.fr 📋 https://tracim.fr

    • [^] # Re: procédurier

      Posté par  (site web personnel, Mastodon) . Évalué à 3.

      J‘ai plutôt compris qu’on se préoccupe des procédures des événements isolément, mais pas de la méga procédure d’ordonnancement…

      “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # Collisions de procédures ?

    Posté par  (site web personnel, Mastodon) . Évalué à 4.

    Dans cette démarche, comment gères-tu les collisions qui pourraient intervenir entre des procédures qui pourraient entrer en conflit ?

    Par exemple si en complément de la ressource EnvoyeurDeMail tu implémentes une ressource SupprimeurDUtilisateurObsolète et que pendant que la première s'exécute, la seconde s'exécute également et a pour conséquence que EnvoyerDeMail essaie d'envoyer un mail à un uilisateur qui n'existe plus ?

    L'exemple est un peu tiré par les cheveux ; plus les procédures sont longues et nombreuses, plus les collisions risquent d'intervenir et donc plus il y a de situations potentiellement problématiques … et du coup je suis curieux de savoir s'il y a des moyens de "garantir que ça fonctionnera toujours"

    • [^] # Re: Collisions de procédures ?

      Posté par  . Évalué à 4.

      Ça fait plus de 10 ans que j'ai pas animé un sprite avec ce genre de moteur, mais dans mes souvenirs, dans un moteur ECS qui fait du parallélisme, l'information des dépendances sur les type de composants utilisés pour chacun des systèmes est documentée par le développeur. Si un système qui utilise les composantes d'utilisateurs, un autre système qui les utilise ne sera pas donc pas exécuté en même temps.

      D'autre part il me semble qu'on agit par phases et on fait le nettoyage après avoir effectué les mises à jour pour éviter les soucis.

      L'utilisation en dehors du jeu vidéo n'est pas impossible, mais son point fort est d'être optimisée pour des scénarios spécifiques où chaque entité possède de caractéristiques de plusieurs types et où les calculs peuvent être fait sur des groupes de caractéristiques communes à un grand nombre d'entités.

      Pour reprendre l'exemple d'envoi d'e-mail, on va considérer une entité (dans un moteur, elle peut être références par un entier, un uuid.., elle possède juste un identifiant unique).

      Cette entité possède les caractéristiques, les composantes "Utilisateur" et "EMailAEnvoyer" avec les informations associées.

      Si la procédure d'envoi d'e-mail nécessite un utilisateur des e-mails à envoyer, le système (la fonction) correspondant sera marqué pour s’exécuter sur les entités avec les composantes "Utilisateur" et "EMailAEnvoyer". Tu ne peux normalement pas retirer EMailAEnvoyer depuis un autre système, pour la raison énoncées plus haut: il ne devrait pas y avoir d'autre système en exécution avec cette composante. On peut imaginer qu'une fois les e-mails à envoyer envoyés, la composante EMailAEnvoyer soit retirée.

      Si d'autres système ont besoin d'y accéder, ils faut qu'ils aient été mis avant dans la liste des systèmes à exécuter, ou ajouter une autre composante pour labelliser l'entité comme étant à nettoyer du label EMailAEnvoyer en fin de boucle.

      Donc collisions, normalement ça ne devrait pas arriver, mais ça demande d'ajuster son moteur au préalable, sans doute plus qu'avec une autre architecture. Le découpage des données en composantes à aussi énormément d'impact sur comment va s'effectuer le parallélisme..

      … et du coup je suis curieux de savoir s'il y a des moyens de "garantir que ça fonctionnera toujours"

      Bref, ça devrait être possible :-)

    • [^] # Re: Collisions de procédures ?

      Posté par  (site web personnel, Mastodon) . Évalué à 1.

      Je reprends les propos de elfir3 :

      Si un système qui utilise les composantes d'utilisateurs, un autre système qui les utilise ne sera pas donc pas exécuté en même temps.

      C'est exactement ça.

      Là où ça peut vite devenir compliqué, je pense, c'est lorsqu'un nombre conséquent de "procédures" se comportent d'une manière inattendue. Ce doit être assez difficile de reproduire le problème. Ce n'est pas directement lié à l'ECS, mais a la forte parallélisation.

      🦀🐍 http://github.com/buxx 🖥 https://algoo.fr 📋 https://tracim.fr

  • # L'ECS pour quel type de jeu ?

    Posté par  . Évalué à 3.

    J'ai plutôt réalisé des jeux très (très) simple et je n'ai jamais eu le besoin de jouer avec de l'ECS, je me demande du coup s'il y a des cas d'usage ou l'ECS est fortement recommandé et d'autre ou c'est une mauvaise pratique.

    En tout cas, c'est très cool. Je jetterai ou coup d'œil au code de neoroll.

    • [^] # Re: L'ECS pour quel type de jeu ?

      Posté par  (site web personnel) . Évalué à 6.

      À ce sujet je te conseils l'article expliquant pourquoi Godot n'utilise pas ECS

      tl;dr:

      ECS c'est une approche plutôt bas niveau qui est surtout intéressante pour des questions de performances.

      De son côté Godot expose une API haut niveau et orientée objet à l'utilisateur, et utilise dans ses systèmes internes (typiquement pour la physique, l'audio et le rendu) une architecture orientée donnée (comme ce que fait ECS) pour être performant.

      Cette approche fonctionne bien pour la plupart des cas, sauf pour des types de jeux particulier devant gérer beaucoup de logique de jeu (mais genre beaucoup beaucoup, comme pour un open world ou un gros city builder).

      À ce moment ajouter une approche ECS pour la logique de jeu devient justifiée (et dans le cas de Godot on peut écrire cette logique sous forme d'extension native pour un max de perf).

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.