2010-04-22 4 views
3

Je suis en train de mettre en œuvre une sorte de moteur de jeu « juste pour-moi » et l'intrigue du problème va de la façon suivante:C++ la conception du jeu et polymorphisme question

Supposons que j'ai une interface abstraite pour une entité renderable , par exemple IRenderable.

Et il est déclaré de la manière suivante:

interface IRenderable { 
    // (...) 
    // Suppose that Backend is some abstract backend used 
    // for rendering, and it's implementation is not important 
    virtual void Render(Backend& backend) = 0; 
}; 

Ce que je fais est en ce moment quelque chose comme déclarant différentes classes comme

class Ball : public IRenderable { 

    virtual void Render(Backend& backend) { 
    // Rendering implementation, that is specific for 
    // the Ball object 
    // (...) 
    } 
}; 

Et puis tout semble bien. Je peux facilement faire quelque chose comme std::vector<IRenderable*> items, pousser certains articles comme new Ball() dans ce vecteur et ensuite faire une similaire d'appel à foreach (IRenderable* in items) { item->Render(backend); }

Ok, je suppose que c'est la façon « polymorphes », mais si je veux avoir différents types de objets dans mon jeu et une capacité à manipuler leur état, où chaque objet peut être manipulé via sa propre interface?

que je pouvais faire quelque chose comme

struct GameState { 
    Ball ball; 
    Bonus bonus; 
    // (...) 
}; 

puis changer facilement des objets état via leurs propres méthodes, comme ball.Move(...) ou bonus.Activate(...), où Move(...) est spécifique pour seulement Ball et Activate(...) - pour seulement Bonus cas.

Mais dans ce cas, je perds l'opportunité d'écrire foreach IRenderable* simplement parce que je stocke ces boules et bonus comme des instances de leurs classes dérivées, pas de base. Et dans ce cas, la procédure de rendu se transforme en un désordre comme

ball.Render(backend); 
bonus.Render(backend); 
// (...) 

et il est mauvais parce que nous perdons en fait notre polymorphisme cette façon (pas de besoin réel pour la fabrication virtuelle, etc.

fonction Render L'autre approche signifie l'invocation par dynamic_cast ou coulée en descente quelque chose avec typeid pour déterminer le type d'objet que vous souhaitez manipuler et cela semble encore pire pour moi et cela brise aussi ce « polymorphes » idée.

Alors, ma question est - Y a-t-il une sorte d'approche (probablement) alternative à ce que je veux faire ou mon pattern actuel peut-il être modifié de manière à stocker IRenderable* pour mes objets de jeu (pour que je puisse invoquer la méthode virtuelle Render sur chacun d'entre eux) tout en préservant la possibilité de changer facilement l'état de ces objets?

Peut-être que je fais quelque chose de tout à fait tort dès le début, le cas échéant, s'il vous plaît le signaler :)

Merci à l'avance!

Répondre

1

Je ne suis pas un programmeur C++, mais, espérons-faux code vous aidera ...

classe Renderable a la fonction Render

classe mobile hérite de Renderable et possède également des propriétés telles que x, y, z et fonctionne comme classe Déplacer

hérite de balle mobile

maintenant, votre état de jeu peut avoir une liste d'objets et affichables, lorsque vient le temps de rendu, il peut juste boucle à travers eux.

D'autres actions, comme une coche de l'horloge mondiale, pourraient faire une boucle dans la recherche d'objets qui sont des instances de Moveable et leur demander de se mettre à jour.

Si votre bonus est rendu (je ne peux pas dire avec certitude à partir de votre question), il pourrait sous-classe Collectable qui sous-classe Moveable.

+0

Est-ce que quelqu'un connaît une méthode C++ efficace pour implémenter la deuxième boucle à travers ces éléments Renderable qui a pour résultat de ne trouver que le sous-ensemble des objets Movable? Soit je suis tout simplement stupide ou cela peut être fait seulement en invoquant la coulée dynamique/typeid/votre propre implémentation RTTI ... Merci pour la réponse :) –

+1

@Kotti suffit de créer une autre liste d'entre eux. Par exemple. vous avez une liste de Renderables, une liste de Movables, une liste de Collectibles. Ensuite, chaque fois que vous voulez opérer sur un sous-ensemble, il suffit de parcourir la liste appropriée. (Encore une fois, ajoutez les Renderables à la liste de rendu dans le constructeur Renderable.) –

3

Le fait que vous stockiez les différents objets en tant que membres de la classe ne devrait pas vous empêcher de stocker des pointeurs dans le vecteur IRenderable*.

struct GameState { 
    Ball ball; 
    Bonus bonus; 
    vector<IRenderable*> m_items; 

    GameState() // constructor 
    { 
    m_items.push_back(&ball); 
    m_items.push_back(&bonus); 
    } 

}; 

De cette façon, vous aussi ne pas avoir à se soucier de libérer les pointeurs dans m_items. Ils seront automatiquement détruits lorsque le GameState est détruit.

En outre, votre syntaxe de foreach est funky.

+0

Ça a l'air bien, merci. Foreach de la même manière que cela peut être fait par exemple en utilisant BOOST_FOREACH. –

2

Vous devriez vraiment avoir des sous-systèmes distincts dans votre moteur de jeu. Un pour le rendu et un autre pour la logique du jeu. Votre sous-système graphique doit avoir une liste de pointeurs vers des objets IRenderable. Votre sous-système de logique de jeu doit avoir sa propre liste d'objets avec une autre interface.

Maintenant, vous pouvez facilement avoir des entités qui ont un état mais qui ne peuvent pas être rendues et ainsi de suite. Vous pourriez même avoir un sous-système distinct pour l'audio. Cela devrait également aider à garder votre code propre et modulaire.

+0

Peut-être, mais je pense que cela peut généralement apporter beaucoup de maux de tête, en particulier la synchronisation IRenderable * pointeur après l'ajout/suppression/... d'objets de jeu et ainsi de suite. Je pourrais me tromper à ce sujet, je devrais probablement essayer. –

+0

Utilisez RAII (http://en.wikipedia.org/wiki/RAII). Enregistrez l'objet dans le constructeur et supprimez-le le destructeur. Cela garantira que l'objet sera enregistré et désenregistré correctement. – bitc

+0

btw, les objets IRenderable visibles seront mis à jour chaque fois que l'écran est actualisé. La logique du jeu doit être indépendante des images par seconde et avoir son propre taux de rafraîchissement. C'est une autre raison de séparer ces responsabilités. – bitc