Wie gestalte ich die Angriffsklasse in einem RPG-Spiel?

37

Ich bin in der Planungsphase eines kleinen Rollenspiels.

Der Charakter hat eine Reihe von Attributen wie Stärke, Beweglichkeit usw., die als ganze Zahlen dargestellt werden. Der Charakter wird auch eine Reihe von Angriffen haben, die als Angriffsklasse dargestellt werden.

Bei jedem Angriff möchte ich, dass er basierend auf den Charakterattributen Schaden verursacht, zB: Der Angriff "Schwerthieb" verursacht 10 dmg + den Wert der Charakterstärke.

Die Art und Weise, wie ich mir das vorgestellt habe, ist eine abstrakte Angriffsklasse mit einer abstrakten Attack-Methode. Für jeden Angriff erstelle ich eine Klasse, die die Attack-Methode implementiert.

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

Ich sehe, dass es ein Albtraum sein wird, dies aufrechtzuerhalten.

Hat jemand eine Idee, wie ich das auf eine schönere Weise erreichen kann?

Was ich denke, ist das Hauptproblem, wie man das korrekte Attribut eingibt, das auf dem Angriff basiert.

eflles
quelle

Antworten:

34

Sie sollten sich hier wahrscheinlich für ein datengesteuertes Design entscheiden.

Erstellen Sie eine allgemeine Attack-Klasse, die die Parameter enthält, mit denen Sie arbeiten möchten - Grundschaden, der den Schaden beeinflusst, eine Reihe möglicher Statuseffekte ... solche Dinge:

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.
  }
}

Platzieren Sie dann Ihre Angriffe in einer Datei, z. B. einer XML-Datei, und laden Sie die Daten von dort:

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

Sie können dies sogar erweitern, um Werte aus mehreren Statistiken zu ziehen, beispielsweise einem Feuerball, bei dem der Schaden sowohl aus einem Intelligenz- als auch einem Feuerstatus berechnet wird:

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

Wenn Sie nicht für alles die gleiche Grundschadensformel verwenden möchten (z. B. magischen Schaden anders berechnen als physischen Schaden), erstellen Sie für jede benötigte Formel eine Unterklasse von Attack und überschreiben Sie Attack, und geben Sie an, welchen Typ Sie in Ihrer XML-Datei haben möchten.

Michael Madsen
quelle
1
+1, aber ich würde GetStatValue sogar durch eine Nachschlagetabelle ersetzen, um zu vermeiden, dass diese switch-Anweisung beibehalten wird.
1
Das Problem bei dieser Methode ist, dass Sie nur generische datengesteuerte Angriffe ausführen können - Sie können nichts haben, das spezielle Logik verwendet. Sie werden mit einem sehr allgemeinen Set von Gegenständen enden (wie Sie in Warcraft bekommen
Iain
2
@Iain: Das lässt sich sehr einfach lösen, indem einfach weitere Daten hinzugefügt werden, um dies zu ermöglichen. Möglicherweise haben Sie eine SpecialAttack-Unterklasse, die mehr erledigt oder den Schaden auf eine ganz andere Weise berechnet. Es geht nur darum, das von Ihnen benötigte Verhalten zu identifizieren und es dann als Daten auszudrücken.
Michael Madsen
2
@Iain: Zusätzlich zum Hinzufügen weiterer Felder können Sie dies auch dadurch lösen, dass einige der Datenfelder Ausdrücke oder Codeblöcke in z. B. Lua sind. Eine gute Verwendung von orthogonalen Komponenten führt auch zu interessanteren Ergebnissen.
1
+1 für die allgemeine Vorstellung, datengesteuert zu sein. Ich bin nicht damit einverstanden, XML vorzuschlagen. Es gibt bessere Formate - yaml, json oder eine einfache .lua-Datei, wenn Sie Lua einbetten.
Egarcia
2

Ich hätte eine Waffenklasse, die eine Angriffsmethode hat, die Sie mit dem gewünschten Verhalten überschreiben. Sie können dann auch festlegen, wie die Waffe im Spiel aussieht, wie viel sie im Inventar verkauft usw. in derselben Klasse.

Iain
quelle
6
-1, dies ist nicht nur nicht datengetrieben, sondern vielmehr eine tiefe Hierarchie als komponentengetrieben. Es ist die schlechteste mögliche Lösung.
4
Nur weil diese Methode nicht datengesteuert ist, ist sie keine schlechte Wahl, und die Hierarchie wäre sowieso nicht so tief. Es ist einfach, aber dennoch leistungsfähig (die UnrealEngine ist ein perfektes Beispiel dafür), wenn es richtig gemacht wird (dh keine fest codierten Werte). Sicher, es hat seine Schattenseiten, aber weiter unten im Entwicklungszyklus eines datengetriebenen Systems sind die Schattenseiten offensichtlich. Ich denke, Ihr grundlegendes OOP-Design ist immer noch eine gültige Lösung, und wenn er die Standardwerte im Handumdrehen bearbeiten möchte, kann es genauso einfach auf einem Hierarchiesystem implementiert werden.
Dalin Seivewright
6
Nicht alles muss datengesteuert sein - es kommt auf den Umfang des Spiels an. Es ist wahrscheinlich ein bisschen arrogant zu denken, dass meine Antwort "falsch" ist, aber ich danke Ihnen für Ihre Ehrlichkeit. Ich denke, das ist nur ein Stilkonflikt zwischen dem, was für mich funktioniert, dem, was jeden Tag Flash-Spiele macht, und Ihrem traditionelleren Entwicklungsmodell. Ich kann Ihnen sagen, dass mein Ansatz viel schneller zu implementieren ist und Sie eine bessere Überprüfung der Kompilierungszeit haben. Ihr Kommentar zu. Lua geht davon aus, dass der Fragesteller an einer Plattform arbeitet, die dies unterstützen würde.
Iain
2
Als RPG-Spiel ist es wahrscheinlich ziemlich unpraktisch, jeden Punkt so zu implementieren.
Vaughan Hilts
1

Ich bin wirklich neu in diesem Bereich, aber die Art und Weise, wie ich es tun würde, ist, eine allgemeine Angriffsklasse zu erstellen.

Wenn eine Charakterinstanz eine andere Charakterinstanz angreifen möchte, wird eine Instanz der Angriffsklasse erstellt, die mit den erforderlichen Daten und der ID des Charakters gefüllt ist, der sie erstellt hat. Anpassungen von der Ausrüstung würden dann auf das Angriffsobjekt angewendet, wobei Daten verwendet würden, die in ein XML-Dokument oder ähnliches eingegeben werden könnten.

Diese Klasseninstanz würde dann in eine andere Klasse eingeschlossen, um der Umgebung Hooks zur Bestimmung des Bereichs oder dergleichen bereitzustellen. Wenn der Angriff gültig ist, wird die Angriffsinstanz an den angegriffenen Charakter übergeben, der die Effekte anwendet.

Ich hoffe, das ergab einen Sinn.

Dave
quelle