J'espère que ces divagations clarifieront ma question - je comprendrais totalement si elles ne le font pas, alors faites-le moi savoir si c'est le cas, et j'essaierai de me clarifier.
Rencontrez BoxPong , un jeu très simple je fait pour se familiariser avec le développement de jeux orienté objet. Faites glisser la boîte pour contrôler le ballon et collecter des objets jaunes.
Faire BoxPong m'a aidé à formuler, entre autres, une question fondamentale: comment puis-je avoir des objets qui interagissent entre eux sans avoir à «appartenir» les uns aux autres? En d'autres termes, existe-t-il un moyen pour les objets de ne pas être hiérarchisés, mais plutôt de coexister? (Je vais entrer dans les détails ci-dessous.)
Je soupçonne que le problème des objets coexistant est un problème courant, alors j'espère qu'il existe un moyen établi de le résoudre. Je ne veux pas réinventer la roue carrée, donc je suppose que la réponse idéale que je recherche est "voici un modèle de conception qui est couramment utilisé pour résoudre votre type de problème."
Surtout dans des jeux simples comme BoxPong, il est clair qu'il y a, ou devrait y avoir, une poignée d'objets coexistant au même niveau. Il y a une boîte, il y a une balle, il y a un objet de collection. Tout ce que je peux exprimer dans des langages orientés objet, bien que - ou du moins il semble - ce sont des relations HAS-A strictes . Cela se fait via des variables membres. Je ne peux pas simplement commencer ball
et le laisser faire, j'ai besoin qu'il appartienne en permanence à un autre objet. Je l' ai mis en place afin que le principal objet de jeu a une boîte, et la boîte à son tour , a une balle, et a un compteur de score. Chaque objet possède également unupdate()
, qui calcule la position, la direction, etc., et j'y vais de la même manière: j'appelle la méthode de mise à jour de l'objet de jeu principal, qui appelle les méthodes de mise à jour de tous ses enfants, et ils appellent à leur tour les méthodes de mise à jour de tous leurs enfants. C'est la seule façon dont je peux voir pour créer un jeu orienté objet, mais je pense que ce n'est pas la manière idéale. Après tout, je ne penserais pas exactement à la balle comme appartenant à la boîte, mais plutôt comme étant au même niveau et interagissant avec elle. Je suppose que cela peut être réalisé en transformant tous les objets de jeu en variables membres de l'objet de jeu principal, mais je ne vois pas cela résoudre quoi que ce soit. Je veux dire ... en laissant de côté le désordre évident, comment y aurait-il un moyen pour le ballon et la boîte de se connaître , c'est-à-dire d'interagir?
Il y a aussi le problème des objets qui ont besoin de passer des informations entre eux. J'ai un peu d'expérience dans l'écriture de code pour la SNES, où vous avez accès à pratiquement toute la RAM tout le temps. Supposons que vous vous faites un ennemi personnalisé pour Super Mario World , et que vous souhaitez qu'il supprime toutes les pièces de Mario, puis stockez simplement zéro pour adresser 0DBF $, pas de problème. Il n'y a pas de limitations empêchant les ennemis d'accéder au statut du joueur. Je suppose que j'ai été gâté par cette liberté, car avec C ++ et autres, je me demande souvent comment rendre une valeur accessible à d'autres objets (ou même globaux).
En utilisant l'exemple de BoxPong, que faire si je voulais que la balle rebondisse sur les bords de l'écran? width
et height
sont des propriétés de la Game
classe,ball
d'y avoir accès. Je pourrais transmettre ces types de valeurs (soit par le biais de constructeurs ou des méthodes où elles sont nécessaires), mais cela me fait juste penser à de mauvaises pratiques.
Je suppose que mon principal problème est que j'ai besoin d'objets pour se connaître, mais la seule façon dont je peux voir cela est une hiérarchie stricte, ce qui est laid et peu pratique.
J'ai entendu parler de "classes d'amis" sur C ++ et je sais un peu comment elles fonctionnent, mais si elles sont la solution finale, alors comment se fait-il que je ne vois pas de friend
mots - clés répandus partout dans chaque projet C ++, et comment le concept n'existe pas dans toutes les langues POO? (Il en va de même pour les pointeurs de fonction, dont je viens d'apprendre récemment.)
Merci d'avance pour les réponses de toute nature - et encore une fois, s'il y a une partie qui n'a pas de sens pour vous, faites-le moi savoir.
Réponses:
En général, cela se passe très mal si des objets de même niveau se connaissent. Une fois que les objets se connaissent, ils sont liés ou couplés les uns aux autres. Cela les rend difficiles à changer, difficiles à tester, difficiles à entretenir.
Cela fonctionne beaucoup mieux s'il existe un objet "au-dessus" qui connaît les deux et peut définir les interactions entre eux. L'objet qui connaît les deux pairs peut les lier ensemble via l'injection de dépendances ou via des événements ou via le passage de messages (ou tout autre mécanisme de découplage). Oui, cela mène à une sorte de hiérarchie artificielle, mais c'est bien mieux que le désordre de spaghetti que vous obtenez lorsque les choses interagissent simplement bon gré mal gré. Cela n'est que plus important en C ++, car vous avez également besoin de quelque chose pour posséder la durée de vie des objets.
Donc, en bref, vous pouvez le faire en ayant simplement des objets côte à côte partout liés par un accès ad hoc, mais c'est une mauvaise idée. La hiérarchie fournit l'ordre et la propriété claire. La principale chose à retenir est que les objets dans le code ne sont pas nécessairement des objets de la vie réelle (ou même du jeu). Si les objets du jeu ne font pas une bonne hiérarchie, une abstraction différente peut être meilleure.
la source
Non!
Je pense que le principal problème que vous rencontrez est que vous prenez la "programmation orientée objet" un peu trop littéralement. En POO, un objet ne représente pas une "chose" mais une "idée" qui signifie qu'une "balle", un "jeu", une "physique", une "math", une "date", etc. Tous sont des objets valides. Il n'y a pas non plus d'obligation pour les objets de "savoir" quoi que ce soit. Par exemple,
Date.Now().getTommorrow()
demanderait à l'ordinateur quel jour est aujourd'hui, appliquer des règles de date floues pour déterminer la date de demain et la renvoyer à l'appelant. L'Date
objet ne sait rien d'autre, il a seulement besoin de demander les informations nécessaires au système. De plus,Math.SquareRoot(number)
n'a besoin de rien savoir en plus de la logique de calcul d'une racine carrée.Donc, dans votre exemple que j'ai cité, la "Boule" ne devrait rien savoir de la "Boîte". Les boîtes et les balles sont des idées complètement différentes et n'ont pas le droit de se parler. Mais un moteur physique sait ce qu'est une boîte et une balle (ou du moins, ThreeDShape), et il sait où ils se trouvent et ce qui devrait leur arriver. Donc, si la balle rétrécit parce qu'il fait froid, le moteur physique dirait à cette instance de balle qu'elle est plus petite maintenant.
C'est un peu comme construire une voiture. Une puce informatique ne sait rien d'un moteur de voiture, mais une voiture peut utiliser une puce informatique pour contrôler un moteur. L'idée simple d'utiliser de petites choses simples ensemble pour créer une chose légèrement plus grande et plus complexe, qui est elle-même réutilisable en tant que composant d'autres parties plus complexes.
Et dans votre exemple Mario, que se passe-t-il si vous êtes dans une salle de défi où toucher un ennemi ne vide pas les pièces de Marios, mais l'éjecte simplement de cette pièce? C'est en dehors de l'espace des idées de Mario ou de l'ennemi que Mario devrait perdre des pièces lorsqu'il touche un ennemi (en fait, si Mario a une étoile d'invulnérabilité, il tue l'ennemi à la place). Donc, quel que soit l'objet (domaine / idée) responsable de ce qui se passe lorsque mario touche un ennemi, c'est le seul qui doit être au courant de l'un ou de l'autre et devrait faire tout ce qu'il veut pour l'un ou l'autre (dans la mesure où cet objet permet des changements externes) ).
En outre, avec vos déclarations sur les objets appelant des enfants
Update()
, cela est extrêmement sujet aux bogues, comme siUpdate
on l'appelait plusieurs fois par image de différents parents? (même si vous attrapez cela, c'est du temps CPU perdu qui peut ralentir votre jeu) Tout le monde ne devrait toucher que ce dont il a besoin, quand il le faut. Si vous utilisez Update (), vous devez utiliser une forme de modèle d'abonnement pour vous assurer que toutes les mises à jour sont appelées une fois par trame (si cela n'est pas géré pour vous comme dans Unity)Apprendre à définir vos idées de domaine en blocs clairs, isolés, bien définis et faciles à utiliser sera le plus grand facteur dans la façon dont vous pouvez utiliser la POO.
la source
Vous semblez manquer le point de la programmation orientée objet.
La programmation orientée objet consiste à gérer les dépendances en inversant de manière sélective certaines dépendances clés de votre architecture afin de prévenir la rigidité, la fragilité et la non-réutilisation.
Qu'est-ce que la dépendance? La dépendance est la dépendance à autre chose. Lorsque vous stockez zéro pour adresser $ 0DBF, vous comptez sur le fait que cette adresse est l'endroit où se trouvent les pièces de Mario et que les pièces sont représentées sous forme d'entier. Votre code ennemi personnalisé dépend du code implémentant Mario et ses pièces. Si vous modifiez l'emplacement où Mario stocke ses pièces en mémoire, vous devez mettre à jour manuellement tout le code faisant référence à l'emplacement de la mémoire.
Le code orienté objet consiste à faire dépendre votre code des abstractions et non des détails. Donc au lieu de
tu écrirais
Maintenant, si vous voulez changer la façon dont Mario stocke ses pièces d'un entier à un long ou un double ou le stocker sur le réseau ou le stocker dans une base de données ou lancer un autre long processus, vous effectuez le changement en un seul endroit: le La classe Mario et tous vos autres codes continuent de fonctionner sans aucun changement.
Par conséquent, lorsque vous demandez
vous demandez vraiment:
qui n'est pas une programmation orientée objet.
Je vous suggère de commencer par tout lire ici: http://objectmentor.com/omSolutions/oops_what.html puis de rechercher tout sur YouTube par Robert Martin et de tout regarder.
Mes réponses viennent de lui et certaines d'entre elles sont directement citées de lui.
la source
mario.coins = 0;
, maismario.loseCoins();
ce qui est bien et vrai - mais mon point est, comment l'ennemi peut-il avoir accès à l'mario
objet de toute façon?mario
être une variable membre deenemy
ne me semble pas juste.Vous pouvez activer le couplage lâche en appliquant le modèle de médiateur. Son implémentation nécessite que vous ayez une référence à un composant médiateur qui connaît tous les composants récepteurs.
Il réalise le genre de pensée "maître du jeu, s'il vous plaît laissez ceci et cela arriver".
Un modèle généralisé est le modèle de publication-abonnement. Il convient que le médiateur ne contienne pas beaucoup de logique. Sinon, utilisez un médiateur fabriqué à la main qui sait où acheminer tous les appels et peut-être même les modifier.
Il existe des variantes synchrones et asynchrones généralement appelées bus d'événements, bus de messages ou file d'attente de messages. Recherchez-les pour déterminer si elles sont appropriées dans votre cas particulier.
la source