code review java – Juego de aventuras basado en texto en Java

Pregunta:

Estoy tratando de hacer un juego basado en texto y creo que tuve un buen comienzo. Sin embargo, este código parece un poco torpe y estoy bastante seguro de que hay una mejor manera de hacer la mayoría de las cosas en el código. Soy muy nuevo en la codificación.

package text.based.adventure;

import java.util.*;

public class TextBasedAdventure {

    public static String Name;
    public static int HP;
    public static int maxHP;
    public static int xp;
    public static int atk;
    public static int def;
    public static int lvl;
    public static int potion;
    static Random rand = new Random();
    static Scanner UI = new Scanner(System.in);
    public static int dam = rand.nextInt(6) + 1;
    public static int heal = rand.nextInt(6) + 1;

    public static void room2() {

    }

    public static void StartRoom() throws InterruptedException {
        String input;

        System.out.println("The room is dark and gloomy, it reeks of dead corpses and rotten food,");
        System.out
                .println("You look behind you, the skeleton you recently killed and the damaged map are on the floor");
        System.out.println("the only choice no is to move forward");
        System.out.println("avail commands: forward, heal");
        input = UI.nextLine();
        if (input.equals("heal")) {
            potion = potion - 1;
            HP = maxHP;
            System.out.print("You are now at max HP, HP=" + HP);
            Thread.sleep(2000);
            StartRoom();
        } else if (input.equals("forward")) {
            room2();
        } else {
            System.out.print("Please Choose A Valid Command");
            Thread.sleep(2000);
            StartRoom();
        }
    }

    public static void combatskel() throws InterruptedException {
        int skeldam = rand.nextInt(3) + 1;
        String input;
        int hpskel;
        hpskel = 5;
        if (HP <= 0) {
            gameover();

        }
        if (hpskel <= 0) {
            System.out.println("You Defeated A Skeleton");
            atk = atk + 1;
            def = def + 1;
            maxHP = maxHP + 1;

            System.out.format("You Have Leveled Up! atk is now %d, def is now %d, HP is now %d", atk, def, HP);
            System.out.println("");
            return;
        }
        while (hpskel > 0) {
            input = UI.nextLine();
            if (input.equals("attack")) {
                hpskel = hpskel - dam - atk;
                HP = HP - skeldam;
                System.out.format("You Have Been Hit, Your HP is %d", HP);
                System.out.println("");
                combatskel();

            } else if (input.equals("heal")) {
                if (potion <= 0) {
                    System.out.println("You Do Not Have Enough Potions");
                    combatskel();

                } else {
                    HP = HP + heal;
                    System.out.println("You Have Been Healed");
                    System.out.format("You Have Been Hit, Your HP is %d", HP);
                    System.out.println("");
                    HP = HP - skeldam;
                    Thread.sleep(1200);
                    System.out.format("You Have Been Hit, Your HP is %d", HP);
                    System.out.println("");
                    combatskel();
                }
            }
        }
    }

    public static void combatzombie() throws InterruptedException {
        System.out.println("You Have Encountered a zombie");
        int zombdam = rand.nextInt(3) + 2;
        String input;
        int hpzomb;
        hpzomb = 7;
        if (HP <= 0) {
            gameover();

        }
        if (hpzomb <= 0) {
            System.out.println("You Defeated A Zombie");
            atk = atk + 1;
            def = def + 1;
            maxHP = maxHP + 1;
            System.out.format("You Have Leveled Up! atk is now %d, def is now %d, HP is now %d", atk, def, HP);
            System.out.println("");
            return;
        }
        while (hpzomb > 0) {
            input = UI.nextLine();
            if (input.equals("attack")) {
                hpzomb = hpzomb - dam - atk;
                HP = HP - zombdam;
                System.out.format("You Have Been Hit, Your HP is %d", HP);
                System.out.println("");
                combatzombie();

            } else if (input.equals("heal")) {
                if (potion <= 0) {
                    System.out.println("You Do Not Have Enough Potions");
                    combatzombie();

                } else {
                    HP = HP + heal;
                    System.out.println("You Have Been Healed");
                    System.out.format("You Have Been Hit, Your HP is %d", HP);
                    System.out.println("");
                    HP = HP - zombdam;
                    Thread.sleep(1200);
                    System.out.format("You Have Been Hit, Your HP is %d", HP);
                    System.out.println("");
                    combatzombie();
                }
            }
        }
    }

    public static void gameover() {
        System.out.println(Name + " Died!");
        System.out.println("GAME OVER!");
        System.exit(0); // terminates if lost
    }

    /**
     * @param args
     *            the command line arguments
     * @throws java.lang.InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        maxHP = 20;
        potion = 3;

        System.out.println("Welcome To Dungeon Maze");
        System.out.println("Please Input Your Name ");
        Name = UI.nextLine();
        System.out.format("%s is a brave adventurer", Name);
        System.out.println("");
        Thread.sleep(3000);
        System.out.println("They were sent on a task to check on one of");
        Thread.sleep(3000);
        System.out.println("the sacred underground shrines of Port Nyanzaru,");
        Thread.sleep(3000);
        System.out.format("but when one of the skeletons knocked off the map %s had,", Name);
        System.out.println("");
        Thread.sleep(3000);
        System.out.println("they had no choice but to try and escape the dungeon");
        Thread.sleep(3000);
        System.out.println("ARE YOU READY BRAVE ADVENTURER");
        HP = maxHP;
        lvl = 1;
        def = 1;
        atk = 1;
        Thread.sleep(5000);
        StartRoom();
    }
}

Respuesta:

¡Gracias por compartir tu código!

Al mirar su código actual, parece que rápidamente se volverá difícil de mantener. Este juego parece un candidato ideal para refactorizar en diferentes clases .

Dijiste que eras nuevo en la programación, por lo que es posible que no tengas mucha experiencia (si es que tienes alguna) en la creación de tus propias clases.

Las clases se pueden considerar como un modelo para un objeto. Debes crear clases para representar las cosas en tu juego. Algunas clases potenciales que me llaman la atención son tal vez una clase de Game para representar el juego en sí, una clase de Player para representar al jugador del juego, una clase de Monster o 'Enemigo' para representar a un enemigo en tu juego. Actualmente tienes un esqueleto y un zombi, por lo que ambos podrían ser una instancia de Monster lugar de una colección de valores que debes administrar tú mismo.

Podrías conformarte con solo esos, o podrías ir aún más lejos y tener una clase de Room , una clase de Item (una poción podría ser un objeto) y así sucesivamente.

Su clase TextBasedAdventure está haciendo todo en este momento; rápidamente se volverá difícil de manejar. Separarlo en diferentes clases contribuirá en gran medida a aumentar la legibilidad y también la mantenibilidad.

Comencemos con la creación de la clase Player y veamos por qué sería preferible a su forma actual de manejar al jugador (ese ser)

public static String Name;
public static int HP;
public static int maxHP;
public static int xp;
public static int atk;
public static int def;
public static int lvl;
public static int potion;

Actualmente tiene solo un montón de valores diferentes que están sueltos en su programa. Movámoslos a una clase Player para encapsularlos .

public class Player {

    private int HP;
    private int maxHP;
    private int xp;
    private int atk;
    private int def;
    private int lvl;

    private int numPotions;

    public Player(){
        HP = maxHP;
        lvl = 1;
        // ... any other initial values you want
    }

    /* Getters & Setters */
    public int getHp(){
        return HP;
    }

    public void setHP(int hp){
        HP = hp;
    }

    // .. any other ones you need
}

Así que ahora tenemos una clase de Player que contiene todos los valores relacionados con el jugador de tu juego.

También puede proporcionar algunos métodos auxiliares en su Player que podrían verse así.

public boolean isAlive(){
    return HP > 0;
}

Es mucho más legible ver algo como esto.

if(player.isAlive()){
   // do stuff
}

Más allá de esto

if(HP > 0){
  // do stuff
}

Y que tal otro

public void heal(){
    if(numPotions > 0){
        numPotions--;
        HP = maxHP;
    }
}

ahora podemos usar

player.heal();

en lugar de implementarlo directamente en el método principal. Esto también tiene la ventaja de permitirnos llamar a este método en cualquier momento que sea necesario y también cambiar la implementación en un lugar si es necesario cambiar, en lugar de buscar y cambiar la lógica en todo el código.

Un gran problema que puedo ver son sus métodos combatskel() y combatzombie() . Estos métodos son casi idénticos, lo único que cambia es el texto que se imprime y algunos números. Esto podría refactorizarse para tomar una instancia de un Monster como argumento.

Primero tenemos que hacer una clase de Monster . Mirando esos 2 métodos, saquemos todas las variables que debería tener un Monster .

Parece hp , damage , atk , def . Puede que me falten algunos, pero sigamos con esto por ahora.

Podríamos terminar con algo como esto

public class Monster {
    
    private int hp;
    private int damage;
    private int atk;
    private int def;

    public Monster(int hp, int damage, int atk, int def) {
        this.hp = hp;
        this.damage = damage;
        this.atk = atk;
        this.def = def;
    }

    public int getHp() {
        return hp;
    }

    public int getDamage() {
        return damage;
    }

    public int getAtk() {
        return atk;
    }

    public int getDef() {
        return def;
    }
}

Ahora tenemos muchos cruces entre la clase Player y la clase Monster , por lo que podríamos derivar ambas de una clase Creature o algo similar. Pero mantengamos las cosas simples por ahora y solo tengamos algo de duplicación de código.

Ahora que tenemos una clase de Monster , tal vez podríamos hacer un método que se parezca a

public void combat(Monster monster){
    // do the fight logic
}

Ahora bien, este monstruo podría ser un zombi, un esqueleto, un fantasma, un orco, un elfo o la cantidad de monstruos nuevos que quieras agregar a tu juego (¡puedes poner su información en un archivo y cargar cualquier cantidad de ellos!)

Intentemos y comencemos a escribir el método, copiando la lógica de su implementación actual.

public void combat(Monster monster){
    System.out.println("You Have Encountered a " + monster.getName());
    int damage = monster.getDamage();

    if (!player.isAlive()) {
        gameover();
    }

    if(!monster.isAlive()){
        System.out.println("You Defeated A " + monster.getName());
        player.levelUp(); // doesn't exist - you could implement it yourself by placing the level up logic there.
    }

    // and the rest...
}

Obviamente, esto es solo el comienzo, no refactorizaré completamente todo su código, pero espero que esto le dé una buena idea de cómo puede usar objetos y métodos para reducir la duplicación de código. ¡Ahora podrías tener 1 método para manejar el combate con un monstruo en lugar de 1 x la cantidad de monstruos en tu juego!

Cosas misceláneas

Muchos de sus métodos arrojan una InterruptedException . Este es un detalle de implementación que se escapa de sus métodos . No hay ninguna razón por la que el código de llamada deba manejar estas excepciones solo porque quieras usar Thread.sleep() en tu juego. Recomendaría algo como esto

public void wait(int numSeconds){
    try {
        Thread.sleep(numSeconds * 1000);
    } catch (InterruptedException e) {
        // don't worry so much about this
    }
}

Normalmente, nunca debería hacer un intento de captura con un bloque vacío, pero en este escenario, realmente no importa si algo se estropea mientras duerme, lo que probablemente nunca sucederá. Ahora, el resto de su código no tiene que lidiar con las excepciones. Podrías usarlo así

wait(3);

esperará 3 segundos.

No use variables públicas . Utiliza muchas variables públicas en su código principal, todas las variables deben ser privadas en el 99% de los casos. Algunos buenos ejemplos de buenos casos de uso para el público son Integer.MAX_VALUE y Math.PI Estos son buenos ejemplos porque son valores que nunca cambiarán. Si desea proporcionar acceso a variables en otras clases, cree campos privados y luego cree métodos getter públicos.

Los nombres de sus variables y métodos deben usar camelCase . Te adhieres a esto correctamente en muchos lugares, pero variables como Name y StartGame violan esta convención.

Consulte los documentos sobre convenciones de nomenclatura aquí.

¡Esperamos que esta reseña haya sido útil para usted!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top

web tasarım