Séparation de la logique et des données dans un jeu par navigateur

8

J'y réfléchis depuis des jours et je ne sais toujours pas quoi faire. J'essaie de refactoriser un système de combat en PHP (... désolé.) Voici ce qui existe jusqu'à présent:

  • Il existe (jusqu'à présent) deux types d'entités qui peuvent participer au combat. Appelons-les simplement joueurs et PNJ. Leurs données sont déjà assez bien écrites.
  • Lorsqu'elles sont impliquées dans un combat, ces entités sont enveloppées d'un autre objet dans la base de données appelé a Combatant, qui leur donne des informations sur le combat en question. Ils peuvent être impliqués dans plusieurs combats à la fois.
  • J'essaie d'écrire le moteur logique du combat en y injectant des combattants.
  • Je veux pouvoir tout simuler pour les tests.

Afin de séparer la logique et les données, je veux avoir deux interfaces / classes de base, l'une étant ICombatantDataet l'autre ICombatantLogic. Les deux implémenteurs de données seront l'un pour les objets réels stockés dans la base de données et l'autre pour mes objets fictifs.

Je suis maintenant confronté à des incertitudes quant à la conception du côté logique des choses. Je peux avoir un implémenteur pour chacun des joueurs et des PNJ, mais j'ai un problème. Un combattant doit pouvoir retourner l'entité qu'il enveloppe. Cette méthode getter doit-elle faire partie de la logique ou des données? Je suis convaincu que cela devrait être dans les données, car la partie logique est utilisée pour exécuter le combat, et ne sera pas disponible si quelqu'un cherche simplement des informations sur un combat à venir. Mais les classes de données ne séparent que la maquette de la base de données, pas le joueur du PNJ. Si j'essaie d'avoir deux classes enfants de l'implémentateur de données DB, une pour chaque type d'entité, comment puis-je l'architecturer tout en gardant mes maquettes dans la boucle? Ai-je besoin d'une troisième interface comme IEntityProvidercelle que j'injecte dans les classes de données?

De plus, avec certaines des idées que j'ai envisagées, je pense que je vais devoir mettre en place des contrôles pour vous assurer que vous ne faites pas de différence, comme faire en sorte que la logique d'un PNJ enveloppe accidentellement les données d'un joueur. Cela a-t-il un sens? Est-ce une situation qui serait même possible si l'architecture est correcte, ou la bonne conception l'interdirait-elle complètement, donc je n'ai pas besoin de la vérifier?

Si quelqu'un pouvait m'aider à simplement mettre en page un diagramme de classe ou quelque chose pour cela, cela m'aiderait beaucoup. Merci.

Éditer

Il est également utile de noter que la classe de données fictives n'a pas vraiment besoin de la Entity, car je vais simplement spécifier directement tous les paramètres comme les statistiques de combat. Alors peut-être que cela affectera la conception correcte.

Tesserex
la source
Ma supposition n'est Combatant.entitypas utilisée pendant le combat et ne devrait donc pas exister. Peut-être avez-vous besoin d'une autre classe comme celle EntityVsEntityCombatqui encapsule la logique de combat, contient les Entity <--> Combatantmappages et met à jour les Entityétats une fois le combat terminé? Peut-être que quelques informations supplémentaires sur votre architecture actuelle pourraient vous aider.
Torious

Réponses:

1

Une partie de la façon dont j'ai abordé cela dans le passé est, plutôt que d'avoir des représentations complètement séparées pour les joueurs et les PNJ, tout en exigeant qu'ils mettent en œuvre une interface commune, conduisant à la convergence de la représentation entre eux dans la plus grande mesure possible, comme en les sous-classant à partir d'un Charactermodèle commun dans lequel je pousse autant à leur sujet qu'il est logique de généraliser. Cela permet d'éviter les problèmes liés à l'exécution d'opérations NPC sur des joueurs et autres en rendant les opérations plus généralement applicables, car les représentations ont moins tendance à diverger que si elles sont des implémentations complètement indépendantes. L'optimisation de base du polymorphisme aide à gérer les cas qui doivent diverger (par exemple, si vous avez fait votreCombatantLogicresponsable de gérer ce qui se passe quand quelqu'un décède, vous devrez faire une vérification typographique pour vous assurer que vous avez utilisé la bonne logique; alors ne le faites pas, demandez aux joueurs et aux PNJ de mettre en œuvre des die()méthodes distinctes et appropriées ).

J'accepte que vous Entityfassiez partie des données. Cependant, sur la base de ce que je disais, j'aurais tendance à supprimer ou à limiter le rôle de votre CombatantDataen faveur d'une logique de combat tirant directement les valeurs de Entity, peut-être en CombatantDatane stockant que des valeurs calculées coûteuses spécifiques au combat. Vos maquettes de test seraient alors davantage orientées vers la fourniture de faux Entitymodèles que vers le remplissage CombatantData. (Le fait de CombatantDatadupliquer beaucoup d'informations Entityme dérange de la même manière qu'une base de données dénormalisée. Cependant, si vous croyez en la loi de Déméter, ce que je ne crois pas passionnément, vous ne voudrez pas faire les choses comme je Bien sûr, si vous croyez en la loi de Déméter, je ne suis pas sûr que votreCombatantDatadevrait même donner accès au Entity.)

le chaos
la source
CombatantData ne duplique pas vraiment les données d'entité, il inclut l'état de l'entité au combat, comme la santé actuelle et tout effet de statut sur celle-ci.
Tesserex
@Tesserex: Ah, d'accord. Si cet état ne persiste pas entre les combats, c'est raisonnable. Ignorez cette partie, alors. :) Est-ce que le reste de ce que je dis est logique?
chaos