Autor Tema: Batalla RPG por turnos con Java juego rol Role Playing Game Señor de los Anillos  (Leído 7380 veces)

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #20 en: 22 de Enero 2022, 22:28 »
Una última pregunta, o eso espero que ya bastante me estás ayudando, para mostrar el resultado de la batalla por un campo de texto en vez de por consola, ¿declaro primero una variable JTextField y la asocio al botón de Luchar o mejor implementar getContentPane() en todos los mensajes.

Gracias de nuevo.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 983
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #21 en: 23 de Enero 2022, 00:51 »
JTextField se te quedaría pequeño. Mejor un JTextArea.

Sin embargo, hacer esto va a requerir bastantes cambios en todo el proyecto.
La clase Batalla ahora tendrá que comunicarse de algún modo con ese JTextArea para lanzarle mensajes.
Pero recuerdo que algunos mensajes del combate, los lanzaban las distintas clases Soldado desde sus métodos atacar()..., ¿también habrá que buscarles conexión con el JTextArea o mejor pensar en otra solución?

También, usabamos un Scanner simplemente para pausar la consola de texto, poder leer lo que salía en pantalla, y pulsar la tecla enter para continuar con el siguiente turno de ataque.
Esto ahora no se podrá hacer y lo que vamos a tener van a ser decenas de mensajes a la velocidad de la luz, los combates van a durar apenas unos milisegundos.
Se puede intentar añadir un retardo entre un mensaje y otro..., pero para ello vamos a tener que jugar con hilos (threads)

 Lo ideal sería reescribir el proyecto desde casi cero, ya teniendo en mente que estará destinado a una GUI y no ha mostrarse por consola de texto.
Pero bueno, parcheando por un lado y por otro, supongo que se podrá apañar...


De momento, crearía una nueva clase JPanel, que tenga un botón y un JTextArea.
Le daremos un método para que reciba un String y lo añada como nueva línea al JTextArea. Así abrimos una vía para que otra clase (Batalla) pueda indicarle que ha de ir mostrando.

El botón de este panel, ha de poner en marcha la clase Batalla, la cuál ha de tener acceso a los Ejercitos (Heroes y Bestias) que se encuentran en la clase JFrame principal.
Esto significa que desde este nuevo panel, no podemos llamar a la clase Batalla, porque desde aquí no podemos "ver" a los ejércitos.

Se puede solucionar tal y como hemos hecho en los paneles anteriores, añadir atributos para poder referenciar a esos ejercitos mediante el constructor de este panel.

Pero otra opción, y que podríamos aplicarla aquí para variar un poco, sería escribir el ActionListener de este botón en la clase principal, y luego hacérselo llegar mediante un método.
Así que haremos eso, un método que reciba un ActionListener y lo aplique al botón de este panel.
Código: [Seleccionar]
public class PanelTexto extends JPanel {

private JTextArea areaTexto;
private JButton btLuchar;

public PanelTexto() {
areaTexto = new JTextArea();
areaTexto.setEditable(false);
btLuchar = new JButton("¡Luchar!");

JScrollPane scrollArea = new JScrollPane();
scrollArea.setViewportView(areaTexto);
scrollArea.setPreferredSize(new Dimension(50, 200));
scrollArea.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 20, 20, 20),
BorderFactory.createLoweredSoftBevelBorder()));

JPanel pnBoton = new JPanel();
pnBoton.add(btLuchar);

setLayout(new BorderLayout());
add(pnBoton, BorderLayout.NORTH);
add(scrollArea, BorderLayout.CENTER);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(0, 10, 10, 10),
BorderFactory.createRaisedSoftBevelBorder()));
}

//La accion de este botón se escribirá en otra clase
public void setAccionBotonLuchar(ActionListener accion) {
btLuchar.addActionListener(accion);
}

public void nuevaLinea(String linea) {
areaTexto.append(linea + "\n");
}

}

Luego veremos como ponemos este panel en el JFrame, pero antes vamos tratar el tema de la clase Batalla y hacer que los mensajes salgan con algo de retraso entre ellos.

Para esto, como dije antes, deberíamos transformar la clase Batalla en un Thread, un hilo.
No se si has trabajado con hilos antes, no son complicados la verdad. Si lo convertimos en un hilo, el proceso de batalla se llevará a cabo por separado del proceso que estará gestionando la interfaz gráfica: ventana, botones, etc..
Eso nos permite cosas como hacer "dormir" ese hilo durante unos milisegundos cada vez que queramos, y así podremos ver como los mensajes van apareciendo en el area de texto de uno en uno, y no todos de golpe como ocurrirá si no lo hacemos en un hilo paralelo.

Para convertir Batalla en un hilo, basta con hacer que herede de la clase Thread y poner el código que queremos que se ejecute en un método llamado run().
Si no quieres "cargarte" la clase que ya tenemos, puedes crear otra nueva.
Yo lo prefiero así y he creado una clase llamada BatallaHilo.
Le vamos a dar tres atributos: dos objetos Ejercito y un objeto PanelTexto, que será una referencia al nuevo panel que acabamos de crear. Así esta clase tendrá acceso a todo lo que necesita, los Ejércitos para que se líen a mamporrazos entre ellos y el área de texto donde ir publicando los detalles de la contienda.

En el método run(), vamos a poner el mismo código que teníamos en el método estático llamado batallar() de la anterior clase Batalla.
El código va a ser el mismo, pero los System.out.println() los vamos cambiar por llamadas al PanelTexto donde le pasamos los Strings para que los muestre en el JTextArea.

Es decir, donde antes hacíamos esto:
Código: [Seleccionar]
System.out.println(bestia.getNombre() + " ha muerto.");Ahora haremos:
Código: [Seleccionar]
pnTexto.nuevaLinea(bestia.getNombre() + " ha muerto.");
El método pausa() donde antes usábamos el Scanner para detener la consola, ahora lo vamos a cambiar por un código que hará dormitar el hilo durante los milisegundos que queramos indicarle.
Esta sería la clase BatallaHilo
Código: [Seleccionar]
public class BatallaHilo extends Thread {

private Ejercito heroes;
private Ejercito bestias;
private PanelTexto pnTexto;

public BatallaHilo(Ejercito heroes, Ejercito bestias, PanelTexto pnTexto) {
this.heroes = heroes;
this.bestias = bestias;
this.pnTexto = pnTexto;
}

@Override
public void run() {
//Mientras ningun ejército haya sido derrotado....
while(!heroes.esDerrotado() && !bestias.esDerrotado()) {

//Calculamos el total de turnos, según el ejército más grande
int turnosTotal;
if (heroes.soldados.size() >= bestias.soldados.size())
turnosTotal = heroes.soldados.size();
else
turnosTotal = bestias.soldados.size();

//Comienza una ronda de turnos
for (int turno = 0; turno < turnosTotal; turno++) {
//Seleccionamos combatientes
Heroe heroe = (Heroe) heroes.getSoldado(turno);
Bestia bestia = (Bestia) bestias.getSoldado(turno);
//Comprobamos que ninguno sea null
if (heroe == null && bestia == null)
//¿Ambos son null?Entonces esta ronda de turnos ha terminado
break;
else if (heroe == null) {
//No hay Heroe, Bestia queda en guardia
pnTexto.nuevaLinea(bestia.getNombre() + " queda en guardia");
pausa(500);
}
else if (bestia == null) {
//No hay Bestia, Heroe queda en guardia
pnTexto.nuevaLinea(heroe.getNombre() + " queda en guardia");
pausa(500);
}
else {
//Ninguno es null, comienza el combate
pnTexto.nuevaLinea("Lucha entre " + heroe + " y " + bestia);
pausa(250);
//Turno heroe
pnTexto.nuevaLinea("Turno de " + heroe.getNombre());
pausa(250);
pnTexto.nuevaLinea(heroe.atacar(bestia));
pnTexto.nuevaLinea("Datos Actualizados de " + bestia);
pausa(1000);
if (bestia.estaMuerto()) {
pnTexto.nuevaLinea(bestia.getNombre() + " ha muerto.");
pausa(1000);
}
else {
//Turno bestia
pnTexto.nuevaLinea("Turno de " + bestia.getNombre());
pausa(250);
pnTexto.nuevaLinea(bestia.atacar(heroe));
pnTexto.nuevaLinea("Datos Actualizados de " + heroe);
pausa(1000);
if (heroe.estaMuerto()) {
pnTexto.nuevaLinea(heroe.getNombre() + " ha muerto.");
pausa(1000);
}
}
}
//Turno combate finalizado, ejercitos actualizan sus filas
heroes.comprobarEjercito();
bestias.comprobarEjercito();
//Y se inicia el siguiente turno
}
}

//Las rondas de turnos han finalizado porque algún ejército ha sido derrotado. Comprobamos
if (heroes.esDerrotado())
pnTexto.nuevaLinea("Han ganado las Bestias. Soldados restantes: " + bestias.soldados.size());
else
pnTexto.nuevaLinea("Han ganado los Heroes. Soldados restantes: " + heroes.soldados.size());
}

private void pausa(long milis) {
try {
sleep(milis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

Hay otro cambio importante aquí. Recuerda que dije que algunos mensajes en pantalla, venían del método atacar de las clases Soldado, por ejemplo:
Código: [Seleccionar]
@Override
public void atacar(Soldado enemigo) {
if (enemigo instanceof Orcos) {
//Regla específica cuando un Elfo ataca un Orco
Random dado = new Random();
int tirada1 = dado.nextInt(101);
int tirada2 = dado.nextInt(101);
System.out.println("Primer dado: " + tirada1);
System.out.println("Segundo dado: " + tirada2);
int maximo =  Math.max(tirada1, tirada2);
System.out.println("*****¡¡El odio élfico hacia los Orcos incrementa el ataque en 1.5x!!*****");
maximo *= 1.5;
System.out.println("Valor de ataque resultante: " + maximo);
enemigo.recibirAtaque(maximo);

}
else //Si no es Orco, se aplica la regla general de los Heroes
super.atacar(enemigo);
}

Estos mensajes se lanzan a la consola durante la ejecución de la "batalla"_
Citar
               else {
                  //Turno bestia
                  System.out.println("Turno de " + bestia.getNombre());
                  bestia.atacar(heroe);
                  System.out.println("Datos Actualizados de " + heroe);

Nosotros hemos reconducido los System.out de la clase Batalla para que ahora salgan por el area de texto, pero estos no los tenemos controlados porque provienen de otras clases...
¿Cómo hacemos para tener control sobre ellos?

Pues vamos a tener que cambiar los métodos atacar() de las clases Soldado, para que en lugar de que hagan System.out por su cuenta, nos retornen esos mensajes como String, y así podamos reconducirlos para que vayan al JTextArea.

Esta "reconducción", ya la he puesto en el código de la clase BatallaHilo que he puesto antes:
Citar
               else {
                  //Turno bestia
                  pnTexto.nuevaLinea("Turno de " + bestia.getNombre());
                  pausa(250);
                  pnTexto.nuevaLinea(bestia.atacar(heroe));
                  pnTexto.nuevaLinea("Datos Actualizados de " + heroe);

Pero para que funcione, hay que modificar las clases Soldado. Comenzando por la clase madre. Aquí pusimos un método abstracto de tipo void para que las clases hijas lo sobreesribiesen a su manera cada una.
Ya no será void, ahora retornará un String
Clase Soldado
Citar
   //Este método lo han de sobreescribir Heroe y Bestia, porque será distinto para cada uno
   public abstract String atacar(Soldado enemigo);

Las siguientes clases hijas, ya no harán System.out. Ahora retornarán los mensajes en un String.

Clase Heroe
Código: [Seleccionar]
@Override
public String atacar(Soldado enemigo) {
// El ataque será el mejor lanzamiento entre dos dados de 0 a 100
Random dado = new Random();
int tirada1 = dado.nextInt(101);
int tirada2 = dado.nextInt(101);
//System.out.println("Primer dado: " + tirada1);
//System.out.println("Segundo dado: " + tirada2);
int maximo =  Math.max(tirada1, tirada2);
enemigo.recibirAtaque(maximo);

return "Primer dado: " + tirada1 + "\n" + "Segundo dado: " + tirada2;
}

Clase Bestia
Código: [Seleccionar]
@Override
public String atacar(Soldado enemigo) {
//Único lanzamiento de un dado entre 0 y 90
Random dado = new Random();
int tirada = dado.nextInt(91);
//System.out.println("Resultado del dado es: " + tirada);
enemigo.recibirAtaque(tirada);
return "Resultado del dado es: " + tirada;
}


La clase Elfos, es la única que tiene una versión propia de este método por aquello de que queríamos un ataque especial cuando luchaba con Orcos.
También ha de ser "reconducido":
Código: [Seleccionar]
@Override
public String atacar(Soldado enemigo) {
if (enemigo instanceof Orcos) {
//Regla específica cuando un Elfo ataca un Orco
Random dado = new Random();
int tirada1 = dado.nextInt(101);
int tirada2 = dado.nextInt(101);
//System.out.println("Primer dado: " + tirada1);
//System.out.println("Segundo dado: " + tirada2);
int maximo =  Math.max(tirada1, tirada2);
//System.out.println("*****¡¡El odio élfico hacia los Orcos incrementa el ataque en 1.5x!!*****");
maximo *= 1.5;
//System.out.println("Valor de ataque resultante: " + maximo);
enemigo.recibirAtaque(maximo);

return "Primer dado: " + tirada1 + "\n" + "Segundo dado: " + tirada2 + "\n"
+ "*****¡¡El odio élfico hacia los Orcos incrementa el ataque en 1.5x!!*****\n"
+ "Valor de ataque resultante: " + maximo;
}
else //Si no es Orco, se aplica la regla general de los Heroes
return super.atacar(enemigo);
}


Vale, tenemos un nuevo panel con area de texto y una renovada clase Batalla que ahora es un hilo que publicará mensajes en el area de texto.

Ahora nos vamos a la clase principal JFrame, porque tenemos que encajar este panel en la interfaz y escribir el ActionListener para el botón de "Luchar".
Este ActionListener lo que hará será poner en marcha el hilo de batalla.

Resalto en azul los cambios:
Citar
public class BatallaRPG extends JFrame {
   
   //Modelo
   private Ejercito bestias;
   private Ejercito heroes;
   
   //Vista
   private PanelCrearSoldado crearHeroes;
   private PanelCrearSoldado crearBestias;
   private PanelTexto pnTexto;
   
   public BatallaRPG() {
      bestias = new Ejercito();
      heroes = new Ejercito();
      reclutar();
      //Este panel referenciará los Heroes
      crearHeroes = new PanelCrearSoldado("Heroes", new String[] {"Elfo", "Humano", "Hobbit"}, heroes);
      //Este refenciará a la Bestias
      crearBestias = new PanelCrearSoldado("Bestias", new String[] {"Trasgo", "Orco"}, bestias);
      
      pnTexto = new PanelTexto();
      pnTexto.setAccionBotonLuchar(new AccionBotonLuchar());

      
      JPanel pnSuperior = new JPanel();
      pnSuperior.add(crearHeroes);
      pnSuperior.add(crearBestias);
      
      JPanel pnInferior = new JPanel();
      pnInferior.add(pnTexto);
      
      setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
      add(pnSuperior);
      add(pnTexto);

      
      setTitle("Batalla RPG");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      pack();
      setLocationRelativeTo(null);
      setVisible(true);
   }
   
   private class AccionBotonLuchar implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         BatallaHilo batalla = new BatallaHilo(heroes, bestias, pnTexto);
         batalla.start();
      }
   }


   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            new BatallaRPG();   
         }
      });
   }
}

Y con todo esto, ya tenemos un JTextArea que muestra los vaivenes de la batalla.



Hay una pega, el JTextArea no muestra directamente las últimas lineas que se añaden, tenemos que estar haciendo scroll todo el rato para ver los últimos mensajes.

No se si este comportamiento se puede cambiar, lo investigaré.

Pregunta si algo no ha quedado claro, o si sientes que te has perdido..., ya imagino que no esperabas tener que hacer taaaaantos cambios
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 983
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #22 en: 23 de Enero 2022, 01:01 »
Ah.., pues ya he descubierto como hacer que el scroll del area de texto vaya siguiendo las líneas publicadas.

Básicamente, es calcular el tamaño del area de texto tras cada línea publicada y setear el view position del scroll al final del todo.

Hay que cambiar la clase PanelTexto, poniendo el JScrollPane como atributo y añadiendo unas líneas de código al método de añadir nueva línea.

Citar
public class PanelTexto extends JPanel {
   
   private JTextArea areaTexto;
   private JButton btLuchar;
   private JScrollPane scrollArea;
   
   public PanelTexto() {
      areaTexto = new JTextArea();
      areaTexto.setEditable(false);
      btLuchar = new JButton("¡Luchar!");
      
      scrollArea = new JScrollPane();
      scrollArea.setViewportView(areaTexto);
      scrollArea.setPreferredSize(new Dimension(50, 200));
      scrollArea.setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createEmptyBorder(10, 20, 20, 20),
            BorderFactory.createLoweredSoftBevelBorder()));
      
      JPanel pnBoton = new JPanel();
      pnBoton.add(btLuchar);
      
      setLayout(new BorderLayout());
      add(pnBoton, BorderLayout.NORTH);
      add(scrollArea, BorderLayout.CENTER);
      setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createEmptyBorder(0, 10, 10, 10),
            BorderFactory.createRaisedSoftBevelBorder()));
   }
   
   //La accion de este botón se escribirá en otra clase
   public void setAccionBotonLuchar(ActionListener accion) {
      btLuchar.addActionListener(accion);
   }
   
   public void nuevaLinea(String linea) {
      areaTexto.append(linea + "\n");
      Dimension dim = areaTexto.getSize();
      Point p = new Point(0, dim.height);
      scrollArea.getViewport().setViewPosition(p);

   }

}
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #23 en: 24 de Enero 2022, 19:53 »
Muchas gracias por todo de verdad, creo que lo he entendido peeero voy a darle unas cuantas vueltas e intentar hacer pequeños proyectos para asentar lo que me has enseñado porque así visto lo entiendo pero cuando empiece a hacer un nuevo proyecto será cuando me de cuenta de si de verdad lo he comprendido.
De nuevo muchísimas gracias  :) :)

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 983
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #24 en: 25 de Enero 2022, 00:00 »
Lo más "difícil" es encontrar la forma óptima de comunicar los objetos de unas clases con otras.
- "Donde pongo este ArrayList, que ha de recibir datos de los campos de texto de una clase JPanel, cuando se pulse el botón de otra clase JPanel y luego enviarlos a una tabla que está en otra clase..."

Pero ya hemos visto que se pueden poner referencias allá donde necesitemos.
Si hay tres o cuatro clases JPanel que necesitan interactuar con un objeto declarado en la clase principal, pues a esos JPanel les ponemos un atributo que haga referencia a ese objeto y listo.
Luego esa referencia se la hacemos llegar por constructor, o incluso por un método setter si hace falta.

Porque eso no va a implicar que hayan cuatro objetos distintos, solo habrá uno, pero con varias referencias apuntando hacia el mismo objeto, para que todo el que lo necesite pueda trabajar con él.

Un saludo.

NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #25 en: 06 de Febrero 2022, 20:47 »
Buenas noches de nuevo, sigo estudiando y quería perfeccionar el programa intentando meter alguna excepción (es un tema que me está costando bastante entender), pero lo estoy haciendo mal, este es el código que he hecho en el ActionListener de "AccionCrearSoldado":

Código: [Seleccionar]
            if (jtVida.getText().isEmpty() || jtArmadura.getText().isEmpty()) {
                JOptionPane.showMessageDialog(null, "El campo no puede estar vacío");
                try {
                    Integer valor = Integer.parseInt(jtVida.getText());
                    Integer valor2 = Integer.parseInt(jtArmadura.getText());
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(null, "El valor tiene que ser numérico");
                }
            }

He estado mirando un post reciente pero no consigo ver el error que estoy cometiendo.

De nuevo muchísimas gracias por cualquier ayuda que me puedas indicar.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 983
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #26 en: 07 de Febrero 2022, 00:52 »
Bueno, yo ahí lo que veo "raro" es que, tras confirmarse que los campos están vacíos, igualmente intentas parsearlos a Integer. Esto último habría que hacerlo, en el caso de confirmar que NO están vacíos.
Citar
            if (jtVida.getText().isEmpty() || jtArmadura.getText().isEmpty())
                JOptionPane.showMessageDialog(null, "El campo no puede estar vacío");
            else { //Ningún campo está vacío, intentamos el parseo
                try {
                    Integer valor = Integer.parseInt(jtVida.getText());
                    Integer valor2 = Integer.parseInt(jtArmadura.getText());
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(null, "El valor tiene que ser numérico");
                }
            }
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #27 en: 07 de Febrero 2022, 20:37 »
Hola de nuevo, perdona pero no entiendo bien entonces la estructura, porque ¿no habría que meter el parseo en el "try"? Adjunto el error que me aparecía y que no puse en el anterior mensaje, por lo que veo viene del parseo pero no veo como arreglarlo:

Citar
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: ""
   at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
   at java.lang.Integer.parseInt(Integer.java:592)
   at java.lang.Integer.parseInt(Integer.java:615)


Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 983
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #28 en: 08 de Febrero 2022, 01:02 »
El parseo está bien en dentro del try.
Lo que no estába bien, es el try dentro de la condición de ese if.

Este era tu código:
Código: [Seleccionar]
            if (jtVida.getText().isEmpty() || jtArmadura.getText().isEmpty()) {
                JOptionPane.showMessageDialog(null, "El campo no puede estar vacío");
                try {
                    Integer valor = Integer.parseInt(jtVida.getText());
                    Integer valor2 = Integer.parseInt(jtArmadura.getText());
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(null, "El valor tiene que ser numérico");
                }
            }

Ahí estas diciendo:
-Si el campo vida o el campo armadura están vacíos, alerto de que no deben estarlo y a continuación intento el parseo.
Y ese es el error, intentar el parseo cuando YA SABES que uno (o ambos) de los campos está vacío.

Por eso te da un error indicando que no se puede parsear una cadena vacía (que proviene de un campo vacío)
Citar
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: ""
   at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
   at java.lang.Integer.parseInt(Integer.java:592)
   at java.lang.Integer.parseInt(Integer.java:615)


En el código que yo propuse, a ese if le añado un else, que será donde intentaremos el parseo.
Citar
            if (jtVida.getText().isEmpty() || jtArmadura.getText().isEmpty())
                JOptionPane.showMessageDialog(null, "El campo no puede estar vacío");
            else { //Ningún campo está vacío, intentamos el parseo
                try {
                    Integer valor = Integer.parseInt(jtVida.getText());
                    Integer valor2 = Integer.parseInt(jtArmadura.getText());
                } catch (NumberFormatException ex) {
                    JOptionPane.showMessageDialog(null, "El valor tiene que ser numérico");
                }
            }

Ahí, estoy diciendo:

-Si el campo vida o el campo armadura están vacíos, alerto de que no deben estarlo(y no hacemos nada más, el usuario tendrá que corregir los campos y volver a pulsar el botón)
Si no, es decir, cuando ambos campos contienen algún texto, entonces intentamos el parseo



Espero haberme explicado mejor ahora.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #29 en: 08 de Febrero 2022, 07:06 »
Aaah vale estaba mezclándolo, ahora si lo entiendo gracias!!!
Otra pregunta en relación a las excepciones yo la he implementado en el accionlistener de AccionCrearSoldado indicando throws, ¿sería mejor "sacarla" fuera y hacerla en un método aparte?

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 983
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #30 en: 08 de Febrero 2022, 11:29 »
Un ActionListener es algo que ocurre durante un evento concreto (cuando se pulsa un determinado botón).
Es un proceso individual, muy corto y que suele estar al margen del flujo general del programa.

Así que yo ni le pondría throws ni nada.
Las excepciones que se tengan que controlar, las trataría todas dentro del ActionListener, no hay necesidad de "lanzarlas" hacia fuera.

Las excepciones que puedan ocurrir durante la creación de un soldado, son problemas que tendrán que resolverse durante ese mismo proceso.
Al resto de procesos del programa les va a dar igual, no es asunto de ellos.

Si la acción crear soldado les dice (hace un throw) a los demás procesos:
-Eh tíos, no he podido crear un soldado, porque el usuario se divierte escribiendo palabrotas en lugar de darme números enteros para la vida y la armadura

Los demás procesos se encogerán de hombros porque no les afecta a ellos para poder hacer sus cosas.
Por ejemplo el proceso que actualiza el JList le diría:
- ¿Y a mí que me cuentas? Mira, si logras crear un soldado, actualizaré el JList de la interfaz para mostrarlo.
Y si no lo consigues, pues casi mejor porque así no tendré que hacer nada y seguiré rascándome el ombligo.
Así que tus problemas son cosa tuya...


Bueno, con esta dramatización  ;D lo que vengo a decir es que la acción de crear un soldado, es una subtarea independiente, así que las excepciones no es necesario lanzarlas a ningún sitio. Se tratan de manera interna con try catch y ya está.

Sería distinto si por ejemplo hubiera otro proceso que se ha quedado detenido expresamente a la espera de la creación de un soldado.
Aquí si podríamos hacer un throw porque este proceso, para poder continuar, necesitaría o bien recibir un soldado, o si no una excepción para saber que no ha sido posible y ya entonces su flujo irá por un lado o por otro.

Resumiendo, al tratar excepciones no siempre es necesario lanzarlas con throw.
Las lanzaremos o no, según si podemos (o queremos, va un poco a gusto de cada uno), resolverlas de forma interna al proceso donde se han generado o bien resolverlas de forma externa, mediante un proceso ajeno al que ha "sufrido" la excepción.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #31 en: 10 de Febrero 2022, 22:19 »
¡Muchísimas gracias! La verdad que con la dramatización lo he entendido mucho mejor  ;D

Un saludo.

Giaan46

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 3
    • Ver Perfil
buenas ! estoy haciendo el mismo proyecto, le quise implementar un fondo con unos iconos que representen a los personajes para hacerlo un poco mas llamativo, el problema que tengo es que no puedo hacer que a medida que se creen los personaje se pinten en el jpanel con el fondo, dejo mi codigo aver si me podrian dar una mano , desde ya muchas gracias!
Código: [Seleccionar]
package Game.tokio.Grafic;

import Game.tokio.Ejercito;
import Game.tokio.Personajes.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;


public class PanelCrearPersonaje extends JPanel {

    private JTextField jtNombre;
    private JComboBox<String> jcTipo;

    private JTextField jtVida;
    private JTextField jtArmadura;
    private JButton btaniadir;

    //Atributo para referenciar alguno de los ejercitos de la clase main
    private Ejercito ejercito;

    //Panel con la lista de personajes
    private PanelLista lista;
    private CampoDeBatalla campoDeBatalla;


    public PanelCrearPersonaje(String titulo, String[] tipos, Ejercito ejercito) {
        // inicializamos componentes
        jtNombre = new JTextField();
        jcTipo = new JComboBox<String>(tipos);
        jtVida = new JTextField();
        jtArmadura = new JTextField();
        btaniadir = new JButton("añadir");
        btaniadir.addActionListener(new AccionCrearPersonaje());

        this.ejercito = ejercito; // Ejercito referenciado
        lista = new PanelLista(titulo, ejercito);//pasamos titulo de borde y referencia a ejercito
        this.campoDeBatalla = campoDeBatalla;

        // layout de "cajas" verticales
        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

//       cada "caja" apilada verticalmente sera un panel de la clase panleConLabel
        add(new PanelConLabel("Nombre: ", jtNombre));
        add(new PanelConLabel("Tipo: ", jcTipo));
        add(new PanelConLabel("Vida: ", jtVida));
        add(new PanelConLabel("Armadura: ", jtArmadura));

        // Boton añadir
        JPanel pnAniadir = new JPanel();
        pnAniadir.add(btaniadir);
        add(pnAniadir);

        //colocamos el panel lista debajo de todo
        add(lista);

        // combinamos dos bordes, uno titulador y otro vacio para crear algo de relleno
        setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createEmptyBorder(15, 15, 15, 15),
                BorderFactory.createTitledBorder(titulo)));
    }
    private class PanelConLabel extends JPanel {

        public PanelConLabel(String texto, JComponent componente) {
            //Layout tipo grilla, sera una fila con dos columnas
            setLayout(new GridLayout(1, 2));
            // En la primera columna, la etiqueta
            add(new JLabel(texto));
            // En la segunda columna, el componente que acompaña la etiqueta
            add(componente);
            // inflamos el panel con aldo de borde vacio
            setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        }
    }
    public class AccionCrearPersonaje implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            // Recogemos datos
            String nombre = jtNombre.getText();
            String tipo = (String) jcTipo.getSelectedItem();
            int vida = Integer.parseInt(jtVida.getText());
            int armadura = Integer.parseInt(jtArmadura.getText());

            // Creamos nuevo personaje segun tipo
            switch (tipo) {
                case "Elfo":
                    ejercito.addPersonaje(new Elfos(nombre, vida, armadura));
                    repaint();
                    revalidate();
                    break;
                case "Humano":
                    ejercito.addPersonaje(new Humanos(nombre, vida, armadura));
                    repaint();
                    revalidate();
                    break;
                case "Hobbit":
                    ejercito.addPersonaje(new Hobbits(nombre, vida, armadura));
                    repaint();
                    revalidate();
                    break;
                case "Trasgo":
                    ejercito.addPersonaje(new Trasgos(nombre, vida, armadura));
                    repaint();
                    revalidate();
                    break;
                case "Orco":
                    ejercito.addPersonaje(new Orcos(nombre, vida, armadura));
                    repaint();
                    revalidate();
                    break;
            }
            // Limpiamos campos
            jtNombre.setText(null);
            jcTipo.setSelectedIndex(0);
            jtVida.setText(null);
            jtArmadura.setText(null);


            //Actualiza lista
            lista.actualizarLista();
            campoDeBatalla.repaint();
            campoDeBatalla.revalidate();
        }

    }
}
Código: [Seleccionar]
package Game.tokio.Grafic;

import Game.tokio.Ejercito;
import Game.tokio.Personajes.*;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;


public class CampoDeBatalla extends JPanel {
    private BufferedImage backgroundImage;
    private Ejercito heroes;
    private Ejercito bestias;
    private BufferedImage elfo;
    private BufferedImage humanos;
    private BufferedImage hobbits;
    private BufferedImage trasgo;
    private BufferedImage orco;
    Ejercito ejercito;
    String nombre;
    PanelLista lista;
    private Personaje personaje;

    public CampoDeBatalla() {
        heroes = new Ejercito();
        bestias = new Ejercito();
        this.elfo = elfo;
        this.humanos = humanos;
        this.orco = orco;
        this.trasgo = trasgo;
        this.hobbits = hobbits;

        ejercito = new Ejercito();

        lista =  new PanelLista(nombre,ejercito);

        setBackground(Color.white);

        try {
            backgroundImage = ImageIO.read(new File("/Users/gian/IdeaProjects/ProyectoFinal_GianlucaMaida/src/Game/tokio/Multimedia/FondoJuego.png"));
            elfo = ImageIO.read(new File("/Users/gian/IdeaProjects/ProyectoFinal_GianlucaMaida/src/Game/tokio/Multimedia/elfo.PNG"));
            humanos = ImageIO.read(new File("/Users/gian/IdeaProjects/ProyectoFinal_GianlucaMaida/src/Game/tokio/Multimedia/humano1.PNG"));
            hobbits = ImageIO.read(new File("/Users/gian/IdeaProjects/ProyectoFinal_GianlucaMaida/src/Game/tokio/Multimedia/hobbit.PNG"));
            trasgo = ImageIO.read(new File("/Users/gian/IdeaProjects/ProyectoFinal_GianlucaMaida/src/Game/tokio/Multimedia/trasgo.PNG"));
            orco = ImageIO.read(new File("/Users/gian/IdeaProjects/ProyectoFinal_GianlucaMaida/src/Game/tokio/Multimedia/orco1.PNG"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // Dibuja el fondo
        g.drawImage(backgroundImage, 0, 0, 750, 520, null);
        //  Dibuja los personajes

        for (int i = 0; i < ejercito.personajes.size(); i++) {
            if (ejercito.getPersonaje(i) instanceof Elfos) {
                g.drawImage(elfo, 300, 100, null);
                setOpaque(false);
                revalidate();
                repaint();
            } else if (ejercito.getPersonaje(i)instanceof Humanos) {
                g.drawImage(humanos, 300, 200, null);
                setOpaque(false);
                revalidate();
                repaint();
            } else if (ejercito.getPersonaje(i)instanceof Hobbits) {
                g.drawImage(hobbits, 300, 300, null);
                revalidate();
                repaint();
            } else if (ejercito.getPersonaje(i)instanceof Orcos) {
                g.drawImage(orco, 350, 200, null);
                revalidate();
                repaint();
            } else if (ejercito.getPersonaje(i)instanceof Trasgos) {
                g.drawImage(trasgo, 350, 300, null);
                revalidate();
                repaint();

            }

        }
        setOpaque(false);
        revalidate();
        repaint();
    }
}

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".