Datenstruktur für Einheiten-Effekte

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
mtorc1
Beiträge: 80
Registriert: 20.02.2021, 16:24

Datenstruktur für Einheiten-Effekte

Beitrag von mtorc1 »

Hallo,

ich arbeite derzeit an einem kleinen 2D-MOBA für den Browser (HTML5 + TypeScript). Wie für das Genre üblich, sollen auf Einheiten alle möglichen Effekte wirken können. Damit meine ich einerseits simple Statusänderungen wie "+5 Schaden", "+20 Rüstung" etc. aber auch Effekte wie "Einheit ist festgefroren", "Einheit verursacht +25% Schaden, wenn sie einen Gegner von hinten trifft", "bei Treffer werden x% Schaden zurückgeworfen".

Ich überlege mir aktuell, wie ich die am Besten implementieren soll. Die naheliegendste Überlegung ist, dass jede Einheit eine Liste an Effekten hat, die jeweils auf entsprechende Events warten. Events haben eine entsprechende Payload, die durch den Effekt als Event Listener manipuliert werden kann. Für Effekte, die sich klar auf eine Einheit beziehen ist das auch so weit so gut. Beispiel: 50% "Dornenschaden"-Effekt:

Code: Alles auswählen

class ThrowbackDamageEffect extends BaseEffect {
  // ...

  public constructor(...) {
    // ...
    // Register callback "this.onDamage" for events of type "EventTypes.damage" sent to entity "this.entityId".
    this.listenerId = manager.addListener(EventTypes.damage, this.entityId, this.onDamage);
  }

  public onDamage(dmgInfo: DamageEvent): void {
    if(dmgInfo.causedByEffect) return;
    manager.broadcast({
      type: EventTypes.damage,
      dealer: this.entityId,
      causedByEffect: true,
      damage: dmgInfo.damage * 0.5
    });
  }

  public onDelete(): void {
    manager.removeLilstener(this.listenerId);
  }
}
So weit, so einfach. Der nächste Schritt wären dann jedoch Dinge wie "Talente" (im Sinne eines WoW oder Diablo) oder "Verbesserungen", die (1) auch nach dem Spielertod bestehen bleiben und (2) auch NPC-Mobs des Spielers betreffen können ("Deine (Unter-)Einheiten verursachen +10 Blitzschaden"). Jetzt wird es schwierig mit oben beschriebenen Modell, weil die Effekte ja beim Spielercharakter gespeichtert sind.

Mein erster Gedanke war dann, sie statt beim Spielercharacter ("Hero") beim Spieler selbst ("Player") zu speichern. Dann kann ein Effekt nicht nur den Spielertod überdauern sondern es ist auch semantisch schöner. Es fühlt sich ja schon etwas schmutzig an, wenn ein Hero-Effekt unmittelbar darauf Einfluss nimmt, wenn zwei NPC sich gegenseitig attackieren. Mit den Effekten beim Player stimmt diese Zuordnung ja, weil die NPCs selber auch zum Player gehören. Dann müssen die Events nur noch speichern, von wem und an wen es geht; und die Effekte müssen noch speichern, auf wen sie jetzt genau alles wirken sollen.

Dann bin ich aber an dem Punkt, dass eigentlich komplett egal ist, wo der Effekt jetzt gespeichert wird, weil er mit seinen Meta-Informationen komplett autark von seinem "Holder" ist. Warum es sich also nicht einfach machen und die Effekte in einem globalen Array speichern, ganz gleich auf wen sie wirken. Daraus ergäbe sich nur ein potenzielles Performance-Problem, weil alle Events stets alle Effekte im ganzen Spiel durchlaufen. Es wäre aber trivial einfach.

Eine Alternative wäre es zu sagen, dass Events in einem Baum auf- und absteigen. Bei folgendem Baum:

Code: Alles auswählen

- World
  - Player 1
    - Hero
    - NPC 1
    - NPC 2
  - Player 2
    - Hero
    - NPC 3
... würde ein Schaden von NPC1 and NPC 3 folgenden Weg nach Effekten abklappern: Flag "Capture" auf true -> NPC1 -> Player1 -> World -> Flag "Capture" auf false -> World -> Player 2 -> NPC3

Letztlich führen beide Wege nach Rom, ich wollte aber gerne fragen, ob jemand hier schon einmal an dieser Stelle stand und welche Erfahrungen er letztlich damit gemacht hat.

Liebe Grüße
Letztes Projekt: Grave of the Pumpkin (ZFX Halloween Action 2021)
Benutzeravatar
Jonathan
Establishment
Beiträge: 2348
Registriert: 04.08.2004, 20:06
Kontaktdaten:

Re: Datenstruktur für Einheiten-Effekte

Beitrag von Jonathan »

Hi,

prinzipiell hören sich die Überlegungen alle ok an, finde ich. Was jetzt insgesamt die beste Lösung ist und in allen Fällen und auch in der Zukunft am schönsten zu benutzen sein wird, ist vermutlich schwer zu sagen.
Aber eine Anregung hätte ich noch: Wieso nicht Dinge, die komplett unterschiedlich funktionieren, auch unterschiedlich im Code behandeln? In einem Rollenspiel in dem der Spieler kleine Geschütze aufstellen kann, würde man ja auch nicht mit aller Gewalt versuchen, die durch die gleiche Klasse abzudecken wie das Schwert des Spielercharakters, bloß weil beides Schaden verursacht. Gegebenenfalls kann man ja trotzdem Teile der Klassenhierarchie wiederverwenden, aber das bedeutet ja nicht zwangsweise, dass man alle Arten von Effekten an der selben Stelle speichern muss.
Lieber dumm fragen, als dumm bleiben!
https://jonathank.de/games/
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: Datenstruktur für Einheiten-Effekte

Beitrag von odenter »

Ob Events die richtige, im Sinne von gute Lösung, sind kann ich nicht beurteilen.
Ich würde mir Komponenten basteln welche die Boni abbilden. Und diese Komponenten würde ich als Liste an die jeweiligen Entitäten hängen, egal ob Spieler oder NPC.
An der Stelle wo Du deine Entitäten updatest kannst dann einfach die Liste durchlaufen und Boni anwenden.

Ich würde auch nicht alles in Klassen packen.
Also ich würde z.B. eine HealthComponente basteln welche Boni abbildet die sich auf Health auswirken usw. damit am Ende diese Komponenten einfach z.B. über eine JSON Datei definiert, geladen und erzeugt werden können.
Stichwort EntityComponentSystem.
Benutzeravatar
Lord Delvin
Establishment
Beiträge: 574
Registriert: 05.07.2003, 11:17

Re: Datenstruktur für Einheiten-Effekte

Beitrag von Lord Delvin »

Du solltest dir überlegen, wie du eine Applikationsreihenfolge durchsetzen kannst. Sonst macht es einen Unterschied, ob man erst einen "+1 dmg" item ausrüstet und dann ein "+10% dmg" oder umgekehrt.
XML/JSON/EMF in schnell: OGSS
Keine Lust mehr auf C++? Versuche Tyr: Get & Get started
Antworten