Desarrollo de personajes de videojuegos
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 86 de 87.
Este desafío consiste en construir un sistema de Desarrollo de Personajes de Juego que demuestra el poder de la POO al modelar entidades complejas e interactivas.
Diseña una jerarquía de personajes donde una clase base Character defina atributos comunes como salud, nombre y nivel. Clases especializadas como Warrior, Mage y Archer extiendan esta base con habilidades y estilos de combate únicos.
Las interfaces funcionan de forma natural aquí: Attackable podría definir métodos de combate, mientras que Healable maneja habilidades de restauración. Un Mage podría implementar ambos, mientras que un Warrior se enfoca solo en atacar.
Considere utilizar el patrón Strategy para diferentes comportamientos de ataque, lo que permite a los personajes cambiar de estilo de combate de forma dinámica. El patrón State podría gestionar las condiciones del personaje, como los estados normal, envenenado o aturdido, que afectan al comportamiento.
Piense en la composición para sistemas de equipamiento donde los personajes "tienen" armas y armaduras en lugar de heredar de ellas. Use enums para las clases de personajes o los tipos de daño, y aplique el encapsulamiento para proteger las estadísticas de modificaciones no válidas.
Desafío
Fácil¡Vamos a construir un sistema de Desarrollo de Personajes de Juego que demuestre el poder de la POO al modelar entidades de juego complejas e interactivas! Crearás una jerarquía de personajes con diferentes estilos de combate, habilidades especiales y un sistema de estado que afecta el comportamiento de los personajes.
Organizarás tu código en siete archivos:
DamageType.java: Crea un enum que represente los diferentes tipos de daño en el juego. Incluye los valoresPHYSICAL,MAGICALyRANGED. Agrega un campo privadodescription(String), un constructor que lo asigne y un getter. El daño físico debe tener la descripción "brute force", el mágico debe tener "arcane energy" y el a distancia debe tener "precision strike".Attackable.java: Crea una interfaz que defina las capacidades de combate. Declara los métodosattack(Character target)que devuelve un String describiendo el ataque, ygetDamageType()que devuelve un DamageType.Healable.java: Crea una interfaz para las habilidades de restauración. Declara un métodoheal(int amount)que devuelve un String describiendo la acción de curación.Character.java: Crea una clase base abstracta para todos los personajes del juego. Incluye campos privados paraname(String),health(int),maxHealth(int) ylevel(int). El constructor recibe name, maxHealth y level, estableciendo la salud igual a maxHealth inicialmente. Incluye getters para todos los campos. Agrega un método protegidosetHealth(int health)que asegure que la salud se mantenga entre 0 y maxHealth. Crea un métodotakeDamage(int damage)que reduzca la salud y devuelva"[name] takes [damage] damage! Health: [health]/[maxHealth]". Agrega un métodoisAlive()que devuelva true si la salud es mayor que 0. Declara un método abstractogetCharacterClass()que devuelva un String.Warrior.java: Crea una clase que extienda de Character e implemente Attackable. Agrega un campo privadostrength(int). El constructor recibe name, maxHealth, level y strength. ImplementagetCharacterClass()para que devuelva"Warrior". ImplementagetDamageType()para que devuelvaPHYSICAL. Implementaattack(Character target)para infligir un daño igual a strength más level, llama al método takeDamage del objetivo y devuelve"[name] strikes with [damageType description] for [damage] damage!"seguido de un salto de línea y el resultado de takeDamage.Mage.java: Crea una clase que extienda de Character e implemente tanto Attackable como Healable. Agrega campos privadosintelligence(int) ymana(int). El constructor recibe name, maxHealth, level, intelligence y mana. ImplementagetCharacterClass()para que devuelva"Mage". ImplementagetDamageType()para que devuelvaMAGICAL. Implementaattack(Character target)para infligir un daño igual a intelligence multiplicado por 2, siguiendo el mismo formato que Warrior. Implementaheal(int amount)para restaurar la salud (usando setHealth) y devuelve"[name] channels healing magic! Health: [health]/[maxHealth]".Main.java: ¡Dale vida a tu sistema de juego! Recibirás una cadena de comandos separados por comas:CREATE_WARRIOR:name:maxHealth:level:strengthCREATE_MAGE:name:maxHealth:level:intelligence:manaATTACK:attackerName:targetNameHEAL:mageName:amountSTATUS:characterName
Mantén una colección de personajes. Procesa cada comando e imprime:
- CREATE_WARRIOR/CREATE_MAGE:
[name] the [characterClass] joins the battle! - ATTACK: Imprime el resultado del ataque (el String devuelto por el método attack)
- HEAL: Imprime el resultado de la curación, o
[name] cannot healsi el personaje no es un Mage - STATUS:
[name] ([characterClass]) - Level [level] - Health: [health]/[maxHealth] - [Alive/Defeated]
Después de todos los comandos, imprime
--- Battle Summary ---seguido del estado de cada personaje.
Recibirás una entrada: la cadena de comandos separados por comas.
Por ejemplo, con la entrada CREATE_WARRIOR:Thorin:100:5:15,CREATE_MAGE:Gandalf:80:7:12:50,ATTACK:Thorin:Gandalf,HEAL:Gandalf:25,STATUS:Gandalf, tu salida sería:
Thorin the Warrior joins the battle!
Gandalf the Mage joins the battle!
Thorin strikes with brute force for 20 damage!
Gandalf takes 20 damage! Health: 60/80
Gandalf channels healing magic! Health: 80/80
Gandalf (Mage) - Level 7 - Health: 80/80 - Alive
--- Battle Summary ---
Thorin (Warrior) - Level 5 - Health: 100/100 - Alive
Gandalf (Mage) - Level 7 - Health: 80/80 - Alive¡Este desafío combina herencia (jerarquía Character), implementación de múltiples interfaces (Mage implementa tanto Attackable como Healable), enums con métodos (DamageType), polimorfismo (tratar diferentes tipos de personajes de manera uniforme) y encapsulamiento (gestión protegida de la salud) en un sistema de juego atractivo!
Hoja de referencia
La programación orientada a objetos (POO) permite modelar sistemas complejos a través de clases, herencia, interfaces y patrones de diseño.
Jerarquía de clases
Cree una clase base con atributos comunes y extiéndala con clases especializadas:
public abstract class Character {
private String name;
private int health;
private int maxHealth;
private int level;
public Character(String name, int maxHealth, int level) {
this.name = name;
this.maxHealth = maxHealth;
this.health = maxHealth;
this.level = level;
}
protected void setHealth(int health) {
this.health = Math.max(0, Math.min(health, maxHealth));
}
public abstract String getCharacterClass();
}Interfaces
Defina contratos para comportamientos específicos que las clases pueden implementar:
public interface Attackable {
String attack(Character target);
DamageType getDamageType();
}
public interface Healable {
String heal(int amount);
}Enums con campos
Los enums pueden tener campos, constructores y métodos:
public enum DamageType {
PHYSICAL("brute force"),
MAGICAL("arcane energy"),
RANGED("precision strike");
private String description;
private DamageType(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}Implementación de múltiples interfaces
Una clase puede implementar múltiples interfaces:
public class Mage extends Character implements Attackable, Healable {
private int intelligence;
private int mana;
@Override
public String attack(Character target) {
// Implementación
}
@Override
public String heal(int amount) {
// Implementación
}
}Encapsulamiento
Use campos privados con setters protegidos para controlar el acceso y validar los datos:
protected void setHealth(int health) {
this.health = Math.max(0, Math.min(health, maxHealth));
}Polimorfismo
Trate diferentes tipos de personajes de manera uniforme a través de su clase base común o interfaces, permitiendo colecciones y llamadas a métodos flexibles.
Pruébalo tú mismo
import java.util.Scanner;
import java.util.HashMap;
import java.util.Map;
import java.util.LinkedHashMap;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: Usa una colección para mantener los personajes (LinkedHashMap preserva el orden de inserción)
// LinkedHashMap<String, Character> characters = new LinkedHashMap<>();
// TODO: Divide la entrada por comas para obtener comandos individuales
// String[] commands = input.split(",");
// TODO: Procesa cada comando:
// - CREATE_WARRIOR:name:maxHealth:level:strength
// Imprime: "[name] the Warrior joins the battle!"
//
// - CREATE_MAGE:name:maxHealth:level:intelligence:mana
// Imprime: "[name] the Mage joins the battle!"
//
// - ATTACK:attackerName:targetName
// Imprime el resultado del ataque desde el método attack
//
// - HEAL:mageName:amount
// Imprime el resultado de la curación, o "[name] cannot heal" si no es un Mage
//
// - STATUS:characterName
// Imprime: "[name] ([characterClass]) - Level [level] - Health: [health]/[maxHealth] - [Alive/Defeated]"
// TODO: Después de todos los comandos, imprime "--- Battle Summary ---"
// Luego imprime el estado de cada personaje
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy14Desafíos finales
Plataforma de E-LearningSistema bancarioDesarrollo de personajes de videojuegosServicio de alquiler de vehículos3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator