Je crée mon jeu vidéo E01 : les systèmes à entités

Posté par  (Mastodon) . Édité par ZeroHeure, Pierre Jarillon, palm123 et Nÿco. Modéré par Ontologia. Licence CC By‑SA.
100
16
sept.
2013
Jeu

«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.

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.

Sommaire

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 permettent l'héritage multiple, mais comme tout le monde le sait, ça pose des problèmes. 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, 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 : 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 !.

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 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. 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é, qui est ma première contribution pour cette série d'articles (licence ISC). 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 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, Arni Arent, 2012, Java
  • EntityX, Alec Thomas, 2012, C++
  • 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.

Aller plus loin

  • # J'approuve

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

    Sympa comme concept, vivement la suite !

    kentoc'h mervel eget bezan saotred

    • [^] # Re: J'approuve

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

      Sympa comme concept, vivement la suite !

      Totalement d'accord !

      En plus je suis en train de coder moi même un jeu, et tu m'as convaincu d'utiliser la programmation orientée donnée pour gérer l'ensemble des entités.

      Il y a d'ailleurs de fortes changes que j'utilise ta librairie ;)

      • [^] # Re: J'approuve

        Posté par  (Mastodon) . Évalué à 2.

        Il y a d'ailleurs de fortes changes que j'utilise ta librairie ;)

        N'hésite pas à faire remonter tes besoins pour l'améliorer !

        • [^] # Re: J'approuve

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

          N'hésite pas à faire remonter tes besoins pour l'améliorer !

          Ce sera fait =)

        • [^] # Re: J'approuve

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

          Salut,

          Je suis en train de jouer avec ta librairie et de m'habituer avec ce type de programmation (dur dur de se défaire de l'objet après tant d'années).

          J'ai une question qui me taraude :
          - Quelle est la bonne manière de faire quand tu veux que un composant dépende d'un autre ? Par exemple, j'ai un composant Modèle et un composant Animation, et l'animation à besoin du Modèle. Je dois créer un composant ModèleAnimé ou juste ajouter Animation à mon entité (dans ce cas là comment je gère le lien entre les deux) ?

          Merci d'avance.

          • [^] # Re: J'approuve

            Posté par  (Mastodon) . Évalué à 3.

            Typiquement, le lien entre les deux composants va se faire dans un système (qui va gérer l'animation si je comprends bien). Ton système va considérer toutes les entités qui ont à la fois Animation et Modèle, et donc tu auras accès au deux pour déterminer quel est la bonne frame dans l'animation. Normalement, dans Animation et Modèle, tu n'auras que des données et rien d'autres.

            • [^] # Re: J'approuve

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

              D'accord. Mais du coup comment tu gères l'ordre dans lequel les Systèmes sont appelés ?

              • [^] # Re: J'approuve

                Posté par  . Évalué à 3.

                C'est l'ordre dans le quel tu les enregistre dans le manager qui définit l'ordre d'appel. Ensuite c'est l'appel du preUpdate() de tous les systèmes, puis de update(), puis de postUpdate().

                https://github.com/jube/libes/blob/master/src/lib/Manager.cc#L124

                Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

                • [^] # Re: J'approuve

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

                  Ok merci je vais tenter le coup.

                • [^] # Re: J'approuve

                  Posté par  (Mastodon) . Évalué à 2.

                  C'est l'ordre dans le quel tu les enregistre dans le manager qui définit l'ordre d'appel. Ensuite c'est l'appel du preUpdate() de tous les systèmes, puis de update(), puis de postUpdate().

                  Pas exactement. Le constructeur de System prend en paramètre une priorité et, comme indiqué dans la doc, les systèmes avec une priorité petite seront exécutés les premiers. Dans le Manager, ça se traduit par un tri suivant la priorité avant l'initialisation de tous les systèmes.

            • [^] # Re: J'approuve

              Posté par  . Évalué à 3.

              J'ai mis vite fait le nez dans ta bibliothèque et je me pose une question, pour la classe es::System. Il ne faudrait pas que le destructeur et les méthodes faites pour être surchargées soient déclarées virtual ?

              Je vois que tu ne les définis comme virtuels que dans les classes filles (il s'agit alors d'une redéfinition). En fait je ne sais même pas comment ça fonctionne, vu que de ce que je crois savoir si tu as une référence de type es::System qui pointe vers une sous-classe l'appel vers une méthode non virtuelle sur cette référence va appelle la méthode de la classe mère et pas de la classe fille.

              Je suis vraiment à coté de la plaque ?

              Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

              • [^] # Re: J'approuve

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

                Non, tu as tout à fait raison.

              • [^] # Re: J'approuve

                Posté par  (Mastodon) . Évalué à 4.

                Il ne faudrait pas que le destructeur et les méthodes faites pour être surchargées soient déclarées virtual ?

                C'est bien le cas.

                Je vois que tu ne les définis comme virtuels que dans les classes filles

                Non, dans System, j'ai bien un destructeur virtuel et les méthodes init, preUpdate, update, postUpdate qui sont virtuelles. Après, dans le .cc, tu n'es pas obligé (et même je crois que c'est interdit) de mettre le mot-clef virtual. Ce qui compte ici, c'est bien le .h et dans le .h, c'est bien virtual.

                • [^] # Re: J'approuve

                  Posté par  . Évalué à 3.

                  Arg ! … j'ai oublié d'aller voir le header… Désolé pour le bruit. :)

                  Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

            • [^] # Re: J'approuve

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

              Comment tu fais si tu as 2 fois une propriété du même objet représenté graphiquement à l'écran, par exemple un chiffre qui change en fonction de l’élément graphique sélectionné, et la couleur de l’élément en question.

              "La première sécurité est la liberté"

  • # Langage

    Posté par  . Évalué à 2.

    Si j'ai bien compris, tu vas vers le C++ en ce qui concerne le langage.

    Questions de curiosité (ceci n'est pas un appel au troll):
    Pour quelles raisons ? Tu connais déjà le langage ? Certaines librairies sont plus faciles d'accès (j'entends dans le choix du système à entités).

    Peut-être avais-tu prévu d'y répondre lors d'un prochain épisode.
    Dans ce cas, désolé !

    Merci pour tes réponses !

    • [^] # Re: Langage

      Posté par  (Mastodon) . Évalué à 9.

      Pour quelles raisons ? Tu connais déjà le langage ? Certaines librairies sont plus faciles d'accès (j'entends dans le choix du système à entités).

      Oui je connais déjà le langage, et sa version 2011 est vraiment sympa à utiliser. Dès qu'on commence, on peut difficilement s'en passer. Ensuite, le C++ étant le langage dominant pour les jeux vidéos, il existe tout un tas de bibliothèque plutôt bien foutu pour tout un tas d'usage (j'aurai l'occasion d'en reparler dans les prochains épisodes). Et puis même pour les trucs génériques, il y a du choix genre Boost. Donc, ça ne se discute même pas, je n'ai même pas réfléchi à utiliser un autre langage, c'est C++ et rien d'autre.

      Maintenant, pour les systèmes à entités, il existe des implémentations dans plusieurs langages dont Java, ActionScript, Ruby, C#, et sans doute d'autres que je ne connais pas.

  • # Conception objet quand même ?

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

    Salut.

    Comme tu dis, ils faut se oublier la conception objet… Un moment je me suis demandé si ton système Rendu n'allait pas utiliser Representation via des appels de fonction (type dessineMoi) :)

    Mais si j'ai bien compris, Rendu va, au contraire, utiliser les données du composant Representation. C'est ça ?

    Du coup les données qu'un système utilise sont prédéfinies au moment de la conception du jeux ? Ma question reviens à demander comment on fait de la "généricité" ?

    • [^] # Re: Conception objet quand même ?

      Posté par  (Mastodon) . Évalué à 5.

      Du coup les données qu'un système utilise sont prédéfinies au moment de la conception du jeux ?

      Oui, il vaut mieux.

      Ma question reviens à demander comment on fait de la "généricité" ?

      Tout dépend ce que tu appelles de la généricité. Mais je dirais que dans le cadre d'un jeu où tout est à peu près connu et borné, on n'en a pas réellement besoin.

  • # Et les traits ?

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

    Certains langages permettent l'héritage multiple, mais comme tout le monde le sait, ça pose des problèmes. Que faire ?

    En fait, ce pb ne vient pas de la POO mais plus des limites des langages orientés-objet. Ce problème a été une des raisons pour laquelle les traits ont été introduits dans des langages orientés-objets (même dans Smalltalk avec Pharo). Un trait est, grosso-modo, un ensemble de propriétés que peuvent se partager les objets (les propriétés d'un trait sont décrites par des messages, associés ou non avec une méthode par défaut, que doivent comprendre les objets qui satisfont ce trait).

    Mais, effectivement, contrairement au système à entités présenté ici, de par l'essence de la POO, l'accent est toujours mis sur le comportement (les messages), alors que, apparemment, d'après ton article, la programmation des jeux nécessiteraient plus une approche centrée sur les données.

    • [^] # Re: Et les traits ?

      Posté par  (Mastodon) . Évalué à 4.

      Mais, effectivement, contrairement au système à entités présenté ici, de par l'essence de la POO, l'accent est toujours mis sur le comportement (les messages), alors que, apparemment, d'après ton article, la programmation des jeux nécessiteraient plus une approche centrée sur les données.

      Oui, dans un jeu, l'important, c'est le contenu, et ça paraît assez logique que ça se retrouve dans le paradigme. Disons pour être plus précis que dans la POO, les données et le comportement sont définis au même endroit (dans une classe) alors qu'avec un système à entités, on sépare bien les deux : les données dans les composants, les comportements dans les systèmes. Ce qui permet de décorréler les deux et donc de ne plus subir la hiérarchie de classe comme expliqué au début. Mais du coup, ça donne une plus grande importance aux données, parce qu'on va définir nos entités par leurs composants, donc leurs données, et plus par leur comportement, leurs méthodes.

    • [^] # Re: Et les traits ?

      Posté par  . Évalué à 2.

      Complètement d'accord. L'exemple donné de "problème de la POO" n'est en fait qu'une limite de certains langages de programmation orienté objet mais pas un problème de POO.
      Les traits permettent de résoudre ce problème est encapsulant des préoccupations. Une classe peut alors se composer de traits.

      D'ailleurs la notion de "composants" qui est décrite ici me fait penser à cela car son but est de décrire une préoccupation. La différence est que c'est le système qui décrit le comportement associé aux composants et non ces derniers. Pourquoi ? J'imagine que l'on peut vouloir avoir plusieurs comportements différents pour un même composant. Dans ce cas ça ressemble fort au patron stratégie, ou au contrôleur du modèle PAC.

      Bref, je ne vois pas vraiment l'avantage par rapport à la POO. Il faudrait des exemples concrets qui montrent les bénéfices.

      • [^] # Re: Et les traits ?

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

        L'auteur a cité par exemple le côté assez dynamique des composants, que tu peux plus ou moins ajouter et retirer à la volée. Est-ce que les traits permettent ce niveau de flexibilité ?

        • [^] # Re: Et les traits ?

          Posté par  . Évalué à 1.

          Ca dépend du langage car si la composition de traits est dynamique, alors il faut un langage à typage dynamique.
          Sinon, les systèmes à base de composants sont là pour ça, l'auteur dit d'ailleurs qu'il y a quelques vagues ressemblances.

          • [^] # Re: Et les traits ?

            Posté par  (Mastodon) . Évalué à 2.

            Le problème n'est pas un problème de langage, les systèmes à entités peuvent s'implémenter de différentes manières suivant le langage. C'est un paradigme suffisamment générique pour être utilisé où on en a besoin, dans n'importe quel langage.

            • [^] # Re: Et les traits ?

              Posté par  . Évalué à 1.

              Ok ! Alors c'est un patron de conception en fait.

              • [^] # Re: Et les traits ?

                Posté par  (Mastodon) . Évalué à 2.

                Non, c'est un paradigme. Il n'y a pas de relation un à un entre les paradigmes et les langages. Certaines langages implémentent plusieurs paradigmes, d'autres un seul. Certains paradigmes peuvent être utilisés dans des langages sans que ces derniers n'offrent d'aide (exemple : l'orienté objet en C).

                • [^] # Re: Et les traits ?

                  Posté par  . Évalué à 1. Dernière modification le 16 septembre 2013 à 13:34.

                  C'est peut être un paradigme mais ce que tu dis n'est pas correct et l'exemple de l'orienté objet en C est une vaste bidouille :
                  - OO en C: il va falloir m'expliquer comment avoir la liaison dynamique, les génériques, le polymorphisme en C de manière propre. Ou alors ça s'appelle le C++.
                  - chaque langage repose sur 1 ou plusieurs paradigmes de programmation. Lorsque c'est possible (et c'est rare : c'est possible en Java par exemple pour faire de la programmation événementielle, orientée composants), rajouter un paradigme à un langage à forcement ses limites (cf. point précédent). Exemple : Scala repose sur l'impératif et le fonctionnel. Grâce au fait qu'il est possible de surcharger les opérateurs et de remplacer le '.' par un espace lors d'appel on peut simuler du déclaratif (cf. parseur combinatoire) sans vraiment avoir un langage déclaratif à la prolog.

                  • [^] # Re: Et les traits ?

                    Posté par  (Mastodon) . Évalué à 4.

                    OO en C: il va falloir m'expliquer comment avoir la liaison dynamique, les génériques, le polymorphisme en C de manière propre. Ou alors ça s'appelle le C++.

                    Hint: GObject. Après, entre le dogme (le paradigme) et la manière dont c'est implémenté, il y a toujours des compromis à faire. L'orienté objet et ses diverses variantes suivant les langages montrent bien que la «propreté» dépend vraiment des gens. Personnellement, je n'aime pas GObject mais j'aime beaucoup C++. J'aime bien Ruby aussi dans un tout autre style d'orienté objet.

                    • [^] # Re: Et les traits ?

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

                      Je répond à ton post mais aussi aux autres.
                      En fait, on peut distinguer deux approches orientés-objets :
                      - celle telle que définie par Alan Kay (le père de la POO) et au travers de laquelle l'accent est mis avant tout sur le comportement : à mes yeux, Smalltalk et Ruby s'inscrivent dans cette approche.
                      - celle, beaucoup plus pratiquée, initiée par les enfants de Simula (C++, Java, …), dans laquelle l'accent est mis avant tout sur la structuration au travers du concept de classe qui fusionne celui de de module et de type. Pour distinguer les deux, on peux désigner cette approche comme étant orienté-classe.

                      Ainsi, pour prendre l'exemple de GObject, c'est une tentative d'apporter au langage C cette couche de structuration issue de l'approche orienté-classe.
                      Et c'est aussi la raison pour laquelle la manière de programmer en Ruby diffère de celle en C++. Après, ce ne sont que des langages qui facilitent une certaine approche, mais n'empêchent pas les programmeurs de garder leurs habitudes.

                  • [^] # Re: Et les traits ?

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

                    En fait, beaucoup de langages sont conçus à la base pour un paradigme de programmation donné, mais cela n'empêche pas d'autres paradigmes d'être mis en oeuvre.

                    Quelques exemples à chaud de paradigmes étrangers:
                    - faire de l'objet en C : gobject
                    - faire de l'objet en C++: ça, c'est juste pour le troll
                    - faire du fonctionnel en C++: boost
                    - faire de l'objet en javascript
                    - faire du procédural en Caml
                    - faire du parallele avec autre chose que Erlang
                    - faire du fonctionnel en python

                    Après, le fait qu'un langage se prête plus ou moins bien à un paradigme n'est pas l'unique critère qui détermine l'utilisation d'un langage donné. La disponibilités d'autres fonctionnalités du langages, la familiarité de l'équipe de développement, l'écosystème du milieu professionnel sont autant de paramètres qui jouent.

                    Rewind est très clair: dans le milieu du jeu video professionnel, c'est C++ un point c'est tout. Donc le paradigme choisi sera mappé sur du C++.

                    Dans le milieu Gnome, le langage de référence a longtemps été le C. Donc les gnomeux ont développé une boucle d’événement et un langage objet en C.

                    Dans le milieu de la recherche, j'ai cru comprendre que c'était surtout soit du C, soit du fonctionnel. Pareil, un nouveau paradigme sera donc implémenté sur ces langages-là.

                    Après, dire "change de langage et ton paradigme marchera mieux", c'est faire preuve d'étroitesse d'esprit. Qui dit que les autres critères qui ont déterminé le choix du langage sont respectés par le langage-tip-top-cool qui va bien ? C'est comme les gens qui suggèrent à Linus de réécrire son noyau en --< insérer le langage qui va bien >-- . C'est simplement débile.

                    Il est bien sur clair que si le coeur de ton programme est de faire de la programmation fonctionnelle, un langage fonctionnel te facilitera la tâche. Mais c'est pas pour ça que tous les softs de programmation fonctionnels sont écrits en fonctionnel.

                  • [^] # Re: Et les traits ?

                    Posté par  . Évalué à 2.

                    Cf mon commentaire précédent.

                    À partir du moment où tu peux écrire dans un langage foo un interpréteur pour un langage bar, tu peux faire du bar avec du foo. Donc tu peux faire du fonctionnel pur en C et de la programmation logique en Ada.

                    C'est un peu tiré par les cheveux mais ça marche. Attention, je n'ai pas dit que c'était naturel ou efficace.

      • [^] # Re: Et les traits ?

        Posté par  . Évalué à 3.

        D'après moi, le principal avantage se ressent surtout du point de vue du non-programmeur.

        Ca permet aux autres corps de métiers du jeu vidéo (level-designer, game-designer, artistes 3d, etc…) de créer très rapidement des entités à leur convenance sans avoir à mettre le nez dans les "trucs de devs", sans avoir à passer par eux, et en utilisant des notions qu'ils comprennent facilement et qui s'intègrent bien à leurs outils.

        Dans des projets que je fais en solo, je ne trouve pas très utile de séparer données et comportements, et j'en reviens plus souvent à des modèles de programmation par traits.

        • [^] # Re: Et les traits ?

          Posté par  (Mastodon) . Évalué à 3.

          C'est un des autres avantages des systèmes à entités effectivement. Les données sont partagées entre les dev et les créateurs de contenu, le comportement c'est le domaine exclusif des dev.

          Dans des projets que je fais en solo, je ne trouve pas très utile de séparer données et comportements, et j'en reviens plus souvent à des modèles de programmation par traits.

          Oui je pourrais faire la même chose mais j'avais envie d'essayer ce paradigme pour voir ce qu'il donne dans la vraie vie.

          • [^] # Re: Et les traits ?

            Posté par  . Évalué à 1.

            Première chose qui m'est venu à l'esprit, les traits me paraissent répondre à certaines limitation de la POO.

            En tout cas, merci pour ton article. J'en redemande.

  • # « préférer la composition à l’héritage »

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

    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.

    N’est-ce pas là le cas typique où il faut appliquer le principe de « composition plutôt qu’héritage » ?

    • [^] # Re: « préférer la composition à l’héritage »

      Posté par  . Évalué à -1.

      Il en parle :

      Ce n'est pas de la programmation orientée composants, même si par certains côtés, ça se ressemble, de loin, dans le brouillard, la nuit.

    • [^] # Re: « préférer la composition à l’héritage »

      Posté par  (Mastodon) . Évalué à 4.

      N’est-ce pas là le cas typique où il faut appliquer le principe de « composition plutôt qu’héritage » ?

      Oui et non. Tu peux regarder What is an entity system framework for game development? qui montre comment on passe d'un truc orienté objet à un système à entité. Au milieu, ça ressemble à «composition plutôt qu'héritage». À mon sens, la simple composition ne suffit pas. À un moment, tu voudras ajouter un composant dynamiquement à ton entité et si tu utilises de la composition, tu ne pourras pas. Par exemple, imagine un composant Lifetime qui dit combien de temps il reste à vivre à une entité. Le héros n'a pas ce composant par défaut. Mais s'il se fait empoisonner, on peut lui ajouter dynamiquement un composant Lifetime et hop, c'est bon.

      • [^] # Re: « préférer la composition à l’héritage »

        Posté par  . Évalué à 3.

        Il suffit que la classe Héros ait un tableau de composants (composition donc) et c'est bon.
        Petit rappel utile que les zélotes oublient trop souvent : on peut tout faire avec tous les paradigmes de programmation.

        L'entité/système a la sauce objet s'appelle DCI, pour Data Context Integration. Mais cela suppose d'avoir un vrai langage orienté objet (avec traits/mix-ins) et pas un langage orienté classe à la java.

        Si j'essaye de résumer à la va vite, un objets sert alors uniquement à stocker des données de représentation. Et on lui injecte dynamiquement des comportements en fonction du contexte d'exécution.

        • [^] # Re: « préférer la composition à l’héritage »

          Posté par  (Mastodon) . Évalué à 3.

          C'est exactement ce que j'ai appelé la mauvaise manière d'implémenter les systèmes à entités ;)

          Mais oui, on peut faire comme ça. Mais non, je ne ferai pas comme ça.

          Sans compter qu'un tableau de composants, et de la composition, pour ma part, c'est différent.

          • [^] # Re: « préférer la composition à l’héritage »

            Posté par  . Évalué à 1.

            Je sens que tu vas dire qu'implémenter de façon objet un interpréteur pour un langage fondé sur des entités et d'interpréter ton « vrai » programme n'est pas non plus une bonne solution.

            Pffff

    • [^] # Re: « préférer la composition à l’héritage »

      Posté par  . Évalué à 0.

      Yep. Le drapeau rouge, c'est les 2 niveaux/3 classes abstraites pour pas grand chose, qui vient probablement d'une volonte de mutualiser qq proprietes plutot qu'un reel comportement, ainsi qu'un probable manque d'analyse du domaine (bon, la je suppute base sur mon experience, vu qu'il n'y a pas de domaine dans son exemple "en l'air", on peut pas reprocher de pas avoir analyse).

      Regle de base de la programation objet: l'heritage coute trop cher pour ce qu'il rapporte, et les profs feraient bien d'arreter d'enseigner ca en premier quand ils abordent le sujet.
      Regardez un framework comme Cocoa, vous aurez du mal a trouver qq chose qui descend plus bas que 2 niveaux, et on parle d'un framework UI vraiment complet. De meme chez Foundation, et meme Java, legendaire pour etre super complexe dans sa version ee, descends rarement a ce niveau dans sa version se (forte de qq milliers de classes, quand meme).
      On ne le repetera jamais assez: wide and shallow, pas deep and narrow.

      Sans avoir plus d'info que "c'est pour un jeu", et j'imagine un truc a la gta vu la diversite des vehicules existants:
      Une interface Vehicule, avec assez peu de choses dedans (probablement une ou deux methods, pour faire bouger le machin).
      Une classe concrete par vehicule. Probablement pas de classe abstraite, j'ai du mal a voir en quoi une voiture et une moto se comportent de facon similaire. Les differences un 4x4 et une twingo se modelisent par des donnees, pas de classes.

      Une voiture volante, si tant qu'elle se comprte vraiment comme une voiture au sol rt comme un avion en l'air, serait une implementation de Vehicule qui compose une voiture et un avion.
      Probleme resolu, et la meme solution te permet de faire une soucoupe roulante ou un avion sous marin si tu veux.

      Ca implique evidemment que les classes soient fortement data driven, sinon ca marche pas terrible, et du coup on en revient au sujet principal, qui a effectivement l'air plus adapte pour ce genre de problematique, vu la flexibilite que ca apporte (apres, en pratique, j'en sais rien, je connais pas assez le sujet).

      Linuxfr, le portail francais du logiciel libre et du neo nazisme.

      • [^] # Re: « préférer la composition à l’héritage »

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

        Pour moi, le seul moment ou tu as une classe, c'est que de façon statique et non dynamique, tu as un traitement global au niveau de la classe mère. Si il n'y a pas de traitement, il aurait fallu faire une inclusion.

        Le cas typique, est l'usine d'image, capable d'ouvrir du png ou du jpeg, mais qui retourne toujours une "image".

        "La première sécurité est la liberté"

  • # La vulgarisation de la création de jeux vidéos, c'est possible ?

    Posté par  . Évalué à 1.

    Justement, je me disais que ce serait sympa de créer une option "Jeux vidéos" dans l'enseignement.
    L'air de rien, c'est hyper complet : Maths (logique), Français (élaborer un cahier des charges), Musique

    On peut même associer d'autres matières (histoire ?) suivant le thème du jeux

    • [^] # Re: La vulgarisation de la création de jeux vidéos, c'est possible ?

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

      A propos de jeux et d'Histoire, il y a le site http://www.histogames.com/

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

    • [^] # Re: La vulgarisation de la création de jeux vidéos, c'est possible ?

      Posté par  (Mastodon) . Évalué à 9.

      En tant qu'enseignant-chercheur en informatique, je trouve qu'une option jeux vidéos, c'est trop. Et d'ailleurs, j'exècre les formations d'informatique orienté jeux vidéos, ce sont des attrape-nigauds. Parce que finalement, on retrouve dans les jeux vidéos tous les problèmes qu'on a ailleurs, il y en a assez peu qui sont spécifiques aux jeux vidéos. Le jeu vidéo permet en revanche une immersion assez rapide dans la partie «métier», parce qu'en développant un jeu vidéo, on doit forcément s'intéresser à tout un tas d'autres disciplines (comme tu le soulignes), chose qu'on fait rarement normalement.

      En revanche, avoir un club gamedev, ça oui, je pense que c'est une bonne idée (et je l'envisage de plus en plus sérieusement pour là où j'enseigne), parce que ça permet un excellent complémente de formation tout en s'amusant et en n'ayant pas la sanction de la note à la fin. Et oui, on peut associer d'autres disciplines : je n'ai qu'à traverser la rue pour être à l'école des beaux arts ;)

      • [^] # Re: La vulgarisation de la création de jeux vidéos, c'est possible ?

        Posté par  (site web personnel) . Évalué à 3. Dernière modification le 16 septembre 2013 à 11:41.

        Intéressant. Tu enseignes où ? :)

        Personnellement j'ai l'approche inverse, je pense qu'on devrait penser au jeu vidéo pour les projets, justement parce que les problèmes n'y sont pas spécifiques et qu'on y retrouve pas mal de problèmes qui existent ailleurs. Et en plus de ça, c'est fun, ça motive les étudiants.

        D'ailleurs, programmer un jeu est le projet final de ce que je considère comme un des meilleurs cours d'introduction à l'informatique au monde (bien qu'il contienne trop d'objet et de Java).

        • [^] # Re: La vulgarisation de la création de jeux vidéos, c'est possible ?

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

          Le problème c'est qu'apprendre à programmer des jeux apprends surtout à… programmer des jeux!

          Le but d'un jeu est de proposer une expérience amusant, ce qui permets d'énormes simplifications: le code peut être pourri, bourré de hacks, sans test unitaire ou gestion de la qualité, du moment que le jeu ne plante pas et est amusant, ça suffit.

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

        • [^] # Re: La vulgarisation de la création de jeux vidéos, c'est possible ?

          Posté par  (Mastodon) . Évalué à 2.

          Intéressant. Tu enseignes où ? :)

          Si tu cliques sur le bon lien dans la dépêche, tu trouveras facilement ;)

          Personnellement j'ai l'approche inverse, je pense qu'on devrait penser au jeu vidéo pour les projets, justement parce que les problèmes n'y sont pas spécifiques et qu'on y retrouve pas mal de problèmes qui existent ailleurs. Et en plus de ça, c'est fun, ça motive les étudiants.

          Ce n'est pas contradictoire. Je donne déjà pas mal de projet de jeux vidéos pour les projets semestriels au niveau licence 3. Dans mes souvenirs de ces dernières années, mes étudiants ont déjà fait : les Colons de Catane, Doodle Jump, Loups garous de Thiercelieu (sur IRC), Rampart et j'en oublie sans doute un ou deux.

          Quand c'est un projet semestriel en binôme, on n'a pas le temps de bien approfondir les choses et donc on reste cantonné à des jeux assez simples. Avec un club, on peut faire un plus gros jeu avec une grosse équipe (ce qui implique l'utilisation de certains outils) et sur toute une année plutôt qu'un semestre.

        • [^] # Re: La vulgarisation de la création de jeux vidéos, c'est possible ?

          Posté par  . Évalué à 5.

          Et en plus de ça, c'est fun, ça motive les étudiants.

          C'est un peu comme les profs d'EPS qui font faire du foot à leur élèves parce que « tout le monde aime ça ». J'ai jamais apprécié de développer des jeux vidéos et ça m'a toujours barbé d'avoir à en faire pendant mon cursus. Je n'étais pas le seul.

          Je pense que le développement de jeu éparpille trop. Il faut penser un peu à tout alors que les étudiants ont tendance à être distraits par le jeu lui même.

          Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # Nobody escape the object inquisition

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

    Les entity systems sont une variante du pattern role model!

    Une remarque à propos de l'implémentation: les "stores" pourraient être optimisés en utilisant des tableaux associatifs moins dynamiques ( http://www.boost.org/doc/libs/1_54_0/doc/html/container/non_standard_containers.html par exemple) puisque avant le lancement du jeu, on connaît tous les composants qu'une entité pourra avoir.

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

    • [^] # Re: Nobody escape the object inquisition

      Posté par  (Mastodon) . Évalué à 3.

      Les entity systems sont une variante du pattern role model!

      Ça ressemble mais je pense que ce n'est pas pareil. Déjà dans la présentation, les role model sont présentés comme un pattern orienté objet. Je pense réellement que les systèmes à entités forment un nouveau paradigme, ce n'est pas juste un pattern de plus.

      Une remarque à propos de l'implémentation: les "stores" pourraient être optimisés en utilisant des tableaux associatifs moins dynamiques ( http://www.boost.org/doc/libs/1_54_0/doc/html/container/non_standard_containers.html par exemple) puisque avant le lancement du jeu, on connaît tous les composants qu'une entité pourra avoir.

      Oui mais pendant le jeu, on va créer et détruire des entités assez souvent, donc il faut voir si le coût d'insertion et de suppression est prohibitif ou pas. Bon, c'est vrai qu'en utilisant std::set, j'ai pris le premier truc qui m'est tombé sous la main et qu'en fait, on n'a pas besoin d'ordre donc un std::unordered_set serait sans doute plus adapté. Après, il y en a plusieurs qui ne jouent pas tous le même rôle donc à voir. C'est vrai qu'à certains endroits en y repensant, on pourrait utiliser autre chose. Je vais y réfléchir ;)

      • [^] # Re: Nobody escape the object inquisition

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

        ce n'est pas juste un pattern de plus

        Dans mon passé de dev de jeux sérieux, on utilisait un genre de entity system implémenté à base de role pattern :-)

        Oui mais pendant le jeu, on va créer et détruire des entités assez souvent, donc il faut voir si le coût d'insertion et de suppression est prohibitif ou pas

        Tu peux générer un tableau de pointeur vers les composants et les index vers chaque type de composants. Quand un composant est inactif, il est à null. Avec quelques templates bien placés, tout ça peut être calculé à la compilation et bien compact.

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

        • [^] # Re: Nobody escape the object inquisition

          Posté par  (Mastodon) . Évalué à 2.

          Tu peux générer un tableau de pointeur vers les composants et les index vers chaque type de composants. Quand un composant est inactif, il est à null. Avec quelques templates bien placés, tout ça peut être calculé à la compilation et bien compact.

          Ça me paraît complexe comme tu le dis. Il faut que j'y réfléchisse plus calmement. De toute façon, avec l'implémentation actuelle, ça marche assez bien pour l'instant. J'optimiserai mes structures quand ce sera nécessaire. Dans l'immédiat, je peux remplacer quelques std::set par des std::unordered_set ou des boost::flat_set, ça ne coûte rien.

  • # en Java

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

    Pour ceux qui sont intéresses par cette approche mais qui préfèrent Java il existe l'excellente lib JMonkey Engine disponible en licence BSD.
    Leur site est une autre bonne source d'information sur l'approche entity system via les liens de leurs tutoriaux.

  • # Nomenclature

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

    De la même manière qu'MVC n'est pas un modèle mais une architecture, Entity-Component-System est aussi une archi. Certes dans la définition c'est aussi un système mais par souci d'éviter les confusions et autres redondances ça serait pas mal d'appeler ça une architecture, voire un paradigme.

    Parce que quand je vois "Entity System", ça ne décrit pas grand chose. Le nom d'"entité" peut très bien s'appliquer à une hierarchie objet par héritage traditionnelle.

    • [^] # Re: Nomenclature

      Posté par  (Mastodon) . Évalué à 5.

      Ça tombe bien, dès l'introduction je dis : «on va commencer par un peu de technique et parler des systèmes à entités. C'est un nouveau paradigme de programmation»

      Après, c'est pas moi qui ai décidé de l'appeler «système à entités». Personnellement, j'aurais trouvé un nom plus rigolo genre «paradigme des petits pois carottes» : pour faire une entité (le plat), on juxtapose plusieurs composants (petits pois et carottes) et on peut en ajouter dynamiquement des composants (steak haché et moutarde) et on a des systèmes (digestifs) qui permettent de transformer tout ça.

      • [^] # Re: Nomenclature

        Posté par  . Évalué à 2.

        Lire ça à midi, quelle torture !

        "Quand certains râlent contre systemd, d'autres s'attaquent aux vrais problèmes." (merci Sinma !)

      • [^] # Re: Nomenclature

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

        Ça tombe bien, dès l'introduction je dis : «on va commencer par un peu de technique et parler des systèmes à entités. C'est un nouveau paradigme de programmation»
        

        Jusque là, nous sommes d'accord

        Après, c'est pas moi qui ai décidé de l'appeler «système à entités».
        

        Si je puis me permettre, se référer à d'autres pour justifier un abus de langage c'est légèrement bancal. Je ne fais que présenter une suggestion, histoire d'avoir un certain degré d'exactitude dans les propos.

  • # Une solution au problème VoitureVolante ?

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

    Je n'ai pas le temps de tout lire, je le ferai tranquillement ce soir chez moi, mais je m'interroge sur une des premières questions de l'article : l'architecture OO proposée pour les voitures, avions et autres me semble ratée à partir du moment où il est envisagé de fournir des fonctionnalités à des objets qui ne devraient pas en disposer, comme la VoitureVolante.

    En effet, selon l'architecture proposée (certes ce n'est qu'un exemple, mais quand même), un Avion ne roule pas. Or, c'est faux. Si on assume qu'un véhicule a forcément des roues, on peut du coup facilement considérer que la classe Véhicule définit une méthode roule() qui peut être abstraite ou non (si on suppose qu'une voiture ne roule pas comme une moto).

    Pour le problème de rendre un véhicule volant, pourquoi ne pas utiliser une interface "Volant" (avec une méthode vole()) qu'implémente chaque classe concernée ? Avion devient un Véhicule (roulant) implémentant l'interface Volant, on a réglé le problème de façon propre.

    • [^] # Re: Une solution au problème VoitureVolante ?

      Posté par  (site web personnel) . Évalué à 1. Dernière modification le 16 septembre 2013 à 11:56.

      Tu parles déjà d'interface, tu es sorti de la « théorie » objet, avec de l'héritage multiple et te situe déjà dans une solution pour le problème du diamant, c'est à dire l'héritage simple et le sous-typage multiple, comme on peut le trouver dans Java :).

      • [^] # Re: Une solution au problème VoitureVolante ?

        Posté par  . Évalué à 2. Dernière modification le 16 septembre 2013 à 12:58.

        Tu parles déjà d'interface, tu es sorti de la « théorie » objet, avec de l'héritage multiple et te situe déjà dans une solution pour le problème du diamant

        Non. Le problème du diamant à lieu lorsqu'une classe hérite, par multi-héritage, de deux fois la même super classe. Cela ne s'applique pas aux interfaces car elles n'embarquent pas de code (attributs, corps d'opérations), juste les prototypes d'opérations (sinon pourquoi Java aurait utilisé les interfaces au lieu du multi-héritage ?).
        On ne parle pas d'héritage multiple avec les interfaces pour les mêmes raisons. On parle alors de polymorphisme.

        • [^] # Re: Une solution au problème VoitureVolante ?

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

          Je suis d'accord avec toi, et j'ai du mal m'exprimer. Je voulais juste dire que l'héritage multiple posant problème, l'une des solutions de contournement pour ajouter de l'expressivité au langage sans toutefois autoriser l'héritage multiple est d'utiliser des interfaces. Les traits sont un autre exemple.

    • [^] # Re: Une solution au problème VoitureVolante ?

      Posté par  (Mastodon) . Évalué à 1.

      En effet, selon l'architecture proposée (certes ce n'est qu'un exemple, mais quand même), un Avion ne roule pas. Or, c'est faux. Si on assume qu'un véhicule a forcément des roues, on peut du coup facilement considérer que la classe Véhicule définit une méthode roule() qui peut être abstraite ou non (si on suppose qu'une voiture ne roule pas comme une moto).

      C'est un exemple pédagogique. J'aurais pu prendre le traditionnel Forme/Rectangle/Losange avec le Carré qui vient jouer le trouble-fête.

      Pour le problème de rendre un véhicule volant, pourquoi ne pas utiliser une interface "Volant" (avec une méthode vole()) qu'implémente chaque classe concernée ? Avion devient un Véhicule (roulant) implémentant l'interface Volant, on a réglé le problème de façon propre.

      En C++, il n'y a pas d'interface. Donc tu es obligé dans ce cas de faire de l'héritage multiple. En plus, tu verras en lisant la suite de l'article qu'ici, on va plutôt placer les données au centre du jeu, et pas les comportements (ce qu'est une interface).

      • [^] # Re: Une solution au problème VoitureVolante ?

        Posté par  . Évalué à 6.

        En C++, il n'y a pas d'interface. Donc tu es obligé dans ce cas de faire de l'héritage multiple.

        Oui mais une solution (je ne dis pas que c'est la bonne) est d'utiliser une classe abstraite comme interface. Comme cette dernière n'hérite de rien, tu n'a pas de consanguinité (d'héritage en diamant).

        Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

      • [^] # Re: Une solution au problème VoitureVolante ?

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

        Oui oui j'avais bien compris que cet exemple avait un but purement pédagogique, ça va de soi :). Ma critique (gentille ! je ne cherche qu'à demander si mon raisonnement est justifié, en tant que codeur c'est bien de réviser un peu aussi) n'a pour but que de montrer que la conception est effectivement, comme tu l'as bien dit, l'étape préalable à toute ligne de code.

        Cependant, j'avoue ne pas être un spécialiste ultime du C++. Je sais bien qu'il n'existe pas d'interface au sens Java du terme dans ce langage, évidemment, et on m'a toujours dit que "ça revient plus ou moins à faire une classe virtuelle pure". Dans les faits, ce n'est pas forcément faux : une classe héritant d'une classe virtuelle pure DOIT implémenter les méthodes virtuelles pures qu'elle définit, ce qui revient au principe même de l'implémentation d'une interface. Ma question : m'a-t-on appris n'importe quoi ? Je suis curieux.

        De même, l'héritage multiple existe en C++, et on m'a souvent indiqué qu'à partir du moment où une classe doit hériter de plus d'une classe (abstraite ou pas), alors il y a un défaut de conception. Bien souvent, l'utilisation de Design Patterns se révèle une nécessite pour produire un code propre et lisible. Un jeu vidéo contient aujourd'hui plus de 100,000 lignes de code, il me semble pertinent d'indiquer qu'un code propre et lisible est une condition sine qua none d'un développement réussi. Et j'entends trop souvent dire que "si on code bien tout de suite, les Design Patterns sortent tout seul sans avoir à suivre un schéma" : ceux-là, je les mets au défi. AMHA, je doute que n'importe qui soit capable de produire "comme ça, d'un coup" quelque chose qui a nécessité des semaines de reflexion à leurs auteurs (le GOF pour ne pas le nommer).

        Par ailleurs, on parle dejà de conception technique (au sens de la préparation du code) au premier tutoriel pour la création d'un jeu vidéo? Etonnant, j'aurais vu en premier lieu une reflexion sur les idées, scénarios, gameplay, game design, etc avant… Mais comme je l'ai dit je n'ai pas encore tout lu, aussi je m'excuse par avance de cette remarque.

        • [^] # Re: Une solution au problème VoitureVolante ?

          Posté par  (Mastodon) . Évalué à 5.

          Dans les faits, ce n'est pas forcément faux : une classe héritant d'une classe virtuelle pure DOIT implémenter les méthodes virtuelles pures qu'elle définit, ce qui revient au principe même de l'implémentation d'une interface. Ma question : m'a-t-on appris n'importe quoi ? Je suis curieux.

          Oui oui, c'est bien ça, une classe non-virtuelle doit implémenter toutes les méthodes virtuelles. Si tu en oublies une, normalement tu ne pourras pas l'instancier (le compilateur va gueuler).

          De même, l'héritage multiple existe en C++, et on m'a souvent indiqué qu'à partir du moment où une classe doit hériter de plus d'une classe (abstraite ou pas), alors il y a un défaut de conception.

          Oui et non. Parfois, dans des cas très rares, ça se justifie. Mais très souvent, c'est mal utilisé.

          Et j'entends trop souvent dire que "si on code bien tout de suite, les Design Patterns sortent tout seul sans avoir à suivre un schéma" : ceux-là, je les mets au défi. AMHA, je doute que n'importe qui soit capable de produire "comme ça, d'un coup" quelque chose qui a nécessité des semaines de reflexion à leurs auteurs

          Quand tu connais bien les design pattern, au moment de la conception, tu sais reconnaître le bon pattern et du coup, tu l'appliques. Mais ça demande de l'entraînement et une très bonne connaissance des design pattern. Personnellement, je reconnais assez bien le pattern visiteur, mais pour les autres, souvent c'est plus difficile.

          Par ailleurs, on parle dejà de conception technique (au sens de la préparation du code) au premier tutoriel pour la création d'un jeu vidéo? Etonnant, j'aurais vu en premier lieu une reflexion sur les idées, scénarios, gameplay, game design, etc avant… Mais comme je l'ai dit je n'ai pas encore tout lu, aussi je m'excuse par avance de cette remarque.

          Il faut bien ménager le suspense :P Non mais j'ai découvert ça cet été et ça m'a bouleversé (sérieusement) donc je voulais en faire part à la linuxfrosphère. Après, pour le reste, ça va venir. J'ai déjà quelques idées qui sont bien fixées et d'autres qui demandent encore à être travaillé. Donc pour l'instant, je met en place la technique avant de lancer le grand spectacle :D

  • # quelques remarques

    Posté par  . Évalué à 2.

    Bravo pour cette explication du paradigme entity/system.
    Je me suis intéressé aussi à ça il y a quelques semaines. J'ai donc regardé ton code rapidement (à l'époque j'ai regardé attentivement entityx).

    J'ai quelques remarques d'ordre techniques :

    • Je n'aime pas trop le fait de devoir spécifier l'id unique de chaque composant (static const es::ComponentType type = 3;), dans entityx, il utilise un système d'attribut statique qui dépend d'un template.

    • Effectivement au sens strict une entité est juste un entier (j'ai eu du mal à comprendre pourquoi on ne stocke pas les composants directement dans entité, c'est en manipulant le concept que c'est devenu très clair : une entité est une structure abstraite et légère que l'on manipule entre les systèmes.), cependant, moi j'ai envie de manipuler les composants des entités comme cela par exemple :

      Entity e = createEntity();
      e.assign();

    • Dernier point, une des caractéristique intéressante en C++11 est les pointeurs partagés, je pense que ce serait pas mal de les utiliser dans ton code.

    • [^] # Re: quelques remarques

      Posté par  (Mastodon) . Évalué à 3. Dernière modification le 16 septembre 2013 à 13:30.

      Je n'aime pas trop le fait de devoir spécifier l'id unique de chaque composant (static const es::ComponentType type = 3;), dans entityx, il utilise un système d'attribut statique qui dépend d'un template.

      Je trouve qu'indiquer le type directement évite les problèmes. Par exemple, quand on va sauvegarder, on pourra l'utiliser sans risquer qu'il change à la prochaine exécution (parce qu'avec leur système, ça dépend du code et dans quel ordre sont créées les choses, ça crée une dépendance à l'exécution que je trouve inutile et même dangereuse).

      cependant, moi j'ai envie de manipuler les composants des entités comme cela par exemple :

      Il manque pas des choses dans ton exemple ?

      Dernier point, une des caractéristique intéressante en C++11 est les pointeurs partagés, je pense que ce serait pas mal de les utiliser dans ton code.

      Oui, pour l'instant, c'est pas très clean à ce niveau, ça pourrait être mieux. Mais il faut que je réfléchisse au calme avant de trouver le meilleur compromis.

      • [^] # Re: quelques remarques

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

        Comment tu fais un arbre d'entité ? Est-ce que des entités peuvent être référencé dans les composantes ?

        "La première sécurité est la liberté"

        • [^] # Re: quelques remarques

          Posté par  (Mastodon) . Évalué à 2.

          J'inverse l'ordre des questions ;)

          Est-ce que des entités peuvent être référencé dans les composantes ?

          Oui, évidemment. C'est l'équivalent d'un pointeur sauf que ça se sérialise directement.

          Comment tu fais un arbre d'entité ?

          Tu définis un composant Arbre avec une entité fils droit et une entité fils gauche qui auront chacun un composant Arbre s'ils ont des fils ou pas si ce sont des feuilles.

          • [^] # Re: quelques remarques

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

            ok mais l'arbre n'est même pas typé par des archetypes ou autre ?

            On peut vraiment y mettre n'importe quoi.

            Cela risque de devenir un cauchemard à debuguer non ?

            "La première sécurité est la liberté"

            • [^] # Re: quelques remarques

              Posté par  (Mastodon) . Évalué à 2.

              It's not a bug, it's a feature!

              Vraiment, ce n'est pas un problème. Dans un des articles en lien, l'auteur dit (traduction perso) : «Est-ce que les caméras peuvent tuer les gens ? Est-ce que les balles peuvent accepter une entrée utilisateur ? Non ? Et bien… est-ce que vous avez déjà joué à Unreal Tournament ?» Oui, les systèmes à entités permettent plein de choses. Mais c'est justement pour ne pas être bridé par la technique.

      • [^] # Re: quelques remarques

        Posté par  . Évalué à 1. Dernière modification le 16 septembre 2013 à 14:21.

        Effectivement l'ordre d'initialisation des statiques ne peut pas être déterminé. Cependant ce que l'on souhaite c'est sauvegarder les propriétés des composants et non pas l'id (qui pour moi peut être dynamique et définit en runtime). De tout manière il te faudra une méthode de sérialisation et de désérialisation, donc autant avoir un string d'identification, ce qui pourra rendre les fichiers générés plus lisible (je pense aux tags xml ou autre).

        oui il manque des choses dans mon exemple, en fait cela s'inspire du code d'entityx :
        Entity e = createEntity();
        e.assign(3, 2);
        e.remove();

        • [^] # Re: quelques remarques

          Posté par  . Évalué à 1. Dernière modification le 16 septembre 2013 à 14:29.

          Ah oui en fait avec entityx, les id sont créés à l'appel de la méthode family,
          il est donc possible d'appeler tous les composants :
          Component < Position > ::family();
          Component < Direction > ::family();
          comme ça l'ordre est déterministe.

          et l'exemple qui va passer cette fois-ci :
          Entity e = createEntity();
          e.assign < Position > (3, 2);
          e.remove();

          • [^] # Re: quelques remarques

            Posté par  (Mastodon) . Évalué à 2.

            il est donc possible d'appeler tous les composants (…) comme ça l'ordre est déterministe.

            Oui c'est possible mais c'est ce que je disais, tu as une dépendance envers l'exécution de ton code. Et si tu supprimes un composant, tu es mal.

            et l'exemple qui va passer cette fois-ci

            Dans entityx, il y a une référence du Manager dans l'entité, du coup, ils peuvent faire ça. Chez moi, c'est juste un entier, du coup, même encapsulé, c'est plus difficile, à moins d'avoir un singleton, mais les singletons saimal (enfin, dans ce cas là, saimal, on pourrait vouloir deux managers). Et puis bon, ce n'est pas si différent de ma syntaxe ;)

  • # Modifications du système

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

    En lisant le code https://github.com/jube/libes/blob/master/src/lib/System.cc je viens de voir que la modification du système se fait entité par entité.

    Ca peut être suffisant pour des jeux simples, mais ça peut conduire à des bugs bizarres: dans un RTS, une unité peut en tuer une autre alors qu'elle vient de mourir…

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

    • [^] # Re: Modifications du système

      Posté par  (Mastodon) . Évalué à 2.

      je viens de voir que la modification du système se fait entité par entité.

      Oui, je ne vois pas bien comment modifier plusieurs entités en même temps.

      Ca peut être suffisant pour des jeux simples, mais ça peut conduire à des bugs bizarres: dans un RTS, une unité peut en tuer une autre alors qu'elle vient de mourir…

      Effectivement. Mais ça ne va quasiment pas se voir puisque ça se résoudra à la passe suivante, donc 1/60è de seconde après voire moins.

      • [^] # Re: Modifications du système

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

        Oui, je ne vois pas bien comment modifier plusieurs entités en même temps.

        Avec un pattern "commande"? A chaque tick, on parcourt les entités/composants et au lieu de faire les mises à jour directement, on enregistre des commandes de mises à jour. Dans une seconde phase, on les applique en fonction de certaines règles.

        Pour le système d'IA, tu peux coupler ça avec un système de perception: l'entité réfléchit et fait une demande de mise à jour sur la base de sa perception de la situation.

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

        • [^] # Re: Modifications du système

          Posté par  (Mastodon) . Évalué à 2.

          Haaa ! Rien n'empêche de faire ça dans deux systèmes séparés qu'on va exécuter à la suite sur l'ensemble des entités. Comme exemple typique, je vois la gestion de la durée de vie d'une entité. Tu ne vas jamais détruire une entité dans un système, tu vas simplement ajouter (ou modifier) le composant Lifetime en indiquant que l'entité doit mourir et dans le système adéquat, tu détruis l'entité. Donc oui, ça, ça peut se faire et ça se fait. Je dirais même que c'est assez naturel en fait comme fonctionnement (ça fait des systèmes qui font peu de choses mais qui le font bien).

  • # Duck typing et implementation

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

    En dehors de l'implémentation (dont je parlerai dans la deuxième partie du commentaire), le système d'entités me fait grandement penser au duck typing que l'on retrouve dans d'autres langages (notamment python):

    Si une entité a des ailes alors c'est une entité volante.
    Si un objet a des plumes et fait "coin coin" alors c'est un canard.

    C'est très similaire comme approche.

    Et sans aller jusqu'au système d'entité ou le duck typing (on en est même loin), il y a déjà des "avancés" dans ce sens dans les langages objet "classiques" avec les interfaces et les templates.


    Tu parles d'optimisation en "linéarisant" les composants dans un tableau et ainsi en améliorer l'accès en utilisant les systèmes de cache. Je suis d'accord avec toi sur le principe mais, désolé, je ne le vois pas dans ton implémentation.

    (je me base sur ton implémentation de la lib et du tutoriel des balles rebondissantes)

    Dans la lib, le Store stocke les Component dans une map (std::map).
    - Je ne sais pas ce qu'il en est avec la norme du C++11 mais à ma connaissance rien ne garantie que les données d'une map soient stockées de manière linaire.
    - Si c'était le cas, te ne stockes pas un Component mais un pointeur. Et comme dans ton tuto tu alloues les composants à coup de new en alternant les différents types de composant, il y a très peu de chance que les composants d'un même type soit alignés.
    - Si c'était le cas, le système ne parcoure pas dans l'ordre des composants mais dans celui des entités. Pour un exemple simple, il se peut que le parcours des composants se fasse dans l'ordre, mais dans un cas concret avec surpression/ajout d'entités, il y a de fortes chances que ça soit pas le cas. Qui plus est, ça casse quand même le cache vu que tu recharges ton conteneur des entités pour passer à la suivante.

    La critique est facile, l'art est difficile. Alors je vais tenter de te proposer des axes d'améliorations :

    • Stocker les composants directement dans le Store dans un vecteur. Avoir un autre vecteur au lieu d'une map pour faire le lien entre les entités et les composants (un tableau pouvant être vu comme une map dont les clés sont des entier). Le principe est simple, tu continues d'avoir des accès en O(1) et tu gardes tes composants alignés. Dans la pratique ça se complexifie avec l'ajout/suppression d'élément dans le Store puisque qu'il faut tracer les index libérés & co (donc avoir d'autres vecteurs internes à maintenir). Du temps où je bossais dans le jeu vidéo, il était coutume de penser qu'il ne fallait pas utiliser les conteneurs de la std mais de refaire les siens. Ça fait un peu "réinventer la roue", mais la std est assez générique. Même si elle est bien implémentée, il y a beaucoup de choses qui servent à rien dans un contexte "restreint", si en plus il y a de forts besoins particuliers (performance, organisation mémoire) ça vaut souvent le cout de dev des conteneurs adaptés
    • Tu parles de Boost.Pool dans ton tuto, c'est une bonne solution pour éviter les allocations dynamiques mais je pense que c'est à la bibliothèque de gérer ce problème de mémoire et de garantir que la mémoire est alignée. (Ce point là est discutable, je l'accorde)
    • Dans ton exemples tu pourrais réunir les composants Coords et Look en un seul. À ce moment, le système Render peut parcourir directement les composants "GraphicalInfo" de manière linéaire sans passer par les entités. Par contre je n'ai pas de solution évidente pour ce qui est des systèmes "transverses" du type Graphics et Physics: vu que tu travailles sur deux composants en même temps, tu es obligé de passer par les entités. (Peut être que la littérature sur le sujet apporte une solution)

    Juste une réflexion (je sais pas où ça mène ou si c'est utile mais j'ai envie de le dire :-) ) :
    On peut voir le système d'entités/composants comme un système qui associe à un même nom (l'entité) différentes valeurs (les composants) selon "l'espace" dans lequel on regarde (les types des composants). La map dans ton Store (ou le deuxième vecteur dans ma proposition d'implémentation) étant le traducteur entre l'espace commun et les espaces spécifiques.

    Enfin, je critique mais c'est quand même une petite lib sympa que fait le boulot qui lui est demandée. Qui plus est le code est propre et il y a de la doc. (Et vu le code de merde que je suis obligé de lire en ce moment, ça fait vraiment du bien :-) ) J'attends la suite avec impatience.

    Matthieu Gautier|irc:starmad

    • [^] # Re: Duck typing et implementation

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

      Pour simplifier les parcours, est-ce qu'il ne suffirait pas d'avoir une liaison composant -> entité, qui permet de parcourir les composants dans l'ordre mais pouvoir faire référence aux entités si besoin ?

      "La première sécurité est la liberté"

    • [^] # Re: Duck typing et implementation

      Posté par  (Mastodon) . Évalué à 3.

      le système d'entités me fait grandement penser au duck typing

      C'est du duck typing dynamique si on veut.

      Je suis d'accord avec toi sur le principe mais, désolé, je ne le vois pas dans ton implémentation.

      C'est tout à fait vrai !

      Je ne sais pas ce qu'il en est avec la norme du C++11 mais à ma connaissance rien ne garantie que les données d'une map soient stockées de manière linaire.

      Tout à fait, mais ici, l'idée de la map, c'est de retrouver le composant en ayant l'entité, pas de stocker le composant. La manière de stocker le composant est laissée à l'utilisateur.

      Si c'était le cas, le système ne parcoure pas dans l'ordre des composants mais dans celui des entités. Pour un exemple simple, il se peut que le parcours des composants se fasse dans l'ordre, mais dans un cas concret avec surpression/ajout d'entités, il y a de fortes chances que ça soit pas le cas. Qui plus est, ça casse quand même le cache vu que tu recharges ton conteneur des entités pour passer à la suivante.

      Oui, l'ordre des entités n'est pas le même que l'ordre des composants. Mais globalement, actuellement, avec un set ordonné, l'ordre des entités sera l'ordre des composants (tant qu'on n'a pas détruit une entité). Si je prend un set non ordonné, là ça va poser des problèmes. J'avais trouvé un très bon article qui parlait de ce problème et qui proposait une solution (assez complexe) mais je n'ai pas réussi à remettre la main dessus au moment d'écrire l'article. Dès que je le retrouve, je le mets ici.

      Tu parles de Boost.Pool dans ton tuto, c'est une bonne solution pour éviter les allocations dynamiques mais je pense que c'est à la bibliothèque de gérer ce problème de mémoire et de garantir que la mémoire est alignée. (Ce point là est discutable, je l'accorde)

      Perso, je préfère laisser l'utilisateur faire ce qu'il a envie. S'il doit régler un problème de mémoire, il aura la main dessus. Et si jamais il n'y a pas de problème, et bien pas besoin de solution.

      Dans ton exemples tu pourrais réunir les composants Coords et Look en un seul. À ce moment, le système Render peut parcourir directement les composants "GraphicalInfo" de manière linéaire sans passer par les entités. Par contre je n'ai pas de solution évidente pour ce qui est des systèmes "transverses" du type Graphics et Physics: vu que tu travailles sur deux composants en même temps, tu es obligé de passer par les entités. (Peut être que la littérature sur le sujet apporte une solution)

      Ce cas où un système va traiter plusieurs composants est assez courant je pense. Après, rien n'empêche d'optimiser pour le cas où un système s'occupe d'un unique composant. Mais du coup, on retombe sur le problème d'avant : si c'est la bibliothèque qui alloue, c'est ok, sinon c'est foutu pour l'optimisation. Et puis le deuxième problème, c'est qu'il faut l'entité associée au composant et si tu parcoures les composants tels quels, tu auras du mal à savoir quelle est l'entité correspondante.

      On peut voir le système d'entités/composants comme un système qui associe à un même nom (l'entité) différentes valeurs (les composants) selon "l'espace" dans lequel on regarde (les types des composants). La map dans ton Store (ou le deuxième vecteur dans ma proposition d'implémentation) étant le traducteur entre l'espace commun et les espaces spécifiques.

      Oui, si je comprends bien ce que tu dis, c'est bien ça. Et je ne pense pas qu'il y ait une solution unique. J'en propose une, qui est sans doute très imparfaite mais qui m'ira bien jusqu'à ce que j'ai des problèmes de perfs. Et vu la machine sur laquelle je code, je pense que je les verrai assez vite les problèmes de perfs s'il y en a.

      Qui plus est le code est propre et il y a de la doc.

      J'ai fait des efforts ;)

      • [^] # Re: Duck typing et implementation

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

        Et puis le deuxième problème, c'est qu'il faut l'entité associée au composant et si tu parcoures les composants tels quels, tu auras du mal à savoir quelle est l'entité correspondante.

        Justement, sur un système "monocomposant", tu n'as pas à connaitre l'entité associée.


        Après y avoir réfléchi tout l’après midi, je pense que tu as raison. J'ai accroché sur ta phrase où tu parles d’optimisation par rapport au cache mais en fait c'est loin d'être important. Tu auras plein d'autre problème de perf avant de devoir régler celui la.

        En vrac on peut citer :

        • En opengl, ce qui coute cher, c'est le changement de contexte[1]. Typiquement changer la texture, le mesh ou le program (shader) courant (Il faut tj se rappeler qu'opengl est une machine à états, qui plus est distante car sur la carte graphique). La difficulté sera d'ordonner les rendus pour limiter ces changements (La vrai difficulté sera quand des objets partageront certaines textures et d'autres partageront certains mesh, le tout de manière non homogène. Faut-il limiter les changements de texture ou les changements de mesh ?).

        • Pour le moteur physique ce sera comment gérer l’interaction entre les éléments. Typiquement, pour ton exemple des balles rebondissantes, comment rajouter les collisions entre les balle ? Comment trouver les balles proches de celle actuellement traitée sans parcourir toute la liste ?

        Si tu dois optimiser ta lib, pose toi ces questions là avant de voir les problèmes de cache. (En gros continue ce que tu fais :) )

        [1] De manière surprenante, les articles et moteur 3D "basique" ne prennent absolument pas ce fait en compte. Ils ordonnent le rendu des objets pour faire les plus proches en premier et ainsi éviter de dessiner complètement les objets qui seront cachés ensuite par d'autres. Mes tests m'ont montrer que ce n'était pas vraiment là qu'il fallait optimiser. (Ça fait quelque temps que j'ai pas regardé les articles sur le sujet, ça a peut être changer entre temps.)


        Dans un de mes anciens projets pro (qui a était annulé faute de budget. :( ). Une des optims qui était prévu était de faire le rendu en deux passes :

        • Une première classique qui parcourrait les entités dans l'ordre où elles viendraient. Par contre au lieu de faire le rendu directement, elle remplirait une liste de "commande d'affichage" (mesh, program, texture, … à utiliser)
        • La deuxième, trierait cette liste pour limiter les changements de contexte et ferait le rendu réelle en parcourent la liste triée.

        Matthieu Gautier|irc:starmad

        • [^] # Re: Duck typing et implementation

          Posté par  (Mastodon) . Évalué à 3.

          En opengl, ce qui coute cher, c'est le changement de contexte[1]. Typiquement changer la texture, le mesh ou le program (shader) courant (Il faut tj se rappeler qu'opengl est une machine à états, qui plus est distante car sur la carte graphique). La difficulté sera d'ordonner les rendus pour limiter ces changements (La vrai difficulté sera quand des objets partageront certaines textures et d'autres partageront certains mesh, le tout de manière non homogène. Faut-il limiter les changements de texture ou les changements de mesh ?).

          Je fais confiance à SFML pour ça. Il y a des trucs dans SFML qui me font dire que c'est bien foutu à ce niveau là. Ou plutôt pas trop mal foutu.

          Pour le moteur physique ce sera comment gérer l’interaction entre les éléments. Typiquement, pour ton exemple des balles rebondissantes, comment rajouter les collisions entre les balle ? Comment trouver les balles proches de celle actuellement traitée sans parcourir toute la liste ?

          Je crois que je vais étendre ce petit tutoriel pour montrer comment on fait : on utilise ce qui marche déjà. En l'occurrence, des moteurs physiques 2D, il n'y en a pas 50 et on tombe très vite sur un qui est très utilisé (je crois d'ailleurs que c'est celui qui est utilisé dans Newton Adventure). Je crois que ça fera partie d'un de mes prochains articles de la série.

          Une des optims qui était prévu était de faire le rendu en deux passes :

          • Une première classique qui parcourrait les entités dans l'ordre où elles viendraient. Par contre au lieu de faire le rendu directement, elle remplirait une liste de "commande d'affichage" (mesh, program, texture, … à utiliser)
          • La deuxième, trierait cette liste pour limiter les changements de contexte et ferait le rendu réelle en parcourent la liste triée.

          C'est une technique intéressante, j'y réfléchirai le moment venu.

          En tout cas, ça fait plaisir de voir tes commentaires qui mettent un peu la pression ;) N'hésite pas à continuer.

          • [^] # Re: Duck typing et implementation

            Posté par  . Évalué à 3.

            Si je me souviens bien de ma discussion avec devnewton, le moteur de Newton Adventure est pas si bien que ça (l’auteur l’a abandonné, en a sorti un autre et a conseillé de migrer vers celui-ci).

            Ça demanderait beaucoup de travail pour changer le moteur de Newton Adventure car devnewton a essayé et c’est cassé les dents à essayer de reproduire la même physique que ce qu’il avait déjà et qui donne tout son charme au jeu.

            Écrit en Bépo selon l’orthographe de 1990

          • [^] # Re: Duck typing et implementation

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

            Je fais confiance à SFML pour ça. Il y a des trucs dans SFML qui me font dire que c'est bien foutu à ce niveau là. Ou plutôt pas trop mal foutu.

            Et bien je viens de regarder le code de SFML, tu fais bien de prendre des pincettes  ;)

            SFML est bien codé, pas de soucis la dessus mais il ne fait pas les optims sus-citées.

            Les seules optimisations faites à ce niveau sont de ne pas changer une texture (ou blend mode) si elle est déjà celle courante (https://github.com/LaurentGomila/SFML/blob/master/src/SFML/Graphics/RenderTarget.cpp#L192-L198)

            Par contre, il n'y a aucun ordre de rendu géré par SFML. C'est le client (donc ton code) qui fait le rendu dans l'ordre qu'il veut.

            Si on suppose que ton jeu contient 10 entités identiques qui s'affiche avec deux quads identiques, chacun utilisant une texture différente.
            Tu vas donc avoir au total deux textures(A et B) et un quads qu'il faudra afficher 20 fois (10 avec la texture A, 10 avec la texture B)

            • Si tu t'y prends mal, tu parcours bêtement les entités et tu fais le rendu : quad_textureA, quad_textureB, quad_textureA, quad_textureB, … À chaque fois, c'est un changement de texture et tes perfs en prennent un coup (derrière la nuque)
            • Si tu t'y prends bien, tu peux faire le rendu suivant : quad_textureA, …, quad_textureA, quad_textureB, …, quad_textureB. À ce moment, t'as que deux changement de texture et c'est la fête. Par contre ça t'oblige à avoir un algo/structure de donnée qui tient la route, tu ne peux plus parcourir dans l'ordre des entités.

            (Bien sur ne prend pas mes paroles pour des paroles d’évangile. Il y a beaucoup d'inconnus et ça m'étonnerai pas que les coûts de changement varient suivant les cartes graphiques et les drivers. On en revient à la base : Fais un truc qui marche, fais des tests, optimise. (mais pense quand même au problème de loin quand tu fais ton archi)

            Matthieu Gautier|irc:starmad

            • [^] # Re: Duck typing et implementation

              Posté par  (Mastodon) . Évalué à 2.

              Quand je disais pas trop mal foutu, je pensais en particulier aux VertexArray qui permettent justement ce que tu dis (deuxième point).

              • [^] # Re: Duck typing et implementation

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

                Le vertex array ne va t'aider que pour des choses très statiques.

                Si tu veux un grand nombre de sprites animés, qui bougent, des rotations, des zooms, il va falloir gérer l'ordre dynamiquement. Le plus simple c'est que le modèle permettent d'ordonner les entités suivant plusieurs critères (un peu comme boost.multi_index?).

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

                • [^] # Re: Duck typing et implementation

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

                  L'idée de ce genre d'optimisation multicritère n'est pas de maintenir une liste pré-trié pour chaque composant ? Genre tu tries les entités par textures utilisé, puis tu as une autre liste par mesh. Il doit être possible de ne pas avoir à retrier tout à chaque fois, car le contenu de la liste change peu. Ensuite, quand tu fais un rendu tu utilises les 2 listes.

                  "La première sécurité est la liberté"

          • [^] # Re: Duck typing et implementation

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

            Je fais confiance à SFML pour ça. Il y a des trucs dans SFML qui me font dire que c'est bien foutu à ce niveau là. Ou plutôt pas trop mal foutu.

            D'après ce que j'ai vu, c'est loin d'être optimal…

            Si tu veux de la perf, il faut utiliser un scenegraph qui va ordonner les données et les envois au GPU (cedric< proposait les EFL pour ça, mais il y a peut être des choses plus faciles faites pour la SFML).

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

  • # Component-Entity-System en ClosureScript

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

    Chris Granger, développeur de Light Table, a écrit un post-mortem sur un petit jeu qu'il a développé en ClosureScript. Il a utilisé l'approche Component-Entity-System, qui lui a permis de réduire son code à une grosse structure de données.

    Pour lui, toutes les entités sont au même niveau d'une liste, il n'y a pas de listes spécifiques par composant.
    A la place, sachant que les entités peuvent déclarer l'implémentation du composant d'affichage de cette façon :

    (component renderable [func]
               :fn func)
    

    les entités à afficher sont sélectionnées comme-ci :

    (renderer (all-e :renderable)) ;; get all entities that are renderable
    

    cette liste sera envoyée à la fonction d'affichage générale :

    ;; define a function 'renderer' that takes all renderables
    (defn renderer [renderables]
      ;; for each renderable entity
      (doseq [e renderables]
            ;; get the entity's renderable component
            (let [rend (as e :renderable)]
              ;; call the stored render function with the entity
              ((rend :fn) e)))) 
    

    Même sans connaître ClosureScript, l'article un peu technique est intéressant à suivre.

  • # SI on reprend du début...

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

    C'est très intéressant et ça invite à reprendre les choses de la feuille blanche.

    Au sein de Wordnet, l'ontologie de mots développé depuis 20 par l'université de Princeton, les mots ont un certain nombre de relations possibles entre eux :

    • Hyperonymie Généralisation : Véhicule est un hypéronyme de voiture
    • Hyponymie Cas particulier : Voiture est un hyponyme de véhicule
    • Méronymie PartOf : Roue est un méronyme de voiture
    • Termes coordonnés : 2 mots sont des termes coordonnés s'ils partage au moins un hypernyme (Chien et loup par exemple)

    Je n'ai mis que les relations entre noms propre pour rester sur ton approche orienté données.
    Mais les relations possible entre verbes, en particulier d'action, sont intéressant à analyser.

    En ce qui me concerne, j'aurai plutôt une autre approche, (mais ce n'est qu'une vague idée) :

    • Classe pour les concepts : le concept de matrice s'instancie
    • Prototype pour les objets inanimés : une fourchette est un objet qui existe et se clone, sur lequel on agit
    • Agent pour tout ce qui est animé : un agent a une vie propre et peut être animé par une machine à état hiérarchique.

    Ce pourrait être intéressant de bâtir un paradigme sur cette logique, qui plus est, Wordnet pourrait être un bon assistant dans l'IDE…

    « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

    • [^] # Commentaire supprimé

      Posté par  . Évalué à -5.

      Ce commentaire a été supprimé par l’équipe de modération.

      • [^] # Re: SI on reprend du début...

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

        Effectivement tu as visé juste en explorant les limites de mon modèle. Un modèle est toujours une simulation (et donc une simplification) de la réalité, en plus de n'être que la reconstruction d'un être forcément subjectif.
        Ce modèle que je propose est une tentative de faire ce que j'ai l'habitude de critiquer : platonifier, selon l'expression de Nassim Nicholas Taleb dans son livre Le Cygne noir

        Platonifions, donc.

        Effectivement on a :

        • Des objets complètement inertes, qui vont n'avoir d'interaction, de fragmentation (je pense à un mur) que si on agit sur eux. La limite du modèle est évidemment de les définir comme un objet pour lequel il n'est pas prévu de fragmentation de celui-ci : si je casse un mur, comment je modélise le fait que le prototype mur deviennent des milliers d'objets gravats ?

        • Des objets inactifs, mais ayant une structure impliquant qu'il puisse se comporter différemment selon leur état. Une éponge de cuisine est un objet inerte qui n'a pas le même comportement selon qu'elle est sèche ou mouillée.

        • Des objets pouvant être l'objet de changement d'état (tout ce qui est chimique), selon une vitesse de cinétique totalement variable. Dois-je les qualifier d'agents ?

        • Des entités ayant un fonctionnement interne autonome : automates, agents, système auto-organisés.

        Effectivement, la séparation en divers concept pose problème, il va falloir que je revoie ma copie. Merci pour la critique bien utile.

        « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

    • [^] # Commentaire supprimé

      Posté par  . Évalué à -10. Dernière modification le 17 septembre 2013 à 13:36.

      Ce commentaire a été supprimé par l’équipe de modération.

  • # Ada

    Posté par  . Évalué à 3.

    Génie logiciel avec Ada

    voir le chapitre : La classification en Ada et dans d'autres langages

  • # C’est abstrait mais c’est intéressant

    Posté par  . Évalué à 5.

    Ça tombe hyper bien, ça fait un moment que je me documente sur la chose, et en fait je trouve ça très bien sur le papier mais très abstrait (et quand je regarde le code ça m’aide pas tant que ça), surtout qu’il y a plusieurs façon de faire…

    Le plus simple c’est sans doute de prendre un exemple proche de moi. Je veux faire un jeu, grosso modo c’est le principe d’un Sokoban mais j’ai des caisses en bois que je peux pousser dans l’eau pour faire un pont, des interrupteurs qui ouvrent des portes quand on marche dessus. Du coup, j’ai la tuile (sol, eau ou mur en gros) qui est dessiné en premier, ensuite les mécanismes (interrupteurs, portes, pics qui sortent du sol à intervalle régulières…) et enfin les «objets» (je ne sais pas comment nommer cette catégorie: personnages, monstres, caisses…).

    Ces 3 catégories correspondraient à un attribut «level», et c’est très pratique parce que je ne peux pas avoir plusieurs éléments de même «level» qui se chevauchent, ni plusieurs éléments «bloquants» sur la même case (exemples: j’ai un mur donc je ne peux rien mettre d’autre, ou les pics sortent du sol avec la caisse dessus du coup conflit, les pics cassent l’objet dessus).

    Au début je me disais que seul les objets pouvaient bouger mais maintenant, je veux avoir des mécanismes qui peuvent bouger par exemple. Du coup je me dis qu’un ECS ça pourrait être pratique: un composant «mécanisme», un composant «mouvement», un composant «objet» pour savoir si c’est un objet qui flotte ou non et savoir et une autre propriété du style, etc. Et même avec des spécialisations, genre certains mécanismes ne sont pas à mettre à jour automatiquement ne sont pas à mettre à jour automatiquement (portes et interrupteurs sont déclenchés par l’action du joueur mais les pics qui sortent puis rentrent du sol sont automatiques).

    Le truc c’est que par exemple une IA (très basique) pour les monstres, c’est une méthode. Or j’avais compris que les composants ne stockaient que les données! Alors où se trouver la logique du jeu?

    Bref, en gros je pensais à un tableau en 3 dimensions pour stocker les tuiles, les mécanismes et les objets. Je mets à jour tout ce beau monde: automatiques + joueur + tout ce qui pourrait avoir été affecté par les précédents → ça évite que pour chaque case d’eau je vérifie si une caisse est au-dessus à chaque tour par exemple (je pense même à ne presque rien mettre en mise à jour auto et n’inscrire les caisses en automatiques que quand il y a déplacement par exemple).

    Ensuite je regarde s’il n’y a pas de conflits, sinon certains objets seront détruits (enfin marqués à destruction, on résout tous les conflits et on détruits tout ceux qui sont marqués à destruction), et enfin je déplace ceux qui ont changé de case (ceux qui ont le centre de gravité qui à changé de centaine quoi).

    Du coup grâce à cette technique je peux les dessiner dans le bon ordre, de sorte que les sprites qui dépassent sur la case du dessus ne soit pas dessinés en-dessous!


    • Un entité conteneur de composant vs pas de conteneur et id pour connaitre les entités, quels sont les avantages et les inconvénients? J’aime bien la deuxième méthode mais ça risque de compliquer mon tableau à 3 dimensions! De toute manière, il va falloir faire attention que si un composant «meurt», tous les autres composants de l’entité doivent suivre. C’est pas très compliqué sauf si on fait des listes par composants (est-ce que la vue aurait chaque composant «vue» de chaque entité? Du coup la logique et la partie graphique du jeu auraient une méthode où on passe l’id de l’entité et qui détruit ses composants).

    • Si je modifie un composant j’aimerais bien pouvoir accéder à l’entité pour lui dire de mourir proprement, avec des composants à id on peut faire ça proprement. Ou alors ma super grille ne contient que des int qui sont des id!

    • Pour les composants, j’ai l’impression qu’ils ont forcément une interface commune (au moins une méthode update), mais pas plus? N’est-il pas plus logique d’avoir un pointeur pour chaque type de composant qui est à null s’il n’en a pas? Notamment parce que je ne vois pas l’intérêt pour mon jeu de rajouter des composants à la volée. Du coup il va falloir que je fasse de l’héritage sur les composants, j’ai l’impression à lire çà et là des trucs sur les ECS que c’était «mal»! À moins que je fasse le composant directement à partir de l’interface/classe virtuelle/classe abstraite sans utiliser d’autre forme d’héritage?

    • Est-ce que je pourrait avoir un exemple très basique d’implantation de mon concept (en pseudo-code ou C++/Java/Python/…)? Parce quand c’est les concepts des autres j’ai du mal à suivre… Là je pense (j’espère) que ça m’aiderait carrément.

    Écrit en Bépo selon l’orthographe de 1990

    • [^] # Re: C’est abstrait mais c’est intéressant

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

      Or j’avais compris que les composants ne stockaient que les données! Alors où se trouver la logique du jeu?

      Un graphe de décision, une machine à états finis ou un script peut être considéré comme une donnée :-)

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

    • [^] # Re: C’est abstrait mais c’est intéressant

      Posté par  (Mastodon) . Évalué à 5.

      Ton exemple est un très bon cas d'étude. Il me rappelle un cas similaire sur Bomberman qui a des similitudes avec ton cas et duquel tu pourras sans doute t'inspirer.

      Première chose : il n'est pas sûr que ton cas soit idéal pour utiliser un système à entités. Tu pourrais rester dans une conception classique et ça marcherait, je pense. Maintenant, on peut très bien utiliser un système à entité et je vais te dire comment je ferais.

      Deuxième chose : tout ne se règle pas à coups de composants. Typiquement, le fond de carte n'est dans aucun composant, tu vas la dessiner en entier et ensuite, tu dessines le reste. Un autre classique, c'est la position du héros qui peut être dans un composant mais qui doit être accessible dans la plupart des systèmes.

      Maintenant sur le fond, reprenons.

      Du coup, j’ai la tuile (sol, eau ou mur en gros) qui est dessiné en premier, ensuite les mécanismes (interrupteurs, portes, pics qui sortent du sol à intervalle régulières…) et enfin les «objets» (je ne sais pas comment nommer cette catégorie: personnages, monstres, caisses…).

      En fait, il n'y a pas vraiment trois couches, il y en a deux, voire une et demie. Tu as ton fond de carte (ce que tu appelles tuile et qui ne change pas normalement donc que tu dessines en une seule fois avant tout le reste, ça compte donc pour un demi). Ensuite, tout le reste, ça fait partie de la même catégorie, celle des objets du jeu, éventuellement avec un composant Collideable qui contient un champ height (comme dans l'exemple ci-dessus) qui va indiquer qu'est-ce qui est à quel niveau, ou level comme tu le dis après, c'est la même idée.

      Au début je me disais que seul les objets pouvaient bouger mais maintenant, je veux avoir des mécanismes qui peuvent bouger par exemple. Du coup je me dis qu’un ECS ça pourrait être pratique: un composant «mécanisme», un composant «mouvement», un composant «objet» pour savoir si c’est un objet qui flotte ou non et savoir et une autre propriété du style, etc. Et même avec des spécialisations, genre certains mécanismes ne sont pas à mettre à jour automatiquement ne sont pas à mettre à jour automatiquement (portes et interrupteurs sont déclenchés par l’action du joueur mais les pics qui sortent puis rentrent du sol sont automatiques).

      Ton début n'est pas mauvais. Je ne mettrai pas de composant «mécanisme», parce qu'un mécanisme, c'est une entité, pas une partie d'une entité. Il n'y a pas de données dans un mécanisme (sauf peut-être un type mais en fait, chaque type de composant va être défini par ses capacités, pas par un entier qui ne veut rien dire en tant que tel). Idem pour le composant «objet». En revanche, d'après ta description, tes entités mécanisme et objet vont avoir un composant Position. Ceux qui bougent auront un composant Mouvement indiquant l'amplitude du mouvement (on peut se restreindre à un mouvement soit horizontal soit vertical). Et tu auras donc un système qui prend toutes les entités avec une position et un mouvement pour mettre à jour la position en fonction du mouvement. Tu pourrais avoir un composant Ouverture pour l'entité interrupteur qui contient l'entité porte correspondante (ou même plusieurs entités si ça peut ouvrir plusieurs portes). Et donc, un système qui prend toutes les entités avec une position et une ouverture et qui va regarder la position actuelle du héros et ouvre la porte si jamais le héros et sur la même position que l'interrupteur. Pour tes pics, c'est un peu la même idée que pour mouvement, c'est le cas typique de l'animation. Donc tu vas avoir un composant Animation qui indique le nombre d'animation et la frame courante et les durées entre chaque frame. Ce composant, tu pourras le réutiliser pour d'autres mécanismes animés. Et pour les pics, tu peux avoir un composant Danger qui indique le type de danger (destruction, etc) et à quelle frame ils apparaissent. Tu auras un système qui va prendre les entités avec animation et danger pour dire si l'entité est actuellement en mode danger ou en mode pas danger, puis un système qui va prendre les entités avec danger et position, et qui va regarder si le héros est sur un danger ou pas et si ce danger est actif ou pas, et qui peut faire la même chose avec n'importe quel objet en fait et si ça fait mal, tu ajoutes un composant Lifetime (dont j'ai parlé à plusieurs reprises dans ce thread) qui indique que l'entité est morte. Ce dernier système va également fonctionner pour les danger pas animé. Et tu auras un système qui détruit toutes les entités qui sont mortes.

      Après, avec cette conception, tu peux avoir des mix intéressants, genre des dangers animés qui servent aussi d'interrupteur (dans ton ancienne conception, je suis certain que tu n'y avais même pas pensé, ici c'est gratuit, il suffit de mettre les bons composants dans l'entité).

      Le truc c’est que par exemple une IA (très basique) pour les monstres, c’est une méthode. Or j’avais compris que les composants ne stockaient que les données! Alors où se trouver la logique du jeu?

      Non, ce n'est pas une méthode, c'est un système ! Tu auras un composant IA (avec ce qu'il faut pour que ça marche, par exemple fuite ou recherche pour savoir si l'entité fuit le héros ou le recherche) et ton système va prendre tes entités avec un composant IA et une position et va mettre à jour tout ça. Du coup, tu peux ajouter un composant IA à n'importe quel entité, montre ou même interrupteur, ou même porte (une porte qui fuit le héros, ça c'est fun).

      Bref, en gros je pensais à un tableau en 3 dimensions pour stocker les tuiles, les mécanismes et les objets. Je mets à jour tout ce beau monde: automatiques + joueur + tout ce qui pourrait avoir été affecté par les précédents → ça évite que pour chaque case d’eau je vérifie si une caisse est au-dessus à chaque tour par exemple (je pense même à ne presque rien mettre en mise à jour auto et n’inscrire les caisses en automatiques que quand il y a déplacement par exemple).

      Heu, à moins que tu aies des milliers d'objets (et donc des cartes énormes), je pense que c'est une optimisation prématurée. Quant au tableau, il est parfaitement inutile, sauf pour les tuiles (donc il est à deux dimensions). Tout le reste, ce seront des entités. Et la mise à jour se fait dans des systèmes. Je n'ai pas évoqué tous les systèmes mais tu auras un système pour les collisions par exemple.

      Du coup grâce à cette technique je peux les dessiner dans le bon ordre, de sorte que les sprites qui dépassent sur la case du dessus ne soit pas dessinés en-dessous!

      La manière dont chaque entité est dessiné et la logique du jeu sont deux choses séparées. Ton système de dessin va effectivement regarder toutes les entités avec un sprite et un level, peut les ordonner au préalable et les dessiner, mais quelque part, peu importe leur type, ça reste des sprites avec une hauteur.

      Un entité conteneur de composant vs pas de conteneur et id pour connaitre les entités, quels sont les avantages et les inconvénients? J’aime bien la deuxième méthode mais ça risque de compliquer mon tableau à 3 dimensions! De toute manière, il va falloir faire attention que si un composant «meurt», tous les autres composants de l’entité doivent suivre. C’est pas très compliqué sauf si on fait des listes par composants (est-ce que la vue aurait chaque composant «vue» de chaque entité? Du coup la logique et la partie graphique du jeu auraient une méthode où on passe l’id de l’entité et qui détruit ses composants).

      Du coup, tu peux choisir la deuxième, je t'ai donné des pistes pour supprimer ton tableau à trois dimensions. Pour la durée de vie des entités, tu peux regarder le tutoriel dont le parle, je donne une piste à base de composant Lifetime.

      Si je modifie un composant j’aimerais bien pouvoir accéder à l’entité pour lui dire de mourir proprement, avec des composants à id on peut faire ça proprement. Ou alors ma super grille ne contient que des int qui sont des id!

      Normalement, dans les systèmes, tu modifies des entités et tu accèdes à leurs composants. Donc tu as les entités correspondant aux composants. Et ta super grille, supprime la ! ;)

      Pour les composants, j’ai l’impression qu’ils ont forcément une interface commune (au moins une méthode update), mais pas plus?

      Non ! Ce sont des données, rien de plus. Le code de ta méthode update, il va dans un système, pas ailleurs.

      N’est-il pas plus logique d’avoir un pointeur pour chaque type de composant qui est à null s’il n’en a pas?

      Si tu as une entité container de composant, peut-être. Sinon, ça n'arrive pas puisque tu traites dans ton système uniquement les entités qui ont les bons composants.

      Notamment parce que je ne vois pas l’intérêt pour mon jeu de rajouter des composants à la volée.

      Pour l'instant non. Mais le composant Lifetime, tu peux l'ajouter à la volée, le composant IA, tu peux l'ajouter à la volée (en déclenchant le réveil du monstre avec un interrupteur invisible !). Bref, je vois plein d'extension facile avec les quelques composants que j'ai décrit.

      Du coup il va falloir que je fasse de l’héritage sur les composants, j’ai l’impression à lire çà et là des trucs sur les ECS que c’était «mal»!

      Oui, saimal ! Si tu hérites tes composants, c'est qu'en fait, tu as deux composants : un avec les données de la classe mère et un avec les données de la classe fille (sans les données de la classe mère). Ou alors, tes composants ne sont pas assez atomiques. Ou alors, tes composants sont mal faits.

      Est-ce que je pourrait avoir un exemple très basique d’implantation de mon concept (en pseudo-code ou C++/Java/Python/…)? Parce quand c’est les concepts des autres j’ai du mal à suivre… Là je pense (j’espère) que ça m’aiderait carrément.

      J'ai fait ce que j'ai pu, j'espère que ça t'aura éclairé. Je te conseille vraiment d'aller voir l'exemple donné au début (et aussi de suivre le lien donné au début de l'article).

      • [^] # Re: C’est abstrait mais c’est intéressant

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

        Heu, à moins que tu aies des milliers d'objets (et donc des cartes énormes), je pense que c'est une optimisation prématurée.

        Ca va vite dans les jeux: les niveaux de Newton Adventure font 64x64 cases, soit déjà plus 4000 objets, alors qu'on est très loin de la taille des cartes d'un rpg.

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

        • [^] # Re: C’est abstrait mais c’est intéressant

          Posté par  (Mastodon) . Évalué à 4.

          Ca va vite dans les jeux: les niveaux de Newton Adventure font 64x64 cases, soit déjà plus 4000 objets, alors qu'on est très loin de la taille des cartes d'un rpg.

          Et combien d'objets «bougent», c'est-à-dire ont un mouvement ? Typiquement si tu fais la détection de collision à la main, pas besoin de croiser l'ensemble de 4000 objets (soit 16000000 de tests), il suffit de partir des quelques objets en mouvement (donc un sous-ensemble assez petit des 4000 objets) et de les croiser avec l'ensemble des 4000 objets. Et là, j'ai déjà un algorithme plutôt naïf qui permet de ne pas effondrer les perfs tout de suite.

          • [^] # Re: C’est abstrait mais c’est intéressant

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

            Ca va de quelques dizaines à quelques milliers, car dans certains niveaux, les plateformes sont "vivantes" (elles peuvent bougent ou peuvent disparaître).

            J'ai un quadtree qui sert à la fois à limiter le travail du moteur physique et calculer les objets visibles. J'ai du le mettre en place au début du développement.

            A part pour de très petits jeux, ce genre d'optim (tout comme le regroupement par texture, l'envoi des vertices par batch…) ne sont pas du tout prématures, surtout si on veut qu'il tourne sur un netbook ou sur mobile.

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

        • [^] # Re: C’est abstrait mais c’est intéressant

          Posté par  . Évalué à 2.

          Ah ouais, mine de rien 64×64 c’est déjà ÉNORME! J’avais jamais fait le calcul…

          Écrit en Bépo selon l’orthographe de 1990

      • [^] # Re: C’est abstrait mais c’est intéressant

        Posté par  . Évalué à 3.

        Pour la pertinence de l’ECS, l’architecture de mon programme n’en finissait plus de changer et de se complexifier, je pense que ça réponds exactement à mon problème, et en plus ça m’apprend à penser différemment. Donc dans tous les cas je suis gagnant.

        En résumé, l’entité en elle-même n’est qu’un un amas de composant qui contiennent eux les données. C’est l’objet/machine état/autre système qui s’occupe contient toutes les règles de jeu, un peu comme si l’entité avait une fiche perso, chaque composant serait une compétence ou un aspect du personnage, et le système chapeaute tout ça comme un maitre de jeu.

        J’ai pris conscience que je n’appliquais sans doute pas assez la règle de l’article sur le Bomberman: réduire au minimum les composants, ça permet en plus d’être vachement plus flexible pour des modifications futures… Un autre intérêt semble être que toute la logique est au même endroit donc ça doit sans doute faciliter les évolutions des composants, non?

        Enfin, je tenais à te remercier pour l’idée de l’interrupteur piégé: c’est tout bonnement diabolique, et faudrait que je détaille plus pour l’expliquer mais c’est super intéressant pour mon gameplay. Il faut savoir que c’est largement repris d’un jeu déjà existant mais qui est sorti il y a un certain moment, sous Windows et surtout pas libre. Donc je suis à la recherche d’idées pour démarquer mon jeu de l’original (et le rendre bien meilleur!).

        En tout cas merci beaucoup, je vais prendre le temps de digérer tout ça. J’ai juste pas compris comment sans grille je suis censé savoir quels éléments qui ne se mettent pas à jour automatiquement peuvent être mis à jour (optimisation prématurée, peut-être, je préfère être certain de pouvoir la faire en cas de besoin au moins).

        Pour le dessin de la carte, si j’ai bien compris c’est possible de dessiner un élément en-dessous d’un autre, donc l’ordre importe peu. Je comprends alors bien mieux comment c’est fait dans les autres jeux.

        Écrit en Bépo selon l’orthographe de 1990

  • # Remarques rebondissantes

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

    Quelques remarques sur l'exemple du tutoriel avec les balles rebondissantes:

    • Tu bases tout sur SFML, ce que je trouve un peu dommage, pour moi un des avantages de ce paradigme c'est de séparer proprement le rendu du reste, pour pouvoir faire plusieurs implémentations du système rendu (Tu pourrais donc avoir une version SDL, une version SFML et une version console des mêmes balles rebondissantes en recodant uniquement le rendu).
    • Je saisi pas bien ce besoin d'avoir un composant avec les coordonnés réels, pour moi c'est interne au rendu, et donc le système rendu devrait directement travailler avec le composant position. En plus en SFML ce genre de chose se gère avec un viewport, ce que tu vas avoir du mal à faire si tu sépare rendu et traduction des coordonnés.
    • Comme dit plus haut c'est triste cet entier à mettre à la main dans toutes les classes :-(
    • [^] # Re: Remarques rebondissantes

      Posté par  (Mastodon) . Évalué à 3.

      Tu bases tout sur SFML, ce que je trouve un peu dommage, pour moi un des avantages de ce paradigme c'est de séparer proprement le rendu du reste, pour pouvoir faire plusieurs implémentations du système rendu (Tu pourrais donc avoir une version SDL, une version SFML et une version console des mêmes balles rebondissantes en recodant uniquement le rendu).

      Oui, j'utilise des classes de SFML (en fait une seule, Vector2f) par souci de simplicité. Je pourrais en effet recoder cette classe et faire une copie à chaque tour. Mais autant être simple et utiliser le bon type. Il me semble que ça ne sert à rien de se dire qu'on va utiliser plusieurs rendus quand on n'en utilisera qu'un seul. Oui, ça serait possible mais ce n'est pas un exercice qui m'intéresse plus que ça. Du coup, je m'autorise à reprendre des types de SFML.

      Je saisi pas bien ce besoin d'avoir un composant avec les coordonnés réels, pour moi c'est interne au rendu, et donc le système rendu devrait directement travailler avec le composant position. En plus en SFML ce genre de chose se gère avec un viewport, ce que tu vas avoir du mal à faire si tu sépare rendu et traduction des coordonnés.

      Au contraire, SFML gère très bien la traduction entre coordonnées de l'univers et coordonnées de l'écran grâce à une View. Dans l'exemple, j'ai fait la traduction moi-même mais c'était pour l'exemple. Après, on peut déléguer ça à SFML (il suffit juste de définir la View qui va bien).

      Comme dit plus haut c'est triste cet entier à mettre à la main dans toutes les classes :-(

      Meunon, ça s'automatise tout ça.

      • [^] # Re: Remarques rebondissantes

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

        Il me semble que ça ne sert à rien de se dire qu'on va utiliser plusieurs rendus quand on n'en utilisera qu'un seul.

        Je trouve ça quand même dommage, pouvoir passer à un rendu console ou 3D facilement, c'est chouette :)

        Au contraire, SFML gère très bien la traduction entre coordonnées de l'univers et coordonnées de l'écran grâce à une View. Dans l'exemple, j'ai fait la traduction moi-même mais c'était pour l'exemple. Après, on peut déléguer ça à SFML (il suffit juste de définir la View qui va bien).

        Justement, ça fait pour moi partie du rendu. Et je pensais que c'était pas simple de séparer les deux actions en SFML mais ça fait quelque temps que j'y ai pas touché et tu as l'air sûr de toi donc ça doit être faisable.
        Juste que ça me parait overkill de séparer le rendu en deux systèmes, j'ai du mal à voir ce que ça apporte.

        • [^] # Re: Remarques rebondissantes

          Posté par  (Mastodon) . Évalué à 5.

          Je trouve ça quand même dommage, pouvoir passer à un rendu console ou 3D facilement, c'est chouette :)

          Oui, mais tu vois, tu n'auras pas les mêmes besoins pour chaque rendu. Entre un ou plusieurs caractères pour le rendu console, un sprite pour le rendu 2D et un modèle 3D pour le rendu 3D, si tu dois définir et sauvegarder les trois en permanence, tu en as deux inutiles en permanence. Et il y a certainement d'autres choses à prendre en compte : les collisions en 2D et en 3D, c'est pas exactement pareil. Donc, ce n'est pas juste une histoire de rendu, ça peut avoir une influence. Après si on reste dans un seul domaine, genre en 2D ou en 3D uniquement, là, on pourrait sans doute faire des choses, mais est-ce bien utile ?

          Justement, ça fait pour moi partie du rendu. Et je pensais que c'était pas simple de séparer les deux actions en SFML mais ça fait quelque temps que j'y ai pas touché et tu as l'air sûr de toi donc ça doit être faisable. Juste que ça me parait overkill de séparer le rendu en deux systèmes, j'ai du mal à voir ce que ça apporte.

          Pour moi, ce sont deux choses bien séparés. D'ailleurs, typiquement, tu as deux composants pour ce genre de chose et tu peux très bien imaginer des entités qui ont l'un et pas l'autre. Une entité qui a une position dans le monde mais qui est invisible (donc pas de position à l'écran), c'est assez courant. Et à l'inverse, des élements d'interface graphique qui vont avoir des positions sur l'écran mais pas dans le jeu (genre un score, un nombre de vie), ça existe aussi. Les deux sont nécessaires.

  • # Ah les enfants

    Posté par  . Évalué à -3.

    Oh sympa, un système de contraception objet pour éviter de concevoir des enfants à tout va.

  • # allocation à l'arrache

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

    Est-ce que le fait de préallouer de la mémoire ne serait pas un bon moyen pour avoir l'allocation aligné ?

    En gros, si fixe un max de N objects, chaque composant est allouer N fois, pour trouver le composant allouer est garder l'alignement mémoire, tu passes par une tables de pointeur.
    Genre tu as :

    composant composantPool[N];
    composant * composantAddress[N];

    getComposant (int entite){
    composant * comp = composantAddress[entite];
    if (comp) {
    return * comp;
    } else {
    return null;
    }
    }

    C'est bourrin mais pour quelques milliers d'objet, cela ne consomme pas trop de mémoire, tout est aligné, et l'accès est bien plus rapide qu'avec une table de hash. En plus, Linux ne doit allouer la mémoire que si elle est écrite. Donc, le "haut" du composant pool n'est pas réellement allouer.

    "La première sécurité est la liberté"

    • [^] # Re: allocation à l'arrache

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

      En effet, l'usage d'un std::array pourrait être envisagé, puisque les composants sont stockés par type.

      std::array<Component, N> composantPool;
      std::array<Component *, N> composantAddress;
      
      Component getComponent(int entity)
      {
          Component * comp = std::nullptr_t;
          try
          {
              comp = composantAddress.at(entity);
          }
          catch(std::out_of_range & e)
          {
              comp = std::nullptr_t;
          }
      
          return comp;
      }
      • [^] # Re: allocation à l'arrache

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

        mettre un try/catch si bas niveau, c'est si transparent que ça ? J'ai un doute en C++.

        "La première sécurité est la liberté"

        • [^] # Re: allocation à l'arrache

          Posté par  . Évalué à 1.

          Je ne suis pas sûr de comprendre ce que tu entends par try/catch de bas niveau, mais si c‘est au sujet de la vérification des accès au tableau (ce qui peut lever une exception), c’est juste parce que le code utilise la méthode at au lieu de l‘opérateur []

        • [^] # Re: allocation à l'arrache

          Posté par  (site web personnel, Mastodon) . Évalué à 1. Dernière modification le 20 septembre 2013 à 14:03.

          Je ne comprends pas ta phrase dans ce contexte.

          Au fait, j'ai oublié un * dans le type de retour de la fonction, my bad.

          • [^] # Re: allocation à l'arrache

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

            Pour moi, la gestion d'interruption est quelques choses de lourds. Ici, il s'agit d'un getter, un truc très très sensible à la performance.

            Souvent, dans le cas de try/catch, le compilateur ne fait pas d'inline, ce qui peut être finalement très couteux. Un simple test par rapport à la borne supérieur et à la nullité est suffisant, c'est souvent beaucoup plus efficace.

            "La première sécurité est la liberté"

            • [^] # Re: allocation à l'arrache

              Posté par  (Mastodon) . Évalué à 3.

              Un simple test par rapport à la borne supérieur et à la nullité est suffisant, c'est souvent beaucoup plus efficace.

              Surtout dans ce cas où la taille du tableau est connue à l'avance puisque c'est une constante.

            • [^] # Re: allocation à l'arrache

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

              Ça tombe bien, il n'y a pas d'interruptions, mais une exception. Et les exception ne sont pas lourdes.

              J'ai attrapé l'exception parce que je voulais respecter l'interface initialement proposée. Mais très franchement, l'exception devrait être propagée, car tenter d'accéder au-delà de l'identifiant d'entité maxiamal est une erreur de programmation, qui doit être géré par l'appelant.

              Quand à l'argument foireux de l'inline, tu me fais doucement rire. On parle d'une fonction qui est définie dans un module. Elle ne sera donc pas inlinée dans les modules appelant.

    • [^] # Re: allocation à l'arrache

      Posté par  (Mastodon) . Évalué à 2.

      Je crois que c'est comme ça que fonctionne Boost.Pool. Et oui, c'est une bonne idée pour allouer les composants. Après, comme toutes les entités n'ont pas tous les composants, tu auras sans doute plein de cases vides. Donc, je ne suis pas persuadé que ça soit si bien que ça. Actuellement, la lib utilise un std::set pour avoir la correspondance entre une entité et son composant, parce que c'est à l'utilisateur d'allouer ses composants comme il le souhaite. Après toutes les discussions qui ont eu lieu ici, je vais sans doute réfléchir à faire l'allocation des composants dans la lib.

      • [^] # Re: allocation à l'arrache

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

        "tu auras sans doute plein de cases vides."

        Non, c'est l’intérêt du componentAdress qui peut être aussi un simple tableau de short (max 65 000 objets, ce qui est pas mal). Cela permet d'éviter d'avoir trop de trou et de bénéficier du cache.

        "La première sécurité est la liberté"

        • [^] # Re: allocation à l'arrache

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

          Lorsque tu créé un tableau d'objets, tous les objets sont alloués et initialisés (constructeur par défaut appelé).
          Ce n'est pas le cas si tu alloue un std::vector suivi d'un std::vector::reserve, où seule l'allocation sera effectuée.

          Mais oui, il y aura de la mémoire perdue, forcément.

          • [^] # Re: allocation à l'arrache

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

            Dans ce cas, utilises une variable global comme pour le C, avec un tableau de structure, elle sera alloué dans le BSS, et ne consommera pas de mémoire, si il n'y en a pas besoin.

            "La première sécurité est la liberté"

            • [^] # Re: allocation à l'arrache

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

              C'st une plaisanterie ? Tu ne consommes pas de mémoire parce que tu la réserves à l'invocation du programme ? Il y aura forcément une perte d'espace mémoire pour le gain de performance, c'est un compromis classique.

              • [^] # Re: allocation à l'arrache

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

                Ben non justement, l'allocation mémoire est retardé sous linux. Linux n'alloue réellement la mémoire qu'au premier accès. Donc la mémoire n'est pas rééllement consommé au lancement.

                "La première sécurité est la liberté"

                • [^] # Re: allocation à l'arrache

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

                  Ceci est valable pour la mémoire dynamique. Les sections statiques du programme sont directement mappées dans la mémoire du processus. Ensuite, Linux fait du COW sur ces sections, certes, mais stricto sensus, la mémoire est allouée.

                  • [^] # Re: allocation à l'arrache

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

                    Elle est alloué d'une certain façon, mais les pages physiques ne sont pas mappé à ce niveau là. Le système sait juste qu'en cas de fautes mémoires, il faut allouer physiquement les pages et non pas retourner un segfault.

                    Le COW, c'est en cas de fork, sur la gestion des segments de data, qui ne sont dupliquer qu'en cas de modification.

                    "La première sécurité est la liberté"

                    • [^] # Re: allocation à l'arrache

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

                      Bah, exec, cow, c'est du pareil au même :p

                      Notes bien que ce n'est pas grave, pour moi, que ça prenne une place monstre en mémoire. Tant que ça permet d'améliorer les performances, l'usage du matériel doit être total.

                      • [^] # Re: allocation à l'arrache

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

                        L'avantage c'est que c'est plus rapide. Le défaut, c'est que tu peux avoir des plantages mémoires, après l'allocation (si tu as un "overcommit" supérieur à 1 ou 100%)…

                        L'avantage aussi, c'est qu'un new ou malloc en plein milieu d'un code code "temps réel", c'est prendre un gros risque sur la latence (min : qq cycles, max 15 000 cycles). Le top, c'est tout de même de ne plus avoir d'allocation mémoire après un certain stade. Tu es à peu prés sûr que ton code ne peut plus planter pour des raisons externes.

                        "La première sécurité est la liberté"

                        • [^] # Re: allocation à l'arrache

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

                          On ne parle pas ici de temps réel, mais de jeux vidéos. C'est bien ce que je reproche un peu à ton propos. Si je voulais faire mon chaint, j'aurais pu commencer à parler des problèmes d'orthogonalité d'un code défini dans le header, inliné dans tous les modules ; des problèmes de compilation qui verrons le jour quand la structure d'une classe sera changée, et que tous les modules l'utilisant ne sont pas recompilé (le problème classique de make qui ne prend pas en compte les en-têtes, car il ne peut pas), etc.

                          L'overcommit memory est une optimisation acceptable quand la fiabilité n'est pas priomordiale, mais que la vitesse d'exécution l'est. C'est le cas typique d'un jeu vidéo. Je suis cependant moins enchanté lorsque c'est une base de données qui est trompée par l'OS. Sur ce genre de machines, j'aurais tendance à désactiver l'overcommit (en plus de la swap).

                          • [^] # Re: allocation à l'arrache

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

                            Un jeu vidéo, c'est du temps réel. Il n'y a pas de vie en jeu, mais demande au joueur si il aime les lags.

                            "(le problème classique de make qui ne prend pas en compte les en-têtes, car il ne peut pas),"

                            Si il peut. (gcc -MM de tête) C'est juste très chiant, car en général, une simple modification peut entrainer une cascade de recompilation. En général, les .h ne sont pas inclus, les .c impactés étant aussi modifiés, cela ne se voit pas. En pratique, il faut parfois faire un "make clean".

                            "La première sécurité est la liberté"

                            • [^] # Re: allocation à l'arrache

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

                              Un jeu vidéo, ce n'est pas du temps réel (TR). Le TR, c'est garantir qu'une opération peut être effectuée en un nombre de cycle déterminé, et ce de manière déterministe. Sous architecture Intel, c'est le genre de chose impossible, par exemple. Tu trouveras des tas d'articles qui vont prétendre qu'on peut faire du TR sous Intel, mais dans l'absolu, la moindre instruction (sauf div) peut être victime d'une interuption matérielle, ce qui rend impossible à la plate-forme de garantir un nombre de cycles précis pour une instruction assembleur.

                              Je parlais de make. Make ne sait pas ce qu'est un fichier C++. Il sait simplement qu'un .C va être compilé en .o par gcc suivant une règle par défaut. Mais il ne peut pas deviner quels en-têtes un module inclus. Alors certes, il est possible de contourner avec l'option -MM de gcc, en créant un fichier de dépendance. Mais je n'ai jamais réussi à le mettre en place de manière fiable.

                              En pratique, je fais dans l'orthogonalité, et j'applique le pimpl idiom, histoire de me blinder contre les mauvaises surprises.

                              • [^] # Re: allocation à l'arrache

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

                                " Le TR, c'est garantir qu'une opération peut être effectuée en un nombre de cycle déterminé"

                                N'importe quoi.

                                Le TR c'est garantir un temps d’exécution borné (réagir en moins de 100ms max, exécuter une fonction cycle en 20ms max). Tes histoires d'instructions qui mettent plus ou moins de temps, on s'en tape complètement, ce temps max est borné. Il suffit d'en tenir compte. En TR dure, ils ont peur des caches write back, mais bon… ils ont une bonne quantité de marge.

                                Les jeu vidéo, les lecteurs multimédias sont des TR mou et c'est finalement plus dure à faire. Un TR dure qui franchi sa limite, plante, un TR mou doit faire quelques choses d'intelligent (frame drop, freeze, …).

                                "Je parlais de make. Make ne sait pas ce qu'est un fichier C++. Il sait simplement qu'un .C va être compilé en .o"

                                C'est également n'importe quoi. make sait simplement qu'un fichier dépend d'autres fichiers, et dispose d'une commande shell pour recréer le fichier si une des dépendances est plus récente. Cela peut être les fichiers .c mais aussi .h.

                                "Mais je n'ai jamais réussi à le mettre en place de manière fiable."

                                Et alors ?

                                "La première sécurité est la liberté"

                                • [^] # Re: allocation à l'arrache

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

                                  Aucun ordinateur ne peut garantir une exécution dans un temps donné, parce que tout simplement il ne le peut pas physiquement. La seule chose qu'un processeur peut éventuellement garantir, c'est le nombre de ticks processeurs qui seront alloués à une instruction. Et c'est en comptant ces ticks par instruction qu'on peut déterminer en combien de ticks un programme peut s'exécuter. Cela permet de calculer un temps statistique d'exécution, mais il ne pourra pas être garanti avec précision.

                                  Ce que tu appelle du RT mou, ce n'est pas du RT. C'est une exécution sous contrainte de temps, mais ça n'a rien à voir avec une quelconque garantie.

                                  C'est gentil de dire que je raconte n'importe quoi pour ensuite me paraphraser. Quand je dis « Il sait simplement qu'un .C va être compilé en .o », ça veut bien dire qu'il va vérifier le ctime du .C et lui appliquer une commande pour le transformer en .o. Je n'excluait pas d'autres suffixes.

                                  J'aime la fiabilité. C'est énervant quand un programme est lent, mais c'est la panique quand il tombe en marche pour aller plus vite. En l'occurrence, j'ai bien déjà tenté d'utiliser les mécanismes de génération de dépendance, mais il faut bien avouer que ça ne fonctionne pas toujours. Je préfère quand ça ne marche jamais, ou quand ça marche tout le temps, je déteste l'entre-deux plein de terreur, plein de sombritude. Mais si tu as une ressource qui peut m'aider, tu es le bienvenue (et non, je ne replongerais pas dans autoshit, je tiens à conserver ma santé mental :D ).

                                  • [^] # Re: allocation à l'arrache

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

                                    "Ce que tu appelle du RT mou, ce n'est pas du RT. C'est une exécution sous contrainte de temps, mais ça n'a rien à voir avec une quelconque garantie."

                                    Et pourtant si tu ne respectes pas tes 30 images/s ton film est illisible, idem pour le son. Pour un jeu, si le temps de réaction est au dessus d'une certaine valeur, c'est injouable. C'est ça le TR mou.

                                    "Cela permet de calculer un temps statistique d'exécution, mais il ne pourra pas être garanti avec précision."

                                    Oui et on s'en contre-fou. On détermine un ou une série de path d'execution long, on prends 50% de marge et c'est bon (avec cache froid, sans appel système, ni lock). Dans la vrai vie.

                                    gcc -MM c'est pas mal. Mais globalement, make, c'est pas top. cmake semble avoir la faveur de beaucoup de monde. si tu as un petit projet, tout recompiler ne prend en général pas trop de temps, non plus.

                                    "La première sécurité est la liberté"

                                • [^] # Re: allocation à l'arrache

                                  Posté par  . Évalué à 2.

                                  Je parlais de make. Make ne sait pas ce qu'est un fichier C++. Il sait simplement qu'un .C va être compilé en .o

                                  C'est également n'importe quoi. make sait simplement qu'un fichier dépend d'autres fichiers, et dispose d'une commande shell pour recréer le fichier si une des dépendances est plus récente. Cela peut être les fichiers .c mais aussi .h.

                                  Pourtant il possède une règle qui fait que si je lui dis de créer des fichiers objets à partir de sources C ou C++, je n'ai pas besoin de lui indiquer comment faire. Bien sûr il n'analyse pas les sources, il sait que pour compiler un fichier .c en .o, il doit utiliser cc idem pour compiler des fichiers .o en binaire exécutable. Fait le test écris un hello world en C ou C++ que tu appel par exemple titi.c (si c'est du C) et lance la commande make titi sans avoir de makefile dans ton dossier. Par magie il va savoir quoi faire.

                                  Donc oui make c'est pas top, il ne fait pas tout mais il sait qu'on compile un fichier c en .o (même s'il ne sait pas toujours le faire, ni quand).

                                  Sinon c'est comme si tu disais qu'il ne sait pas quand il doit compiler parce que se baser sur le ctime c'est pas toujours une bonne solution.

                                  Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)

  • # Généralisation du système à entités

    Posté par  . Évalué à 1.

    Je voudrais voir s'il est possible de généraliser ce système à entités et de l'appliquer au développement de mon site internet en PHP. Quelqu'un aurait-il des pistes ou des conseils pour m'aider à débuter ?

    • [^] # Re: Généralisation du système à entités

      Posté par  (Mastodon) . Évalué à 2.

      Ça, c'est la question piège ! Et honnêtement, je n'ai pas de réponse. Je n'ai pas vu d'autres applications des systèmes à entités que dans les jeux vidéo. Après, pour un site web, j'ai un doute sur l'application directe. Je pense qu'il faut adapter le concept, notamment par rapport à la base de données (qui va contenir les composants). Mais je n'ai aucun pointeur à donner.

    • [^] # Re: Généralisation du système à entités

      Posté par  . Évalué à 0.

      Moi je l'imaginerai ainsi :
      * une table "Etats" à 3 champs :
      . * identifiant "objet",
      . * etat,
      . * paramètres (bonus);
      * une table "Positions" à 5 champs :
      . * identifiant "objet",
      . * x début,
      . * x fin,
      . * y début,
      . * y fin,
      * une fonction "traitement_etat_1" pour chaque état à traiter;
      * une fonction "gestion_etat" pour lancer les fonctions précédentes.

      Je suis pas sur que ce soit vraiment le principe, mais cela offre déjà pas mal de possibilités :
      * piquet / s'anime / image 3,
      * personnage / dans l'eau / aux genoux,
      * monstre / bouge / vers le sud.

      La table "Positions" sort un peu de se principe mais permet de gérer les interactions entre les objets :
      * monstre bouge mais sa nouvelle position correspond à un mur.
      * piquet dangereux et une caisse est dessus => caisse prend état explose 1ere image.

      • [^] # Re: Généralisation du système à entités

        Posté par  . Évalué à 0.

        En vérité c'est bien plus compliqué que cela, le problème avec le php c'est qu'il n'y a réaction du système qu'a la demande de l'utilisateur Il faudrait un système qui recalcule tous les effets, mais pour tout le monde et de façon totalement détachée des utilisateurs. Je sais pas trop comment résoudre ce problème, j'imagine que le plus simple serait un scripte qui

        De plus, il faut d'autres fonctions :
        * Initialiser les entités lorsqu'un utilisateur se connecte.
        * Sortir les entités inutiles (qui n'influence pas un autre joueur) lors de la déconnexion.
        * Sortir les entité qui sortent de la zone d'influence des joueur.
        * Gérer un fichier json ou xml pour répondre aux demandes du client (modification des images).

        Bref, c'est quand même pas si simple.
        Mais cela dit, c'est une idée qui mériterait d'être étudiée.

        • [^] # Re: Généralisation du système à entités

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

          En vérité c'est bien plus compliqué que cela, le problème avec le php c'est qu'il n'y a réaction du système qu'a la demande de l'utilisateur

          Non. Tu as plusieurs solutions pour mettre à jour ton monde :

          • un cron job (en PHP)
          • un daemon (en PHP)

          Bref, le plus simple c'est de faire un backend qui donne une portion de monde au frontend appelant, le backend assurant l'intégrité du monde, le frontend pouvant cacher les entités à proximité de l'instance.

  • # Composants

    Posté par  . Évalué à 1.

    Tout d'abord merci pour cet article qui me fait découvrir ce concept.
    J'ai quelques remarques, j'ai regardé un peu le tutoriel que tu as mis en ligne pour ta librairie libes, le premier inconvénient que je trouve c'est de tenir une énumération de tout tes composants , le second est de dérivé tout tes composants de cette structure 'struct Component' , ton composant pourrais directement être du type que tu veut avec des templates, si tu tiens à une enumération pourquoi ne pas incrémenté de manière automatique tes composants ? avec les balles , ca va, tu en as quelques un, mais avec une 50° de composants ?
    La seconde remarque que j'ai , c'est la mémoire vive consommée avec de tel système, de nos jours, j'ai l'impression que les programmeur s'en remettent souvent à la loi de moore, je prends par exemple le jeu que ma femme adore, les sims3, il à clairement été codé avec des moufles, quand je vois ce qu'il consomme par rapport à d'autre jeux au contenu équivalent, je me pose donc la question si un tel système à cause notamment de l'utilisation massive des std::map ne serait pas grand consommateur de ram ?

    • [^] # Re: Composants

      Posté par  (Mastodon) . Évalué à 2.

      Faut pas commenter une news si ancienne ! J'ai failli ne pas voir ton commentaire ;)

      J'ai quelques remarques, j'ai regardé un peu le tutoriel que tu as mis en ligne pour ta librairie libes, le premier inconvénient que je trouve c'est de tenir une énumération de tout tes composants , le second est de dérivé tout tes composants de cette structure 'struct Component' , ton composant pourrais directement être du type que tu veut avec des templates, si tu tiens à une enumération pourquoi ne pas incrémenté de manière automatique tes composants ? avec les balles , ca va, tu en as quelques un, mais avec une 50° de composants ?

      Cette énumération a deux objectifs : premièrement, elle permet d'avoir un id pour retrouver les stores qui vont bien (et là, oui, on pourrait les générer) ; deuxièmement, elle permet d'avoir un id stable dans le temps qui va permettre des sauvegardes faciles. On pourrait faire sans mais on serait obligé à un moment donné de gérer ce problème d'une autre manière moins propre je pense. En plus, dériver tous mes composants de struct Component permet de faire de la vérification de type à la compilation (merci C++11).

      La seconde remarque que j'ai , c'est la mémoire vive consommée avec de tel système, de nos jours, j'ai l'impression que les programmeur s'en remettent souvent à la loi de moore, je prends par exemple le jeu que ma femme adore, les sims3, il à clairement été codé avec des moufles, quand je vois ce qu'il consomme par rapport à d'autre jeux au contenu équivalent, je me pose donc la question si un tel système à cause notamment de l'utilisation massive des std::map ne serait pas grand consommateur de ram ?

      Pour l'instant, ça va, je n'ai pas encore de problème de sur-consommation mémoire (et si j'en avais, ça m'inquièterait). On est ici face à un compromis à trouver entre avoir une structure qui permet de faire une recherche en O(log n) ou enlever la structure mais faire des recherches en O(n). C'est un compromis temps-mémoire et effectivement, ici, j'ai plutôt choisi de sacrifier un peu de mémoire. Peut-être que je reviendrai sur cette décision plus tard. Pour l'instant, tout va bien.

Suivre le flux des commentaires

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