Spielcharaktersystem
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 107 von 107.
Aufgabe
EinfachErstellen wir ein Spielcharakter-System, das Struct-Einbettung, Interfaces und das Strategy-Muster kombiniert, um flexible Kampfmechaniken zu entwickeln! Du entwirfst verschiedene Charaktertypen, die auf einzigartige Weise angreifen und verteidigen können, mit Fähigkeiten, die zur Laufzeit ausgetauscht werden können.
Du organisierst deinen Code auf fünf Dateien verteilt:
character.go: Definiere eineCharacter-Struct mit gemeinsamen Feldern:Name(string),Health(int) undMaxHealth(int). Füge einenNewCharacter-Konstruktor und folgende Methoden hinzu:IsAlive() bool: gibt true zurück, wenn die Gesundheit größer als 0 istStatus() string: gibt das Format[Name]: [Health]/[MaxHealth] HPzurück
interfaces.go: Definiere die Kampf-Interfaces, die Polymorphie ermöglichen:Attacker-Interface mitAttack() int: gibt den verursachten Schaden zurückDefender-Interface mitTakeDamage(damage int) int: wendet Schadensreduzierung an und gibt den tatsächlich erlittenen Schaden zurückAbility-Interface mitUse() string: gibt eine Beschreibung des Fähigkeitseffekts zurück
characters.go: Erstelle drei Charaktertypen, dieCharactereinbetten und sowohlAttackerals auchDefenderimplementieren:Warrior: besitzt die FelderStrength(int) undArmor(int). Der Angriff gibt Strength als Schaden zurück. TakeDamage reduziert den eingehenden Schaden um Armor (mindestens 1 erlittener Schaden).Mage: besitzt die FelderIntelligence(int) undMagicResist(int). Der Angriff gibt Intelligence * 2 als Schaden zurück. TakeDamage reduziert den eingehenden Schaden um MagicResist / 2 (mindestens 1 erlittener Schaden).Archer: besitzt die FelderAgility(int) undEvasion(int). Der Angriff gibt Agility + 5 als Schaden zurück. TakeDamage: Wenn Evasion > 10 ist, wird der Schaden um 25 % reduziert (Ganzzahldivision), andernfalls wird der volle Schaden erlitten (mindestens 1 erlittener Schaden).Füge für jeden Typ einen Konstruktor hinzu:
NewWarrior,NewMageundNewArcher, die Name, Gesundheit und die jeweiligen spezifischen Statuswerte entgegennehmen.abilities.go: Implementiere das Strategy-Muster mit austauschbaren Fähigkeiten:FireSpell-Struct mitPower(int). IhrUse()gibt Folgendes zurück:Casts fireball for [Power] damageIceSpell-Struct mitPower(int). IhrUse()gibt Folgendes zurück:Casts ice shard for [Power] damagePowerStrike-Struct mitMultiplier(int). IhrUse()gibt Folgendes zurück:Performs power strike at [Multiplier]x damageFüge der Mage-Struct ein
ability-Feld (vom TypAbility) sowie die MethodenSetAbility(a Ability)undUseAbility() stringhinzu. UseAbility gibtNo ability setzurück, wenn ability nil ist.main.go: Erstelle Charaktere und führe die Kampfmechaniken vor.Lies den Charaktertyp (
warrior,mageoderarcher) ein. Lies anschließend den Namen, die Gesundheit und die typspezifischen Statuswerte des Charakters ein. Erstelle den entsprechenden Charakter.Lies dann die Anzahl der Aktionen ein. Jede Aktion ist eine der folgenden:
status: gib den Status des Charakters ausattack: gib[Name] attacks for [damage] damageausdefend [amount]: wende Schaden an und gib[Name] takes [actual] damageaussetability [type] [power]: setze nur für Magier eine Fähigkeit (type istfire,iceoderstrike)useability: gib nur für Magier das Use()-Ergebnis der Fähigkeit aus
Die folgenden Eingaben werden bereitgestellt:
- Charaktertyp, Name, Gesundheit und typspezifische Statuswerte
- Anzahl der Aktionen, anschließend jede Aktion in einer eigenen Zeile
Beispielsweise bei folgender Eingabe:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100Deine Ausgabe sollte folgendermaßen aussehen:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damageUnd bei folgender Eingabe:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40Deine Ausgabe sollte folgendermaßen aussehen:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damageUnd bei folgender Eingabe:
archer
Legolas
90
18
15
4
status
attack
defend 24
statusDeine Ausgabe sollte folgendermaßen aussehen:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HPBeachte, wie die Struct-Einbettung es jedem Charaktertyp ermöglicht, die Felder und Methoden der grundlegenden Character-Struct gemeinsam zu verwenden, während Interfaces einen polymorphen Kampf ermöglichen, bei dem jeder Attacker Schaden verursachen und jeder Defender Schaden erleiden kann. Das Strategy-Muster zeigt seine Stärke bei den austauschbaren Fähigkeiten des Mage: Derselbe Charakter kann verschiedene Zauber verwenden, ohne dass seine grundlegende Implementierung geändert werden muss!
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Charaktertyp lesen
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// Charaktername lesen
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// Gesundheit lesen
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Ersten typspezifischen Stat lesen
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Zweiten typspezifischen Stat lesen
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO: Erstelle den entsprechenden Charakter basierend auf charType
// Verwende die Variablen: charType, name, health, stat1, stat2
// Für warrior: stat1 = Strength, stat2 = Armor
// Für mage: stat1 = Intelligence, stat2 = MagicResist
// Für archer: stat1 = Agility, stat2 = Evasion
// Anzahl der Aktionen lesen
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Jede Aktion verarbeiten
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO: Behandle jeden Aktionstyp:
// - "status": Status() des Charakters ausgeben
// - "attack": "[Name] attacks for [damage] damage" ausgeben
// - "defend [amount]": Schaden anwenden und "[Name] takes [actual] damage" ausgeben
// - "setability [type] [power]": für Magier Fähigkeit setzen (fire, ice oder strike)
// - "useability": für Magier UseAbility()-Ergebnis ausgeben
_ = action // Entferne diese Zeile bei der Implementierung
_ = fmt.Println // Entferne diese Zeile bei der Implementierung
}
}
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler