Lorsque vous créez des jeux, vous créez souvent l'objet de jeu suivant dont toutes les entités héritent:
public class GameObject{
abstract void Update(...);
abstract void Draw(...);
}
Donc, dans la boucle de mise à jour, vous itérez sur tous les objets du jeu et leur donnez une chance de changer d'état, puis dans la boucle de dessin suivante, vous itérez à nouveau sur tous les objets du jeu et leur donnez une chance de se dessiner.
Bien que cela fonctionne assez bien dans un jeu simple avec un simple rendu vers l'avant, cela conduit souvent à quelques objets de jeu gigantesques qui doivent stocker leurs modèles, à plusieurs textures et, pire encore, à une méthode de tirage au sort qui crée un couplage étroit entre l'objet de jeu, la stratégie de rendu actuelle et toutes les classes liées au rendu.
Si je devais changer la stratégie de rendu d'avant en différé, je devrais mettre à jour beaucoup d'objets de jeu. Et les objets de jeu que je fabrique ne sont pas aussi réutilisables qu'ils pourraient l'être. Bien sûr, l'héritage et / ou la composition peuvent m'aider à lutter contre la duplication de code et faciliter un peu la modification de l'implémentation, mais cela fait toujours défaut.
Une meilleure façon, peut-être, serait de supprimer complètement la méthode Draw de la classe GameObject et de créer une classe Renderer. Le GameObject devrait toujours contenir des données sur ses visuels, comme le modèle avec lequel le représenter et les textures à peindre sur le modèle, mais la manière de procéder serait laissée au rendu. Cependant, il y a souvent beaucoup de cas de frontière dans le rendu, même si cela supprimerait le couplage étroit du GameObject au Renderer, le Renderer devrait toujours être au courant de tous les objets du jeu qui le rendraient gras, tout en sachant et couplage étroit. Cela violerait pas mal de bonnes pratiques. Peut-être que la conception orientée données pourrait faire l'affaire. Les objets de jeu seraient certainement des données, mais comment le moteur de rendu serait-il entraîné par cela? Je ne suis pas sûr.
Je suis donc perdu et je ne vois pas de bonne solution. J'ai essayé d'utiliser les principes de MVC et dans le passé, j'avais quelques idées sur la façon de l'utiliser dans les jeux, mais récemment, cela ne semble pas aussi applicable que je le pensais. J'aimerais savoir comment vous abordez tous ce problème.
Quoi qu'il en soit, récapitulons, je suis intéressé par la façon dont les objectifs de conception suivants peuvent être atteints.
- Aucune logique de rendu dans l'objet de jeu
- Couplage lâche entre les objets du jeu et le moteur de rendu
- Aucun rendu qui sait tout
- De préférence, commutation d'exécution entre les moteurs de rendu
La configuration de projet idéale serait une «logique de jeu» distincte et un projet de logique de rendu qui n'ont pas besoin de se référencer.
Ce train de pensée a commencé lorsque j'ai entendu John Carmack dire sur Twitter qu'il avait un système si flexible qu'il pouvait échanger des moteurs de rendu au moment de l'exécution et même dire à son système d'utiliser les deux moteurs de rendu (un moteur de rendu logiciel et un moteur de rendu accéléré par le matériel) en même temps afin qu'il puisse inspecter les différences. Les systèmes que j'ai programmés jusqu'à présent ne sont même pas aussi flexibles
la source
Ce que j'ai fait pour mon propre moteur, c'est de tout regrouper en modules. J'ai donc ma
GameObject
classe et elle contient une poignée pour:J'ai donc une
Player
classe et uneBullet
classe. Les deux dériventGameObject
et sont ajoutés auScene
. MaisPlayer
a les modules suivants:Et
Bullet
a ces modules:Cette façon d'organiser les choses évite le "Diamant de la mort" où vous avez un
Vehicle
, unVehicleLand
et unVehicleWater
et maintenant vous voulez unVehicleAmphibious
. Au lieu de cela, vous avez unVehicle
et il peut avoir unModuleWater
et unModuleLand
.Bonus supplémentaire: vous pouvez créer des objets en utilisant un ensemble de propriétés. Tout ce que vous devez savoir est le type de base (Player, Enemy, Bullet, etc.), puis créez des poignées pour les modules dont vous avez besoin pour ce type.
Dans ma scène, je fais ce qui suit:
Update
pour toutes lesGameObject
poignées.ModuleCollision
poignée.UpdatePost
pour toutes lesGameObject
poignées pour faire connaître leur position finale après la physique.m_ObjectsCreated
liste à lam_Objects
liste.Et je pourrais l'organiser davantage: par modules plutôt que par objet. Ensuite, je rendais une liste de
ModuleSprite
, mettais à jour un tas deModuleScriptingBase
et faisais des collisions avec une liste deModuleCollision
.la source
GameObject
(par exemple un moyen de rendre un "serpent" de Sprites), vous devrez soit créer un enfantModuleSprite
pour cette fonctionnalité spécifique (ModuleSpriteSnake
), soit ajouter un nouveau module (ModuleSnake
). Heureusement, ce ne sont que des pointeurs, mais j'ai vu du code oùGameObject
faisait littéralement tout ce qu'un objet pouvait faire.