Desarrollo de personajes de videojuegos
Parte de la sección Programación orientada a objetos del Journey de GDScript de Coddy. Lección 64 de 65.
Desafío
IntermedioCrea los personajes de un juego de rol: un Character abstracto, tres clases que lo extienden y habilidades que aprenden los personajes.
Character en character.gd es una clase @abstract:
- Propiedades:
character_name(del constructor),level1,max_healthyhealth100.0,max_manaymana50,strength,intelligenceyagility10,defense5yabilities, unArray[Ability]. Los valores de salud sonfloat; las demás estadísticas sonint. - Una señal
defeated(character_name: String). - Métodos abstractos
class_title() -> Stringyattack_power() -> float. level_up() -> String: nivel+1, salud máxima+10, maná máximo+5, fuerza, inteligencia, agilidad y defensa+1; la salud y el maná se rellenan hasta sus máximos. DevuelveNAME reached level N!.attack(target): imprimeNAME attacks TARGETy después devuelvetarget.take_damage(attack_power()).take_damage(amount) -> float: el daño esamount - defense * 0.5, pero como mínimo1.0; la salud disminuye en esa cantidad y nunca baja de0.0. ImprimeNAME takes D damage (HP H)con un decimal en cada valor, emitedefeatedcuando este golpe reduce la salud a0y devuelve el daño.is_alive(): salud superior a0.learn_ability(ability)añade la habilidad aabilities.use_ability(index, target) -> float: si el índice está fuera deabilities, imprimeNAME has no ability INDEXy devuelve0.0; de lo contrario, devuelve lo que devuelve eluse()de la habilidad._to_string():NAME the TITLE (level L): HP H/MAX, mana M/MAX, STR S, INT I, AGI A, DEF D; la salud lleva un decimal.
Cada subclase llama al constructor de la clase padre y luego establece sus propias estadísticas iniciales (las demás conservan los valores base), devuelve el nombre de su clase desde class_title() y sobrescribe level_up() con super():
Warrior: salud150.0, maná30, fuerza15, inteligencia5, defensa8; poder de ataquestrength * 1.2; al subir de nivel también obtiene fuerza+2y salud máxima+10(la salud se rellena).Mage: salud80.0, maná150, fuerza5, inteligencia20, defensa3; poder de ataqueintelligence * 0.4; después de un ataque normal recupera5de maná (sin superar el máximo); al subir de nivel también obtiene inteligencia+2y maná máximo+15(el maná se rellena).Rogue: salud100.0, maná60, fuerza12, inteligencia8, agilidad18, defensa4; poder de ataquestrength * 0.5 + agility * 0.7; al subir de nivel también obtiene agilidad+2.
Ability en ability.gd: el constructor recibe ability_name, mana_cost (int) y damage (float). use(user, target) -> float imprime USER does not have enough mana for ABILITY y devuelve 0.0 cuando el maná del usuario es inferior al coste. De lo contrario, consume el maná, imprime USER casts ABILITY on TARGET y devuelve target.take_damage(damage + user.intelligence * 0.5).
El código proporcionado se conecta a la señal defeated de cada personaje e imprime NAME is defeated!. Para esta entrada:
hero warrior|Aragorn
hero rogue|Orc
attack Aragorn|Orc
attack Orc|Aragorn
level Aragorn
status Aragornla salida es:
Aragorn attacks Orc
Orc takes 16.0 damage (HP 84.0)
Orc attacks Aragorn
Aragorn takes 14.6 damage (HP 135.4)
Aragorn reached level 2!
Aragorn the Warrior (level 2): HP 170.0/170.0, mana 35/35, STR 18, INT 6, AGI 11, DEF 9Tu código va en character.gd, warrior.gd, mage.gd, rogue.gd y ability.gd. main.gd contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
extends Node
func _ready():
# Código de entrada/salida proporcionado: mantenlo tal como está
var heroes := {}
var line := OS.read_string_from_stdin()
while line != "":
var parts := line.split(" ", true, 1)
var f: PackedStringArray = parts[1].split("|") if parts.size() > 1 else PackedStringArray()
match parts[0]:
"hero":
var hero: Character
match f[0]:
"warrior":
hero = Warrior.new(f[1])
"mage":
hero = Mage.new(f[1])
"rogue":
hero = Rogue.new(f[1])
hero.defeated.connect(func(who): print(who, " is defeated!"))
heroes[f[1]] = hero
"learn":
heroes[f[0]].learn_ability(Ability.new(f[1], int(f[2]), float(f[3])))
"attack":
heroes[f[0]].attack(heroes[f[1]])
"cast":
heroes[f[0]].use_ability(int(f[1]), heroes[f[2]])
"level":
print(heroes[f[0]].level_up())
"status":
print(heroes[f[0]])
line = OS.read_string_from_stdin()
Todas las lecciones de Programación orientada a objetos
1Fundamentos de la programación orientada a objetos
Archivos externosIntroducción a la programación orientada a objetosClases vs. objetosLa palabra clave selfMétodosPropiedadesConstructor (_init)Repaso: calculadora sencilla4Polimorfismo
Repaso de la sobrescritura de métodosTipado patoClases y métodos abstractosDiseño de interfacesRepaso: calculadora de formas7Señales
¿Qué es una señal?Declarar y emitirConectarse a las señalesSeñales con argumentosRepaso: sistema de salud10Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite2Propiedades de clase
Variables de instancia y estáticasFunciones estáticasMétodos setter de propiedadesMétodos getter de propiedadesPrivado por convenciónRepaso: administrador de cuentas bancarias5Encapsulación
Miembros públicos y privadosValidación con settersOcultación de informaciónPropiedades de solo lecturaRepaso: sistema de registros de estudiantes8Conceptos avanzados de POO
Composición frente a herenciaClases internasNodos como componentesRefCounted frente a NodeEnums en clases3Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosCadenas de herenciaComprobaciones de tipo con isRepaso: jerarquía de empleados6Métodos especiales
Introducción a los métodos virtualesEl método _to_stringIteradores personalizadosRepaso: lista personalizada9Patrones de diseño, parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy12Desafíos finales
Plataforma de aprendizaje en líneaSistema bancarioDesarrollo de personajes de videojuegosServicio de alquiler de vehículosPractica por tu cuenta: Compilador de GDScript online