Sistema dei personaggi di gioco
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 107 di 107.
Sfida
FacileCostruiamo un sistema di personaggi di gioco che combina l'embedding delle struct, le interfacce e il pattern Strategy per creare meccaniche di combattimento flessibili! Progetterai diversi tipi di personaggi capaci di attaccare e difendersi in modi unici, con abilità che possono essere sostituite durante l'esecuzione.
Organizzerai il codice in cinque file:
character.go: definisci una struct di baseCharactercon campi comuni:Name(string),Health(int) eMaxHealth(int). Includi un costruttoreNewCharactere i metodi:IsAlive() bool: restituisce true se la salute è maggiore di 0Status() string: restituisce il formato:[Name]: [Health]/[MaxHealth] HP
interfaces.go: definisci le interfacce di combattimento che consentono il polimorfismo:- Interfaccia
AttackerconAttack() int: restituisce il danno inflitto - Interfaccia
DefenderconTakeDamage(damage int) int: applica la riduzione del danno e restituisce il danno effettivamente subito - Interfaccia
AbilityconUse() string: restituisce una descrizione dell'effetto dell'abilità
- Interfaccia
characters.go: crea tre tipi di personaggi che incorporanoCharactere implementano siaAttackersiaDefender:Warrior: ha i campiStrength(int) eArmor(int). Attack restituisce Strength come danno. TakeDamage riduce il danno in arrivo di Armor (il danno subito è almeno 1).Mage: ha i campiIntelligence(int) eMagicResist(int). Attack restituisce Intelligence * 2 come danno. TakeDamage riduce il danno in arrivo di MagicResist / 2 (il danno subito è almeno 1).Archer: ha i campiAgility(int) eEvasion(int). Attack restituisce Agility + 5 come danno. TakeDamage: se Evasion > 10, riduce il danno del 25% (divisione intera), altrimenti subisce l'intero danno (il danno subito è almeno 1).Includi un costruttore per ciascun tipo:
NewWarrior,NewMage,NewArcher, che accettano nome, salute e i valori delle statistiche specifiche.abilities.go: implementa il pattern Strategy con abilità intercambiabili:Struct
FireSpellconPower(int). Il suoUse()restituisce:Casts fireball for [Power] damageStruct
IceSpellconPower(int). Il suoUse()restituisce:Casts ice shard for [Power] damageStruct
PowerStrikeconMultiplier(int). Il suoUse()restituisce:Performs power strike at [Multiplier]x damageAggiungi un campo
ability(di tipoAbility) alla struct Mage, con i metodiSetAbility(a Ability)eUseAbility() string. UseAbility restituisceNo ability setse ability è nil.main.go: crea personaggi e mostra le meccaniche di combattimento.Leggi il tipo di personaggio (
warrior,mageoarcher), poi leggi il nome del personaggio, la salute e le statistiche specifiche del tipo. Crea il personaggio appropriato.Quindi leggi il numero di azioni. Ogni azione è una delle seguenti:
status: stampa lo stato del personaggioattack: stampa[Name] attacks for [damage] damagedefend [amount]: applica il danno e stampa[Name] takes [actual] damagesetability [type] [power]: solo per i maghi, imposta l'abilità (type èfire,iceostrike)useability: solo per i maghi, stampa il risultato di Use() dell'abilità
Verranno forniti i seguenti input:
- Tipo di personaggio, nome, salute e statistiche specifiche del tipo
- Numero di azioni, poi ogni azione su una riga separata
Ad esempio, dati:
warrior
Thorin
100
15
8
5
status
attack
defend 20
status
defend 100Il tuo output dovrebbe essere:
Thorin: 100/100 HP
Thorin attacks for 15 damage
Thorin takes 12 damage
Thorin: 88/100 HP
Thorin takes 92 damageE dati:
mage
Gandalf
80
25
10
6
attack
useability
setability fire 30
useability
setability ice 25
useability
defend 40Il tuo output dovrebbe essere:
Gandalf attacks for 50 damage
No ability set
Casts fireball for 30 damage
Casts ice shard for 25 damage
Gandalf takes 35 damageE dati:
archer
Legolas
90
18
15
4
status
attack
defend 24
statusIl tuo output dovrebbe essere:
Legolas: 90/90 HP
Legolas attacks for 23 damage
Legolas takes 18 damage
Legolas: 72/90 HPNota come l'embedding delle struct consenta a ciascun tipo di personaggio di condividere i campi e i metodi di Character, mentre le interfacce permettono il combattimento polimorfico: qualsiasi Attacker può infliggere danno e qualsiasi Defender può subirlo. Il pattern Strategy dà il meglio di sé con le abilità intercambiabili del Mage: lo stesso personaggio può usare incantesimi diversi senza modificare la propria implementazione di base!
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Leggi il tipo di personaggio
scanner.Scan()
charType := strings.TrimSpace(scanner.Text())
// Leggi il nome del personaggio
scanner.Scan()
name := strings.TrimSpace(scanner.Text())
// Leggi i punti salute
scanner.Scan()
health, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Leggi la prima statistica specifica del tipo
scanner.Scan()
stat1, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Leggi la seconda statistica specifica del tipo
scanner.Scan()
stat2, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// TODO: Crea il personaggio appropriato in base a charType
// Usa le variabili: charType, name, health, stat1, stat2
// Per warrior: stat1 = Strength, stat2 = Armor
// Per mage: stat1 = Intelligence, stat2 = MagicResist
// Per archer: stat1 = Agility, stat2 = Evasion
// Leggi il numero di azioni
scanner.Scan()
numActions, _ := strconv.Atoi(strings.TrimSpace(scanner.Text()))
// Elabora ogni azione
for i := 0; i < numActions; i++ {
scanner.Scan()
line := strings.TrimSpace(scanner.Text())
parts := strings.Fields(line)
action := parts[0]
// TODO: Gestisci ogni tipo di azione:
// - "status": stampa Status() del personaggio
// - "attack": stampa "[Name] attacks for [damage] damage"
// - "defend [amount]": applica il danno e stampa "[Name] takes [actual] damage"
// - "setability [type] [power]": per i maghi, imposta l'abilità (fire, ice o strike)
// - "useability": per i maghi, stampa il risultato di UseAbility()
_ = action // Rimuovi questa riga durante l'implementazione
_ = fmt.Println // Rimuovi questa riga durante l'implementazione
}
}
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online