Le concept
Fondamentalement, un graphe de scène n'est rien de plus qu'un graphe acyclique bidirectionnel qui sert à représenter un ensemble hiérarchiquement structuré de relations spatiales.
Les moteurs à l'état sauvage ont tendance à inclure d'autres goodies dans le graphique de la scène, comme indiqué. Que vous voyiez cela comme la viande ou la vache dépend probablement de votre expérience avec les moteurs et les bibliothèques.
Le garder léger
Je préfère le style Unity3D d'avoir votre nœud de graphe de scène (qui en son cœur est une structure topologique plutôt qu'une structure spatiale / topographique) inclut intrinsèquement des paramètres spatiaux et des fonctionnalités. Dans mon moteur, mes nœuds sont encore plus légers que Unity3D, où ils héritent de nombreux membres indésirables inutiles des superclasses / interfaces implémentées: voici ce que j'ai - à peu près aussi léger que possible:
- membres du pointeur parent / enfant.
- membres de paramètres spatiaux pré-transformés: position xyz, tangage, lacet et roulis.
- une matrice de transformation; les matrices d'une chaîne hiérarchique peuvent se multiplier très rapidement et facilement en marchant récursivement vers le haut / bas dans l'arbre, vous donnant les transformations spatiales hiérarchiques qui sont la caractéristique principale d'un graphique de scène;
- une
updateLocal()
méthode qui met à jour uniquement les matrices de transformation de ce nœud
- une
updateAll()
méthode qui met à jour cela et toutes les matrices de transformation des nœuds descendants
... J'inclus également des logiques d'équations de mouvement et donc des éléments de vitesse / accélération (linéaires et angulaires) dans ma classe de nœuds. Vous pouvez y renoncer et le gérer dans votre contrôleur principal à la place si vous le souhaitez. Mais c'est tout - très léger en effet. N'oubliez pas que vous pourriez les avoir sur des milliers d'entités. Donc, comme vous l'avez suggéré, gardez-le léger.
Construire des hiérarchies
Que dites-vous d'un graphe de scène référençant d'autres graphes de scène ... J'attends la punchline? Bien sûr qu'ils le font. C'est leur principale utilisation. Vous pouvez ajouter n'importe quel nœud à n'importe quel autre nœud et les transformations devraient se produire automatiquement dans l'espace local de la nouvelle transformation. Tout ce que vous faites, c'est changer un pointeur, ce n'est pas comme si vous copiez des données! En changeant un pointeur, vous avez alors un graphique de scène plus profond. Si l'utilisation de procurations rend les choses plus efficaces que jamais, mais je n'en ai jamais vu le besoin.
Évitez la logique liée au rendu
Oubliez le rendu lorsque vous écrivez votre classe de nœuds de graphe de scène, ou vous confondrez les choses par vous-même. Tout ce qui compte, c'est que vous ayez un modèle de données - que ce soit le graphe de la scène ou non - et que certains moteurs de rendu vont inspecter ce modèle de données et rendre les objets dans le monde en conséquence, que ce soit en 1, 2 , 3 ou 7 dimensions. Le point que je fais est le suivant: ne contaminez pas votre graphique de scène avec une logique de rendu. Un graphique de scène concerne la topologie et la topographie - c'est-à-dire la connectivité et les caractéristiques spatiales. Ce sont le véritable état de la simulation et existent même en l'absence de rendu (qui peut prendre n'importe quelle forme sous le soleil d'une vue à la première personne à un graphique statistique à une description textuelle). Les nœuds ne pointent pas vers des objets liés au rendu, mais l'inverse peut bien être vrai. Considérez également ceci: Tous les nœuds de graphe de scène de votre arborescence ne seront pas tous rendus. Beaucoup ne seront que des conteneurs. Alors pourquoi même allouer de la mémoire à un objet pointeur vers rendu? Même un membre de pointeur qui n'est jamais utilisé prend encore de la mémoire. Inversez donc la direction du pointeur: l'instance liée au rendu fait référence au modèle de données (qui peut être ou inclure votre nœud de graphe de scène), PAS vice versa. Et si vous voulez un moyen simple de parcourir votre liste de contrôleurs tout en ayant accès à la vue associée, utilisez un dictionnaire / table de hachage, qui approche le temps d'accès en lecture O (1). De cette façon, il n'y a pas de contamination, et votre logique de simulation ne se soucie pas des rendus en place, ce qui rend vos jours et vos nuits de codage Alors pourquoi même allouer de la mémoire à un objet pointeur vers rendu? Même un membre de pointeur qui n'est jamais utilisé prend encore de la mémoire. Inversez donc la direction du pointeur: l'instance liée au rendu fait référence au modèle de données (qui peut être ou inclure votre nœud de graphe de scène), PAS vice versa. Et si vous voulez un moyen simple de parcourir votre liste de contrôleurs tout en ayant accès à la vue associée, utilisez un dictionnaire / table de hachage, qui approche le temps d'accès en lecture O (1). De cette façon, il n'y a pas de contamination, et votre logique de simulation ne se soucie pas des rendus en place, ce qui rend vos jours et vos nuits de codage Alors pourquoi même allouer de la mémoire à un objet pointeur vers rendu? Même un membre de pointeur qui n'est jamais utilisé prend encore de la mémoire. Inversez donc la direction du pointeur: l'instance liée au rendu fait référence au modèle de données (qui peut être ou inclure votre nœud de graphe de scène), PAS vice versa. Et si vous voulez un moyen simple de parcourir votre liste de contrôleurs tout en ayant accès à la vue associée, utilisez un dictionnaire / table de hachage, qui approche le temps d'accès en lecture O (1). De cette façon, il n'y a pas de contamination, et votre logique de simulation ne se soucie pas des rendus en place, ce qui rend vos jours et vos nuits de codage Et si vous voulez un moyen simple de parcourir votre liste de contrôleurs tout en ayant accès à la vue associée, utilisez un dictionnaire / table de hachage, qui approche le temps d'accès en lecture O (1). De cette façon, il n'y a pas de contamination, et votre logique de simulation ne se soucie pas des rendus en place, ce qui rend vos jours et vos nuits de codage Et si vous voulez un moyen simple de parcourir votre liste de contrôleurs tout en ayant accès à la vue associée, utilisez un dictionnaire / table de hachage, qui approche le temps d'accès en lecture O (1). De cette façon, il n'y a pas de contamination, et votre logique de simulation ne se soucie pas des rendus en place, ce qui rend vos jours et vos nuits de codagemondes plus facile.
En ce qui concerne l'abattage, reportez-vous à ce qui précède. L'abattage des zones d'intérêt est un concept logique de simulation. Autrement dit, vous ne traitez pas le monde en dehors de cette zone (généralement encadrée, circulaire ou sphérique). Cela a lieu dans la boucle principale du contrôleur / jeu, avant le rendu. En revanche, l'abattage tronconique est purement lié au rendu. Alors oubliez l'abattage maintenant. Cela n'a rien à voir avec les graphiques de scène, et en vous concentrant dessus, vous masquerez le véritable objectif de ce que vous essayez d'atteindre.
Une note finale ...
J'ai l'impression que vous venez d'un arrière-plan Flash (en particulier AS3), étant donné tous les détails sur le rendu inclus ici. Oui, le paradigme Flash Stage / DisplayObject inclut toute la logique de rendu dans le cadre du scénario. Mais Flash fait beaucoup d'hypothèses que vous ne voulez pas nécessairement faire. Pour un moteur de jeu à part entière, il est préférable de ne pas mélanger les deux, pour des raisons de performances, de commodité et de contrôle de la complexité du code via un SoC approprié .
Renderable
s ( ce qui est une interface ou classe abstraite) en interne pour les principaux objets modèle contrôleur. Les entités ou les éléments d'interface utilisateur en sont de bons exemples. Ainsi, vous pouvez accéder rapidement uniquement aux moteurs de rendu pertinents pour cet objet principal particulier - sans les détails d'implémentation qui contamineraient la classe d'entité, d'où l'utilisation d'interfaces.Renderer
.