Comment concevoir la classe d'attaque dans un jeu de rôle?

37

Je suis dans la phase de planification d'un petit jeu de style RPG.

Le personnage aura un ensemble d'attributs, tels que la force, l'agilité, etc. qui sont représentés sous forme d'entiers. Le personnage aura également un ensemble d'attaques représentées en tant que classe d'attaque.

À chaque attaque, je veux que les dégâts soient basés sur les attributs des personnages, par exemple: l'attaque "slash slash" fera 10 dmg + la valeur de la force des personnages.

Pour ce faire, je pensais avoir une classe d'attaque abstraite, dotée d'une méthode d'attaque abstraite, et pour chaque attaque, je crée une classe qui implémente la méthode Attack.

public class SwordSlash:Attack
{
    public void Attack(Character attacker, Character defender)
    {
        defender.DoDamage(10 + attacker.Strength);
    }
}

Je vois que cela en fera un cauchemar à entretenir.

Quelqu'un a-t-il une idée de la façon dont je peux accomplir cela plus agréablement?

Je pense que le problème principal est de savoir comment entrer le bon attribut, en fonction de l'attaque.

eflles
la source

Réponses:

34

Vous devriez probablement opter pour une conception basée sur les données ici.

Créez une classe d'attaque générique contenant les paramètres avec lesquels vous souhaitez travailler - dégâts de base, quelles statistiques affectent les dégâts, ensemble d'effets d'état potentiels ...

public enum AttackStat
{
  Strength,
  Agility,
  Intellect
  // etc.
}

public class Attack
{    
  private int baseDamage;
  private AttackStat stat;
  private double damageMultiplier;
  // ...and so on

  public void Attack(Character attacker, Character defender)
  {
    defender.DoDamage(baseDamage + attacker.GetStatValue(stat) * damageMultiplier);
  }    
}

// Put a method on Character to fetch the appropriate value given an AttackStat:
public int GetStatValue(AttackStat s)
{
  switch(s)
  {
    case AttackStat.Strength:
      return strength;
    case AttackStat.Agility:
      return agility;
    // etc.
  }
}

Ensuite, placez vos attaques dans un fichier, par exemple un fichier XML, et chargez les données à partir de celui-ci:

<Attacks>
  <Attack name="Sword Slash" damage="10" stat="Strength" multiplier="1" />
  <!-- More attacks here -->
</Attacks>

Vous pouvez même étendre cela pour tirer des valeurs de plusieurs statistiques, par exemple une boule de feu où les dégâts sont calculés à la fois par un intellect et une statistique de feu:

<Attack name="Fireball" damage="20">
  <StatModifier stat="Intellect" multiplier="0.4" />
  <StatModifier stat="Fire" multiplier="0.8" />
</Attack>

Si vous ne souhaitez pas utiliser la même formule de dommages de base pour tout (par exemple, calculer les dommages magiques différemment des dommages physiques), créez des sous-classes d'attaque pour chaque formule dont vous avez besoin, remplacez Attack et spécifiez le type souhaité dans votre fichier XML.

Michael Madsen
la source
1
+1, mais je voudrais même remplacer GetStatValue par une table de correspondance pour éviter de conserver cette instruction switch.
1
Le problème avec cette méthode est que vous ne pouvez avoir que des attaques génériques basées sur les données - vous ne pouvez rien avoir qui utilise une logique spéciale. Vous allez vous retrouver avec un ensemble d’objets très génériques (comme dans Warcraft)
Iain
2
@Iain: Cela est très facilement résolu en ajoutant simplement plus de données pour permettre cela. Par exemple, vous pourriez avoir une sous-classe SpecialAttack qui fait plus de choses ou calcule les dégâts d'une manière totalement différente. Il suffit d'identifier le comportement dont vous avez besoin, puis de l'exprimer sous forme de données.
Michael Madsen
2
@Iain: En plus de simplement ajouter plus de champs, vous pouvez également résoudre le problème en indiquant que certains champs de données sont des expressions ou des blocs de code, par exemple en Lua. Une bonne utilisation des composants orthogonaux donne également des résultats plus intéressants.
1
+1 pour l'idée générale d'être piloté par les données. Je ne suis pas d'accord avec suggérer xml. Il existe de meilleurs formats - yaml, json ou un simple fichier .lua si vous intégrez Lua.
egarcia
2

J'aurais une classe d'arme qui a une méthode d'attaque que vous substituez au comportement que vous souhaitez. Vous pouvez également gérer l'apparence de l'arme dans le jeu, l'inventaire, le prix des ventes, etc. dans la même classe.

Iain
la source
6
-1, non seulement ce n'est pas basé sur les données, mais sur la hiérarchie profonde plutôt que sur les composants. C'est la pire solution possible.
4
Ce n'est pas parce que cette méthode n'est pas pilotée par les données que le choix est mauvais et que la hiérarchie ne serait de toute façon pas aussi profonde. Il est simple mais reste puissant (UnrealEngine en est un parfait exemple) s'il est effectué correctement (c'est-à-dire sans valeur codée en dur). Bien sûr, il a ses inconvénients, mais plus loin dans le cycle de développement d'un système basé sur les données, je suis convaincu que ses inconvénients sont visibles. Je pense que votre conception de base de la programmation orientée objet est toujours une solution valable dans ce domaine. S'il souhaite modifier à la volée les valeurs par défaut, il peut tout aussi facilement être implémenté au-dessus d'un système hiérarchique.
Dalin Seivewright
6
Tout ne doit pas nécessairement être piloté par les données - cela dépend de la taille du jeu. C'est probablement un peu arrogant de penser que ma réponse est "fausse", mais merci de votre honnêteté. Je pense que ceci est juste un choc de styles entre ce qui marche pour moi, créer des jeux Flash tous les jours et votre modèle de développement plus traditionnel. Je peux vous dire que mon approche est beaucoup plus rapide à mettre en œuvre et que vous avez une meilleure vérification au moment de la compilation. Votre commentaire re. Lua suppose que le demandeur travaille sur une plate-forme compatible.
Iain
2
Étant un jeu de RPG, il est probablement peu pratique de mettre en œuvre tous les éléments de ce type.
Vaughan Hilts
1

Je suis vraiment nouveau dans ce domaine, mais je le ferais en créant une classe d'attaque générique.

Lorsqu'une instance de personnage veut attaquer une autre instance de personnage, elle crée une instance de la classe d'attaque, contenant les données requises et l'ID du personnage qui l'a créée. Les ajustements apportés par l'engrenage seraient ensuite appliqués à l'objet d'attaque, à l'aide de données pouvant être entrées dans un document XML ou similaire.

Cette instance de classe serait ensuite encapsulée dans une autre classe, afin de fournir des points d'ancrage à l'environnement afin de déterminer la plage ou similaire. Si l'attaque est valide, l'instance d'attaque serait transmise au personnage attaqué, qui appliquerait les effets.

J'espère que cela avait du sens.

Dave
la source