En préliminaire à ces nombreuses questions, je pense qu'il y a une erreur à ne pas commettre : faire rentrer tout dans un système à entité. Et plus particulièrement, je pense qu'il n'est pas possible (ou souhaitable) d'utiliser un système à entité en conjonction avec un moteur de jeu. Parce qu'un moteur de jeu impose souvent une manière de faire qui n'est pas celle des systèmes à entités. Il existe des moteurs de jeu fondés sur les systèmes à entités (comme Unity), et ceux-ci utilisent le concept pleinement. Dans le cas d'Ogre, je crois qu'il va être difficile de le faire entrer dans un système à entité. Ou alors, il faut reprendre quelques classes de bases et refaire les classes haut niveau avec un système à entité, mais tu vas te retrouver à dupliquer du code existant par ailleurs.
Ceci étant dit, je vais essayer de répondre à tes questions.
En premier lieu, comment gérer la surcouche "matérielle" entre les composants de son propre système à entités et une bibliothèque externe ?
Comme indiqué, c'est difficile voire non-souhaitable de le faire, à partir du moment où la bibliothèque externe est assez haut niveau (comme un moteur de jeu complet). En revanche, pour des bibliothèques bas niveau (comme les moteurs physiques, ou les bibliothèques multimédias), c'est un travail d'encapsulation assez classique.
Est-ce que je prends le problème de travers et devrais fusionner ces deux objets ?
Tu prends le problème à l'envers. Il est compliqué de faire rentrer un cube dans un cercle.
Ce serait comme essayer d'appliquer la gravitation à une quête. Peut-on ignorer ce cas de figure, ou est-il raisonnable de penser qu'un composant doit être accompagner d'informations d'antériorité qui doivent être satisfaits pour permettre son existence ? Est-ce hors-sujet pour un système à entités ?
Alors là, je vais te citer un passage d'un article de référence qui dit : «Do Cameras shoot people? Do Bullets accept input from the player? No?» Et en fait, la réponse peut être oui. Donc en gros, les systèmes à entités permettent d'appliquer n'importe quel comportement à n'importe quel entité. Donc pourquoi pas appliquer la gravitation à une quête ? (Bon, ok, dans ton cas, je ne vois pas bien ce que ça voudrait dire). Mais plus concrètement, un object qui a une position n'a pas toujours un corps physique, mais l'inverse n'est pas vrai. Donc évidemment, si une entité a un corps physique, elle doit avoir une position. Et oui, tu peux faire cette supposition. Comme tu le soulignes après, un système n'agit pas sur n'importe quelle entité, il prend en compte le fait qu'une entité possède ou pas un certain nombre de composants.
Mais qui doit s'occuper de la "préparation" de ces cubes, c'est-à-dire notamment créer les objets Ogre à l'intérieur, puis placer correctement tous les cubes selon une scène pré-déterminée d'un fichier de sauvegarde ?
Ça peut être un système, ça peut être à l'initialisation. Généralement, le mot-clef qui va avec ce genre de chose pour les systèmes à entités, c'est «archétype». Un archétype, c'est une fonction qui va générer une entité d'un certain type en lui ajoutant tous les composants qui vont bien. Ces archétypes peuvent être appelé de n'importe où, y compris depuis un système (puisque c'est là que se situe tout le code). Après, la manière dont est créé l'entité dépend du type d'entité. Oui, il faudra peut-être aller lire un fichier de configuration, ça ne change pas grand chose.
Qui doit s'occuper du nettoyage lorsque les composants seront déconstruits (fuite mémoire, notamment) ? Si un composant n'est qu'une structure de donnée sans logique (dans la limite du C++), qui doit s'occuper de ce genre de traitements préparatoires et terminaux ? Un système ? Mais qui l'appellera et quand ? A priori on ne peut pas insérer pareil système préparatoire dans le circuit de la boucle infinie de l'application.
On a vu la naissance, maintenant la fin de vie. C'est un problème différent. Et oui, on peut tout à fait gérer cette fin de vie avec un système spécifique. Dans le tutoriel que j'avais écrit, ça faisait partie des exercices laissés au lecteur. Je conseillais d'ajouter un composant Lifetime avec un booléen indiquant si l'entité est en vie et une fonction à appeler pour détruire l'entité (la fonction inverse de l'archétype). Avec ce composant, on crée un système qui va s'occuper des entités qui possède ce composant. Et à chaque tour, il va appeler les fonctions de destruction sur les entités qui ne sont plus en vie. Après, les autres systèmes vont se contenter d'ajouter un composant Lifetime à une entité s'ils veulent la voir mourir, ou alors il vont positionner le booléen de telle sorte qu'elle meure (cette deuxième possibilité est intéressante dans le cas d'une entité à durée de vie limitée mais dont on connaît la durée de vie à l'avance).
Comment implémenter ce genre de traitement de façon efficace ?
Personnellement, dans ce cas, je ferais appel au moteur physique. C'est lui qui gère les interactions entre entités avec un corps physique. Il sait le faire de façon efficace et normalement, il prévient quand il y a une collision (c'est le cas de Box2D en tout cas).
J'ai du mal à voir comment implémenter le concept de "chose à faire ponctuellement".
Comment dire à un système de modifier un composant *précis *de telle manière *une seule fois ?
Là, le problème est un peu plus large. Et là, ma réponse, c'est que dans toutes les implémentations des systèmes à entités, il y a un systèmes d'événements en parallèle. Ce système d'événements sert justement à «faire une chose ponctuellement». Un système peut alors déclencher un événement et ceux qui sont intéressés par cet événement peuvent alors agir en conséquence.
[^] # Re: Système à entités et interface avec des librairies existantes
Posté par rewind (Mastodon) . En réponse à la dépêche Je crée mon jeu vidéo E13 : un an, premier bilan. Évalué à 2.
En préliminaire à ces nombreuses questions, je pense qu'il y a une erreur à ne pas commettre : faire rentrer tout dans un système à entité. Et plus particulièrement, je pense qu'il n'est pas possible (ou souhaitable) d'utiliser un système à entité en conjonction avec un moteur de jeu. Parce qu'un moteur de jeu impose souvent une manière de faire qui n'est pas celle des systèmes à entités. Il existe des moteurs de jeu fondés sur les systèmes à entités (comme Unity), et ceux-ci utilisent le concept pleinement. Dans le cas d'Ogre, je crois qu'il va être difficile de le faire entrer dans un système à entité. Ou alors, il faut reprendre quelques classes de bases et refaire les classes haut niveau avec un système à entité, mais tu vas te retrouver à dupliquer du code existant par ailleurs.
Ceci étant dit, je vais essayer de répondre à tes questions.
Comme indiqué, c'est difficile voire non-souhaitable de le faire, à partir du moment où la bibliothèque externe est assez haut niveau (comme un moteur de jeu complet). En revanche, pour des bibliothèques bas niveau (comme les moteurs physiques, ou les bibliothèques multimédias), c'est un travail d'encapsulation assez classique.
Tu prends le problème à l'envers. Il est compliqué de faire rentrer un cube dans un cercle.
Alors là, je vais te citer un passage d'un article de référence qui dit : «Do Cameras shoot people? Do Bullets accept input from the player? No?» Et en fait, la réponse peut être oui. Donc en gros, les systèmes à entités permettent d'appliquer n'importe quel comportement à n'importe quel entité. Donc pourquoi pas appliquer la gravitation à une quête ? (Bon, ok, dans ton cas, je ne vois pas bien ce que ça voudrait dire). Mais plus concrètement, un object qui a une position n'a pas toujours un corps physique, mais l'inverse n'est pas vrai. Donc évidemment, si une entité a un corps physique, elle doit avoir une position. Et oui, tu peux faire cette supposition. Comme tu le soulignes après, un système n'agit pas sur n'importe quelle entité, il prend en compte le fait qu'une entité possède ou pas un certain nombre de composants.
Ça peut être un système, ça peut être à l'initialisation. Généralement, le mot-clef qui va avec ce genre de chose pour les systèmes à entités, c'est «archétype». Un archétype, c'est une fonction qui va générer une entité d'un certain type en lui ajoutant tous les composants qui vont bien. Ces archétypes peuvent être appelé de n'importe où, y compris depuis un système (puisque c'est là que se situe tout le code). Après, la manière dont est créé l'entité dépend du type d'entité. Oui, il faudra peut-être aller lire un fichier de configuration, ça ne change pas grand chose.
On a vu la naissance, maintenant la fin de vie. C'est un problème différent. Et oui, on peut tout à fait gérer cette fin de vie avec un système spécifique. Dans le tutoriel que j'avais écrit, ça faisait partie des exercices laissés au lecteur. Je conseillais d'ajouter un composant
Lifetimeavec un booléen indiquant si l'entité est en vie et une fonction à appeler pour détruire l'entité (la fonction inverse de l'archétype). Avec ce composant, on crée un système qui va s'occuper des entités qui possède ce composant. Et à chaque tour, il va appeler les fonctions de destruction sur les entités qui ne sont plus en vie. Après, les autres systèmes vont se contenter d'ajouter un composantLifetimeà une entité s'ils veulent la voir mourir, ou alors il vont positionner le booléen de telle sorte qu'elle meure (cette deuxième possibilité est intéressante dans le cas d'une entité à durée de vie limitée mais dont on connaît la durée de vie à l'avance).Personnellement, dans ce cas, je ferais appel au moteur physique. C'est lui qui gère les interactions entre entités avec un corps physique. Il sait le faire de façon efficace et normalement, il prévient quand il y a une collision (c'est le cas de Box2D en tout cas).
Là, le problème est un peu plus large. Et là, ma réponse, c'est que dans toutes les implémentations des systèmes à entités, il y a un systèmes d'événements en parallèle. Ce système d'événements sert justement à «faire une chose ponctuellement». Un système peut alors déclencher un événement et ceux qui sont intéressés par cet événement peuvent alors agir en conséquence.