URL: https://linuxfr.org/news/je-cree-mon-jeu-video-e01-les-systemes-a-entites Title: Je crée mon jeu vidéo E01 : les systèmes à entités Authors: rewind ZeroHeure, Pierre Jarillon, palm123 et Nÿco Date: 2013年09月15日T18:31:21+02:00 License: CC By-SA Tags: gamedev, système_à_entités, favori et jeu Score: 100 «Je crée mon jeu vidéo» est une série d'articles sur la création d'un jeu vidéo, depuis la feuille blanche jusqu'au résultat final. On y parlera de tout : de la technique, du contenu, de la joie de voir bouger des sprites, de la lassitude du développement solitaire, etc. Vous pourrez suivre cette série grâce au [tag gamedev](http://linuxfr.org/tags/gamedev/public). Cet article est le premier de la série. Avant de vous dévoiler l'idée de jeu que j'ai (et qui n'est pas révolutionnaire, rassurez-vous) dans un prochain article, on va commencer par un peu de technique et parler des systèmes à entités. C'est un nouveau paradigme de programmation assez intéressant, en particulier dans le cadre des jeux vidéos où il est beaucoup utilisé depuis quelques années, en particulier dans le moteur de jeu (propriétaire) [Unity](http://unity3d.com/). ---- [Evolve your hierarchy, Mick West, Jan 2007](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) [Entity Systems are the future of MMOG development, Adam Martin, Sep 2007](http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/) [What is an entity system framework for game development?, Richard Lord, Jan 2012](http://www.richardlord.net/blog/what-is-an-entity-framework) [Entity Systems Wiki](http://entity-systems.wikidot.com/) ---- # Introduction ## Le problème de la programmation orientée objet Les systèmes à entités (_entity systems_) sont nés des limites du paradigme orienté objet appliqué aux jeux vidéos. Tout programmeur qui a pratiqué l'orienté objet suffisamment a déjà rencontré ce cas où il ne sait pas où placer une classe dans l'arbre d'héritage parce qu'elle irait bien à deux endroits différents. Petit exemple : /---+ Volant +---+ Avion | | Vehicule ---+ +---+ Soucoupe | \---+ Roulant +---+ Voiture | +---+ Moto Voici une belle hiérarchie comme on en rencontre souvent. Mais dans cette hiérarchie, où placer la classe `VoitureVolante` ? C'est une voiture, donc elle devrait être une fille de la classe `Voiture`. Mais elle vole donc elle devrait être une fille de `Volant`. [Certains langages](http://fr.wikipedia.org/wiki/C%2B%2B) permettent l'héritage multiple, mais comme tout le monde le sait, [ça pose des problèmes](http://fr.wikipedia.org/wiki/Probl%C3%A8me_du_diamant). Que faire ? ## Les non-solutions objets Surtout que ce genre de cas arrive assez fréquemment dans les jeux vidéos où il y a une foultitude d'objets avec une chiée de propriétés. Du coup, si on reste avec de l'orienté objet, on se retrouve avec deux solutions qui n'en sont pas vraiment : 1. Remonter les propriétés dans l'arbre des classes. Mais du coup, on se retrouve avec des classes de base énormes qui ont des tonnes de propriétés inutiles pour de nombreuses sous-classes. Dans notre exemple, on pourrait très bien mettre une propriété `nombreDeRoues` dans la classe `Vehicule` et mettre cette propriété à 0 pour les objets volants non-roulants. Notre voiture volante serait alors une fille de `Volant` mais avec un nombre de roues strictement positif. Et on sent bien que c'est crade. 2. Dupliquer le code entre les classes. L'inconvénient classique est qu'il faut maintenir deux copies du code et que c'est à coup sûr un échec programmé. Notre voiture volante sera alors une fille de `Voiture` et on dupliquera le code d'une méthode `vole()` tiré de la classe `Volant`. Bref, saimal. C'est là qu'interviennent les systèmes à entités. ## La programmation orientée donnée Avant de rentrer dans le vif du sujet, disons tout de suite ce que les systèmes à entités ne sont pas. Ce n'est pas une évolution de la programmation orientée objet, c'est même une approche complètement orthogonale. Ce n'est pas de la [programmation orientée composants](http://fr.wikipedia.org/wiki/Programmation_orient%C3%A9e_composant), même si par certains côtés, ça se ressemble, de loin, dans le brouillard, la nuit. Les systèmes à entités sont ce qu'on pourrait appeler de la programmation orientée donnée dans le sens où ce qui est au centre, ce sont les données et pas les fonctions, contrairement à l'orienté objet où le code (les méthodes) sont au cœur même du paradigme. La programmation orientée donnée partage quelques concepts avec les bases de données, et on verra qu'un système à entités pourrait être décrit comme une base de données. Quoi qu'il en soit, si vous découvrez les systèmes à entités, vous devez totalement oublier tout ce qu'on vous a appris et notamment la programmation orientée objet. Dès que vous essaierez de rapprocher les concepts des systèmes à entités de ceux de la programmation orientée objet, vous aurez perdu et vous ne pourrez pas profiter de la puissance des systèmes à entités. Après, qu'on s'entende bien, les systèmes à entités ne signent pas la mort de la programmation orientée objet. Chaque paradigme a ses avantages et ses inconvénients et il convient de choisir le meilleur pour chaque usage. Il se trouve que dans les jeux vidéos, les systèmes à entités trouvent une application assez naturelle. # Les concepts des systèmes à entités Il y a dans les systèmes à entités quelques concepts qu'on retrouve dans toutes les descriptions. Ces concepts portent parfois des noms différents mais globalement, ils ont la même fonction. ## Entité Une entité (_entity_) représente un objet dans le jeu (_game object_), c'est-à-dire n'importe quel élément d'un jeu. Une entité ne possède pas de données propres, ni de méthodes propres. Une entité est une sorte d'identifiant de l'objet du jeu, rien de plus. Mais alors, où sont les données ? Elles sont dans les composants. ## Composants Un composant (_component_) représente un aspect d'un objet ou d'un ensemble d'objet. Par exemple, la couleur est un composant, la position est un composant, la valeur en pièce d'or est un composant, etc. Les données inscrites dans le composant lui permettent de faire fonctionner l'aspect en question. Une entité va donc être caractérisée par un ensemble de composant, pas forcément constant d'ailleurs. Prenez une `Voiture`, ajoutez lui un composant `Ailes` et hop, vous avez une `VoitureVolante`. Donc, si les composants sont un tas de données, comment met-on tout cela en route ? Dans les systèmes. ## Systèmes Un système (_system_) contient tout le code pour mettre à jour les composants. Tout le code se trouve dans les systèmes et pas ailleurs (notamment pas dans les composants). Un système va donc avoir besoin d'un ensemble de composants qu'il va lire et/ou écrire. Un système va être exécuté en permanence sur toutes les entités qui possèdent les composants adéquats. Par exemple, un système `Rendu` va prendre toutes les entités qui possèdent le composant `Representation`, va lire les informations du composant et rendre l'entité sur l'écran. Voilà, on pourrait en rester là mais on va aborder un dernier concept, les archétypes. ## Archétypes Un archétype est un type d'entité, c'est-à-dire une liste de composants qu'on va utiliser pour représenter un type d'objet. Techniquement, on pourrait se passer des archétypes mais ils se révèlent bien pratique pour gérer l'initialisation des entités et en particulier des différents composants des entités. # Comment peut-on implémenter ce bouzin ? ## Généralités Vient alors la question de comment on peut implémenter ce machin. Il y a en gros [deux manières de faire](http://blog.7thfactor.com/?p=436) : la mauvaise et la bonne. Enfin, question de point de vue. Disons que si on ne veut pas trop s'écarter du paradigme, il vaut mieux utiliser la seconde. [Dans la vraie vie, on trouve un peu de tout, même de l'orienté objet !](http://www.tomseysdavies.com/2011/01/23/entity-systems/). La mauvaise manière de faire est d'implémenter les entités comme des listes de composants. Ça semble assez naturel, mais ça pose des problèmes. Notamment quand il faut accéder à la mémoire. En effet, un système va mettre à jour tout un tas de composants auquel il va accéder de manière linéaire. Si tous les composants d'un même type sont alloués les uns à côté des autres (genre dans un tableau), ça va aider le cache et donc améliorer les performances. Si les composants sont stockés dans l'entité, on va perdre cet avantage. La bonne manière de faire, c'est donc d'implémenter les entités comme des entiers. En fait, si on pousse l'orienté donnée jusqu'au bout, on peut imaginer les [systèmes à entités comme des bases de données](http://t-machine.org/index.php/2009/10/26/entity-systems-are-the-future-of-mmos-part-5/) où les entités seraient des identifiants qui serviront de clefs primaires pour les différentes tables. Chaque composant aurait sa table avec les données. Ensuite, on aurait quelques tables pour faire le lien entre les entités et les composants. On pourrait ajouter également quelques tables pour définir des archétypes. Cette dernière manière de faire a été plus ou moins normalisée dans une API qui s'appelle [ES alpha](http://entity-systems.wikidot.com/rdbms-with-code-in-systems). C'est à mon avis la bonne vision à avoir. Même si après, on va être obligé de s'écarter un peu du dogme pour des raisons pratiques parmi lesquelles le besoin de communiquer entre entités qui est généralement géré à part. Un des énormes avantages des systèmes à entités, c'est que la sauvegarde d'un jeu devient triviale : comme toutes les données sont dans les composants, il suffit de sauvegarder les composants. L'ensemble des composants forment l'état du jeu à l'instant `t`. En les sérialisant sur le disque, on crée une photo instantanée du jeu qu'on pourra recharger pour reprendre le jeu exactement dans le même état. ## libes, une implémentation d'un système à entité Je vous propose donc [mon implémentation d'un système à entité](https://github.com/jube/libes), qui est ma première contribution pour cette série d'articles ([licence ISC](http://opensource.org/licenses/isc-license)). `libes`, c'est son nom, est une bibliothèque qui essaie de suivre au mieux l'API ES alpha en l'adaptant à C++. Le code n'est pas très long et reste relativement lisible et compréhensible. J'ai produit [un tutoriel](https://github.com/jube/libes/blob/master/doc/tutorial.md) pour montrer comment on peut utiliser la bibliothèque sur un exemple tout bête : des balles rebondissantes. Le code complet de ce tutoriel est bien entendu disponible. Et bien entendu, j'utiliserai cette bibliothèque et les concepts des systèmes à entités pour mon jeu. ## D'autres implémentations Vous pouvez également aller voir d'autres implémentations existantes (souvent plus complexe à mon sens) : * [Artemis](http://gamadu.com/artemis/), Arni Arent, 2012, Java * [EntityX](https://github.com/alecthomas/entityx), Alec Thomas, 2012, C++ * [anax](https://github.com/miguelishawt/anax), Miguel Martin, 2013, C++ # Pour aller plus loin... Pour écrire cet article, je me suis largement inspiré des articles mis en lien dans cette dépêche et qui sont de très bonnes sources d'information complémentaires. N'hésitez pas à dire si ce genre d'article vous plaît. Si c'est le cas, j'en ferai d'autres.