J'ai le code suivant, pensez shooter simple C++:Quelle est la manière la plus élégante et la plus efficace de modéliser une hiérarchie d'objets de jeu? (Conception dérange)
// world.hpp
//----------
class Enemy;
class Bullet;
class Player;
struct World
{
// has-a collision map
// has-a list of Enemies
// has-a list of Bullets
// has-a pointer to a player
};
// object.hpp
//-----------
#include "world.hpp"
struct Object
{
virtual ~Object();
virtual void Update() =0;
virtual void Render() const =0;
Float xPos, yPos, xVel, yVel, radius; // etc.
};
struct Enemy: public Object
{
virtual ~Enemy();
virtual void Update();
virtual void Render() const;
};
// Bullet and Player are similar (they render and update differently per se,
/// but the behavior exposed to World is similar)
// world.cpp
//----------
#include "object.hpp"
// object.cpp
//-----------
#include "object.hpp"
deux problèmes:
1, les objets du jeu connaissant d'autres objets de jeu.
Il doit y avoir un service qui connaît tous les objets. Il peut ou non devoir exposer TOUS les objets, il doit en exposer certains, en fonction des paramètres du demandeur (position, par exemple). Cette installation est censée être mondiale.
Les objets doivent connaître le monde dans lequel ils se trouvent pour rechercher des informations sur les collisions et d'autres objets. Cela introduit une dépendance où l'implémentation d'Objets et de World doit accéder à l'en-tête de l'objet, ainsi World n'inclura pas son propre en-tête directement plutôt qu'en incluant object.hpp (qui inclut à son tour world.hpp). Cela me met mal à l'aise - je ne pense pas que world.cpp devrait recompiler après avoir modifié object.hpp. Le monde n'a pas l'impression que cela devrait fonctionner avec Object. Cela ressemble à un mauvais design - comment peut-il être réparé? 2, Polymorphisme - peut-il et devrait-il être utilisé pour autre chose que la réutilisation de code et le regroupement logique d'entités de jeu (et comment)? Enemies, Bullets et Player mettront à jour et rendront différemment, sûrement, d'où la fonctionnalité virtuelle Update() et Render() - une interface identique. Ils sont toujours conservés dans des listes séparées et la raison en est que leur interaction dépend des listes de deux objets qui interagissent - deux ennemis se rebondissent, une balle et un ennemi se détruisent, etc.
Ceci est une fonctionnalité c'est au-delà de la mise en œuvre de Enemy and Bullet; ce n'est pas mon problème ici. Je pense qu'au-delà de leurs interfaces identiques, il y a un facteur qui sépare les Ennemis, les Puces et les Joueurs et cela pourrait et devrait être exprimé d'une autre manière, me permettant de créer une liste unique pour chacun d'entre eux.
Le problème est de savoir comment distinguer une catégorie d'objet d'une autre si elle est contenue dans la même liste. Typeid ou autre forme d'identification de type est hors de question - mais alors quelle autre manière de le faire? Ou peut-être qu'il n'y a rien de mal à l'approche?
Nous vous remercions de les deux premiers paragraphes; cela a beaucoup de sens et m'a aidé à me vider la tête. J'ai réécrit ma question pour clarifier ce que je voulais faire avec le polymorphisme. – zyndor
Le motif Visitor est génial, merci de le signaler. Je devine que les méthodes OnHit() sont analogues à la méthode Visit() du modèle et les objets seraient leurs propres visiteurs, non? – zyndor
Il a été difficile de choisir une réponse à accepter comme étant la meilleure, car tous les points étaient bons, mais aucun d'entre eux n'était vraiment complet. Je sentais que les grandes lignes de l'interaction entre l'objet et le monde étaient les plus utiles et les plus pertinentes à ma question. - – zyndor