Autor Tema: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO  (Leído 562 veces)

Weirdo

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 4
    • Ver Perfil
Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« en: 08 de Febrero 2021, 17:54 »
Hola, he estado tratando de hacer un código simple en java sobre una batalla pokemon entre 2 pokemon para practicar como funcionan las clases y objetos pero al final no he entendido.
El problema es programar una batalla automática entre 2 pokemones hasta que uno de los 2 se quede sin puntos de vida. El daño inflingido debe ser aleatorio entre 10 y 20 puntos. Debe haber un 10% de probabilidad de hacer un daño crítico de 150% del daño inicial.
Seria de mucha ayuda si me pueden dar una idea o mostrando un código para guiarme de como hacerlo, igual gracias de  todos modos :D
« Última modificación: 08 de Febrero 2021, 17:55 por Weirdo »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 574
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #1 en: 09 de Febrero 2021, 00:49 »
Hola.
Primero quiero destacar que soy un ignorante absoluto del mundo Pokemon, así que he hecho un programa treméndamente simple je je..

He creado una clase Pokemon con dos atributos, nombre (String) y vida (int).
El valor de estos atributos se establece mediante el constructor.

Hay un tercer atributo que es simplemente un objeto Random para generar valores de ataque al azar.

Uno de los métodos que tiene, es el método atacar() que retornará un valor int que representa el daño conseguido en su turno de ataque.

Este método primero genera un valor aleatorio entre 10 y 20.
Luego, le pregunta a otro método de tipo boolean si este ataque es crítico o no.
Si es crítico, informa en pantalla e incrementa el valor de ataque en un 150%.
Por último, informa en pantalla del valor de ataque final (se haya incrementado o no) y retorna este valor.

¿Y cómo decidimos si este ataque es crítico o no?
El enunciado dice que hay un 10% de probabilidades.
Bien, las probabilidades (o las matemáticas en general) no son lo mío así que no se cuál es la forma más óptima de generar esas probabilidades.

A mí lo que se me ha ocurrido es generar un valor int entre 0 y 100. Si el valor generado es múltiplo de 10, pues retorno true, de lo contrario retorno false.
¿Y por qué hacerlo así?
Porque entre 0 y 100 solo hay diez valores que sean múltiplos de 10, es decir, hay un 10% que son múltiplos de 10 y un 90% que no lo son.
Por lo tanto, si hemos obtenido al azar un múltiplo de 10, pues es lo más parecido a generar una probabilidad del 10%....

Hay otro método llamado perderVida(), que simplemente resta al nivel de vida el valor de ataque generador por un Pokemon rival.

Luego hay un par de getters y un método toString(), en fin, como he dicho, es muy simple y no necesita mayores explicaciones.
Esta sería la clase Pokemon:
Código: [Seleccionar]
public class Pokemon {

private String nombre;
private int vida;
private Random azar = new Random();

public Pokemon(String nombrePok, int vidaPok) {
nombre = nombrePok;
vida = vidaPok;
}

public String getNombre() {
return nombre;
}

public int getVida() {
return vida;
}

public int atacar() {

int ataque = azar.nextInt(11) + 10;

if (esCritico()) {
System.out.println("¡" + nombre + " ha conseguido un ataque crítico!");
ataque =  (int)(ataque * 1.5);
}

System.out.println("Daño conseguido: " + ataque + " puntos");
return ataque;
}

public void perderVida(int ataque) {
vida -= ataque;
}

private boolean esCritico() {
int valor = azar.nextInt(100);
/*
* Entre 0 y 100 hay diez valores que son
* múltiplos de 10.
* Es decir, hay un 10% de posibilidades
* de obtener un valor múltiplo de 10.
*/

return valor%10 == 0;
}

@Override
public String toString() {
return "Nombre: " + nombre + "\nVida: " + vida;
}

}


Luego, para el combate, en la clase principal lo que hago es declarar dos objetos Pokemon e inicio un bucle while que se va a repetir mientras ambos contendientes tengan un nivel de vida superior a 0.
Si alguno pierde toda su vida, el bucle se detiene (el combate finaliza)

Dentro del bucle, pues voy informando de los datos de los contendientes y hay dos turnos de ataque, uno para cada Pokemon.

Cada vez que uno ataca, compruebo si el otro sigue vivo o no. Si sigue vivo, pasamos al turno en el que a este le toca atacar.
Si ha muerto, entonces no hay más turnos y todo termina.

Para poder ir leyendo en pantalla tranquilamente que ocurre en cada turno, utilizo un método llamado pausa() donde simplemente uso un Scanner para que el programa se detenga hasta que el usuario pulse la tecla enter.

Código: [Seleccionar]
public class Batalla {

private static Scanner teclado = new Scanner(System.in);

public static void main(String[] args) {

Pokemon pok1 = new Pokemon("Bulbasur", 100);
Pokemon pok2 = new Pokemon("Pikachu", 100);


while (pok1.getVida() > 0 && pok2.getVida() > 0) {
System.out.println("\nContendientes:");
System.out.println(pok1);
System.out.println(pok2);
pausa();
System.out.println("Turno de ataque para " + pok1.getNombre());
pok2.perderVida(pok1.atacar());
pausa();
if (pok2.getVida() <= 0) {
System.out.println(pok2.getNombre() + "ha sido derrotado.\n");
}
else {
//Turno del segundo pokemon
System.out.println("\nContendientes:");
System.out.println(pok1);
System.out.println(pok2);
pausa();
System.out.println("Turno de ataque para " + pok2.getNombre());
pok1.perderVida(pok2.atacar());
pausa();
if (pok1.getVida() <= 0)
System.out.println(pok1.getNombre() + " ha sido derrotado.\n");
}
}

System.out.println("\n\n\t\tFIN DEL JUEGO");


}

private static void pausa() {
System.out.println("\n\t\tPULSA ENTER PARA CONTINUAR\n");
teclado.nextLine();
}

}

Si lo ejecutamos, en pantalla podemos ver algo parecido a un combate por turnos:
Citar
Contendientes:
Nombre: Bulbasur
Vida: 100
Nombre: Pikachu
Vida: 100

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 10 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 100
Nombre: Pikachu
Vida: 90

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 85
Nombre: Pikachu
Vida: 90

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 17 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 85
Nombre: Pikachu
Vida: 73

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 12 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 73
Nombre: Pikachu
Vida: 73

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
¡Bulbasur ha conseguido un ataque crítico!
Daño conseguido: 27 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 73
Nombre: Pikachu
Vida: 46

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 58
Nombre: Pikachu
Vida: 46

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 20 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 58
Nombre: Pikachu
Vida: 26

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 43
Nombre: Pikachu
Vida: 26

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 15 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 43
Nombre: Pikachu
Vida: 11

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Pikachu
Daño conseguido: 14 puntos

      PULSA ENTER PARA CONTINUAR



Contendientes:
Nombre: Bulbasur
Vida: 29
Nombre: Pikachu
Vida: 11

      PULSA ENTER PARA CONTINUAR


Turno de ataque para Bulbasur
Daño conseguido: 12 puntos

      PULSA ENTER PARA CONTINUAR


Pikachu ha sido derrotado.



      FIN DEL JUEGO
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

Weirdo

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 4
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #2 en: 09 de Febrero 2021, 14:33 »
Hola, muchas gracias por el código me sirvió como guía para seguir aprendiendo a programar.
Disculpe que vuelva a molestar con otra duda pero como pasaría este código a gráficamente, no hace mucho uno de mis retos fue crear una calculadora gráficamente en java NetBeans POO usando JFrame. No sé si se llega a entender lo que trato de decir pero espero que si, igual muchas gracias por todo  :).

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 574
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #3 en: 10 de Febrero 2021, 03:01 »
Hola.
Para pasarlo a interfaz gráfica necesitas manejarte con la librería Swing de Java.
Y para manejarte bien con ella conviene tener dominado el tema de POO.

La librería Swing no es difícil, pero sí extensa y se necesita practicar mucho con ella. No es algo que se pueda explicar en un par de mensajes en el foro.
Hay que buscarse un tutorial, practicar, y sobre todo.. equivocarse mucho y atascarse mucho... porque es cuando más se aprende.

No se si tu intento con NetBeans fue usando el asistente para crear interfaces, que te lo facilita mucho (demasiado) simplemente arrastrando los componentes (botones, campos de texto,..) que quieres mostrar.
No recomiendo aprender Swing usando este tipo de asistentes. Son útiles para ahorrar tiempo cuando YA se ha aprendido y se sabe lo que se está haciendo.

Pero no para aprender, porque NetBeans te va generando montones de código que un humano no entiende... y al final tienes un programa de montones de líneas que no ha escrito uno mismo.
Yo de hecho nunca uso estos asistentes, aunque bueno, no me dedico a esto. Soy otro aprendiz que en sus (escasos) ratos libres aprende Java por ocio, por diversión....

He creado un nuevo proyecto con una interfaz Swing. Es un programa también sencillo y con código escrito rapidito (se me hace muy tarde en la noche y mañana trabajo  :o )

Usa la misma clase Pokemon que puse antes, con algunas modificaciones.
La interfaz es muy simple, he puesto algunos colores y distintos tipos de bordes para los paneles, para que sirvan un poco como ejemplo de uso.
No es muy bonito, pero bueno..



Aunque por mucho que leas y mires el código, no se si servirá para aprender.
Para aprender Swing (o JavaFX, o lo que sea...), hay que empezar desde el principio, con ejercicios sencillos.

Dejo este enlace a Google Drive para descargar el proyecto en un archivo zip. El código también se puede copiar y pegar desde este mismo hilo (ver más abajo).

Mañana si puedo, intentaré dar algunas explicaciones del código.
Un saludo.
« Última modificación: 11 de Febrero 2021, 20:33 por Mario R. Rancel »
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: 574
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #4 en: 11 de Febrero 2021, 01:26 »
A ver, algunas explicaciones.

La clase Pokemon es básicamente la misma, con unos ligeros cambios.
He añadido dos atributos un String llamado mensaje y un boolean llamado suTurno

Antes cuando un Pokemon atacaba, mostraba en consola el daño producido y si era un ataque crítico.
Al pasar a interfaz gráfica ya no usamos la consola, pero esos mensajes nos siguen interesando obtenerlos para mostrarlos en la interfaz.
Así que ahora en lugar de imprimirlos en consola, tras cada ataque se compone un String nuevo que queda guardado en ese atributo.
Así tras la acción de atacar, podemos recuperar ese mensaje y enviarlo al panel superior de la interfaz que funciona como un "display" de mensajes.

El otro atributo, el boolean suTurno, es para que cada objeto Pokemon sepa decirnos si es su turno de ataque o no.
Antes, en modo consola, el turno estaba establecido directamente en el flujo de instrucciones del programa. Cada vez que se repetía el bucle while, atacaba el primer Pokemon y luego el segundo, si es que seguía vivo.
Ahora con la interfaz gráfica, no hay ningún bucle ni flujo de instrucciones que ejecuten los ataques.

Los ataques los produce el usuario pulsando los botones de cada Pokemon. Para garantizar que haya turnos, solo uno de los dos botones está activado. Cuando se pulsa un botón para atacar, este se desactiva y se activa el correspondiente al otro Pokemon para que haya "cambio de turno".

Para que la interfaz sepa en todo momento que botón ha de activar y cuál desactivar, hay que buscar una forma de hacérselo saber. Y se me ocurrió hacer que cada Pokemon tenga un boolean que cambie de true a false cuando ataque y a la inversa cuando reciba daño.
Así cada Pokemon puede decirle a su botón de ataque asociado si ha de estar activo o no.

Y eso es lo único que ha cambiado en la clase Pokemon:

Código: [Seleccionar]
public class Pokemon {

private String nombre;
private int vida;
private Random azar = new Random();

public String mensaje; //Mensaje que se genera tras un ataque
public boolean suTurno; //Indica si es el turno de ataque de este Pokemon

public Pokemon(String nombrePok, int vidaPok, boolean turno) {
nombre = nombrePok;
vida = vidaPok;
mensaje = "";
suTurno = turno;
}

public String getNombre() {
return nombre;
}

public int getVida() {
return vida;
}

public void setVida(int v) {
vida = v;
}

public int atacar() {

mensaje = ""; //Se generará un nuevo mensaje
int ataque = azar.nextInt(11) + 10;

if (esCritico()) {
mensaje += "¡Ataque crítico! ";
ataque =  (int)(ataque * 1.5);
}

mensaje += "Daño: " + ataque + " puntos";
return ataque;
}

public void perderVida(int ataque) {
vida -= ataque;
}

private boolean esCritico() {
int valor = azar.nextInt(100);
/*
* Entre 0 y 100 hay diez valores que son
* múltiplos de 10.
* Es decir, hay un 10% de posibilidades
* de obtener un valor múltiplo de 10.
*/

return valor%10 == 0;
}

@Override
public String toString() {
return "Nombre: " + nombre + "\nVida: " + vida;
}

}

Sobre la interfaz.
Es un JFrame compuesto básicamente de 4 paneles.
Cada panel proviene de una clase que hereda de JPanel (por eso es importante manejar bien POO para sacar partido a Swing)


En la parte superior tenemos un panel de la clase PanelMensajes que simplemente contiene un JLabel (etiqueta) para ir mostrando los mensajes que cada Pokemon genera al atacar.
Se adorna con algunos colores y mayor tamaño de fuente para que los mensajes destaquen más.
La clase tiene un método para recibir y mostrar dichos mensajes.

Código: [Seleccionar]
public class PanelMensajes extends JPanel{

private JLabel mensajes;

public PanelMensajes() {
JPanel display = new JPanel();
mensajes = new JLabel("¡Comienza la batalla!");
mensajes.setFont(new Font("Calibri", Font.PLAIN, 38));
mensajes.setForeground(new Color(243, 81, 55 ));
display.add(mensajes);

setLayout(new GridLayout(1, 1));
add(display);
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15),
BorderFactory.createEtchedBorder(EtchedBorder.RAISED)));
display.setBackground(new Color(230, 234, 160 ));
}

/*
* Recibe y muestra los mensajes que los Pokemon generan
* en cada ataque.
*/
public void setMensaje(String mensaje) {
mensajes.setText(mensaje);
}

}

Debajo de este panel superior, tenemos la parte central del JFrame, que es un panel compuesto de tres subpaneles.

Justo en el centro esta un panel de la clase PanelVS, que es tan simple que en realidad no valía la pena escribir una clase separada. Se podría haber compuesto directamente en la clase principal.
Pero bueno, nunca está de más modular el código.
Este panel simplemente muestra una imagen png que descargue de Internet, con las letras vs (versus).
Es tan simple como esto:
Código: [Seleccionar]
public class PanelVS extends JPanel{

public PanelVS() {
ImageIcon imagen = new ImageIcon(PanelVS.class.getClassLoader().getResource("img/vs.png"));
JLabel icono = new JLabel(imagen);
add(icono);
}

}

Como decía, este es el panel que está en medio de los tres paneles centrales.

Los otros dos paneles, uno a cada lado, provienen de la clase PanelPokemon, que ya sí es una clase más elaborada.
Este panel está a su vez compuesto de otros cuatro paneles dispuestos verticalmente.

El primer panel muestra el nombre del Pokemon al que está asociado.

El segundo panel muestra la foto del Pokemon.

El tercer panel contiene la barra de vida, que es un JProgressBar que se va reduciendo según el Pokemon asociado va perdiendo puntos de vida.

El cuarto y último panel contiene el botón para realizar ataques. Este botón es el que antes dijimos que se activa/desactiva según el Pokemon asociado informa de si es su turno de ataque o no.

Para que este botón funcione, hay que añadirle un ActionListener, una clase con el código que decide que ha de ocurrir cuando se pulse el botón.

Este ActionListener va a provenir de la clase principal y se lo haremos llegar mediante el método agregarAccion()

Código: [Seleccionar]
public class PanelPokemon extends JPanel{

private Pokemon pokemon; //Referencia al objeto Pokemon asociado a este panel
private JButton btAtacar;
private JProgressBar barraVida;

public PanelPokemon(Pokemon pok, String rutaImagen) {

pokemon = pok;

setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new PanelNombre());
add(new PanelImagen(rutaImagen));
add(new PanelVida());
add(new PanelBoton());

}

private class PanelNombre extends JPanel {

public PanelNombre() {
JLabel nombre = new JLabel(pokemon.getNombre());
nombre.setFont(new Font("Consolas", Font.BOLD, 34));
nombre.setForeground(Color.WHITE);
add(nombre);
setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.WHITE, Color.BLACK));
setBackground(new Color(23, 151, 238));
}
}

private class PanelImagen extends JPanel {

public PanelImagen(String rutaImagen) {
ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(rutaImagen));
JLabel icono = new JLabel(imagen);
icono.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(icono);
}
}

private class PanelVida extends JPanel {

public PanelVida() {

barraVida = new JProgressBar();
barraVida.setMaximum(pokemon.getVida());
barraVida.setValue(pokemon.getVida());
barraVida.setPreferredSize(new Dimension(200, 30));
barraVida.setForeground(Color.BLUE);
add(barraVida);
barraVida.setBorder(BorderFactory.createRaisedSoftBevelBorder());

TitledBorder bordeTitulado = new TitledBorder("Nivel de Vida");
bordeTitulado.setTitleFont(new Font("Consolas", Font.ITALIC, 24));
setBorder(bordeTitulado);
}
}

private class PanelBoton extends JPanel {

public PanelBoton() {
btAtacar = new JButton("¡ATACAR!");
btAtacar.setFont(new Font("Consolas", Font.BOLD, 30));
add(btAtacar);
setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 30));
}
}

public void actualizarPanel() {
barraVida.setValue(pokemon.getVida());
btAtacar.setEnabled(pokemon.suTurno);
}

public void agregarAccion(ActionListener accion) {
btAtacar.addActionListener(accion);
}

}


¿Y por qué esa clase ActionListener la vamos a escribir en la clase principal?
Si el botón "ATACAR" está dentro de la clase PanelPokemon, ¿por qué no escribimos ahí su correspondiente código de acción?

Por un problema de "ámbitos". Una de las cosas que quiero que ocurran cuando se ataquen los Pokemon, es que el panel de mensajes reciba y muestre los mensajes que generan los Pokemon.
Pero este panel de mensajes, es una clase llamada PanelMensajes que está escrita separada de la clase PanelPokemon. Esto implica que están en ámbitos distintos, y desde PanelPokemon, no voy a tener visibilidad para actuar sobre PanelMensajes

En cambio, la clase principal, la que va a modelar el JFrame, va a contener dentro de su ámbito a TODAS las otras clases paneles.
Desde ahí si que no voy a tener problemas de visibilidad para hacer que unos paneles interactúen con otros.
Así que puedo escribir ahí la clase ActionListener y luego hacérsela llegar a sendos botones de "ATACAR".


La clase principal, llamada BatallaSwing tiene como atributos los dos Pokemon y referencias a los paneles que componen la interfaz.
Excepto para la clase PanelVS, que como está de puro adorno y no se interactúa con ella durante el programa, no necesita estar referenciada con ningún atributo.

En esta clase podemos ver el código del ActionListener, que viene a ser el "motor" del juego.
Cuando se pulsa un botón de ataque, se pregunta a los Pokemon de quién es el turno (se podría preguntar qué botón ha sido pulsado, pero es más rápido consultar el atributo de los Pokemon).
Entonces el Pokemon que posee el turno ataca al otro.
La interfaz se actualiza con mensaje arriba y las barras de vida.
Y se comprueba si ambos Pokemon siguen con vida.
Si alguno ha muerto, se informa del final del juego con un JOptionPane, que al aceptarlo, se reinicia todo a valores por defecto y el juego comienza de nuevo.

Código: [Seleccionar]
public class BatallaSwing extends JFrame{

private Pokemon pok1;
private Pokemon pok2;
private PanelMensajes pnMensajes;
private PanelPokemon pnPok1;
private PanelPokemon pnPok2;

public BatallaSwing() {

pok1 = new Pokemon("Pikachu", 100, true);
pok2 = new Pokemon("Bulbasaur", 100, false);
pnMensajes = new PanelMensajes();
pnPok1 = new PanelPokemon(pok1, "img/pikachu.png");
pnPok1.actualizarPanel();
pnPok1.agregarAccion(new AccionAtacar());
pnPok2 = new PanelPokemon(pok2, "img/bulbasaur.png");
pnPok2.actualizarPanel();
pnPok2.agregarAccion(new AccionAtacar());

setLayout(new BorderLayout());

add(pnMensajes, BorderLayout.NORTH);
JPanel pnCentro = new JPanel();
pnCentro.add(pnPok1);
pnCentro.add(new PanelVS()); //PanelVS no requiere referencia, no se interactúa con ella.
pnCentro.add(pnPok2);
add(pnCentro, BorderLayout.CENTER);

setTitle("Batalla Pokemon");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

private class AccionAtacar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {

if (pok1.suTurno) {
pok2.perderVida(pok1.atacar());
pnMensajes.setMensaje(pok1.mensaje);
pok1.suTurno = false;
pok2.suTurno = true;
}
else {
pok1.perderVida(pok2.atacar());
pnMensajes.setMensaje(pok2.mensaje);
pok1.suTurno = true;
pok2.suTurno = false;
}

pnPok1.actualizarPanel();
pnPok2.actualizarPanel();

if (pok1.getVida() <= 0 || pok2.getVida() <= 0) {
String mensajeFinal = "Juego Terminado\n Ha ganado: ";
mensajeFinal += pok1.getVida()>0?pok1.getNombre():pok2.getNombre();

JOptionPane.showMessageDialog(null, mensajeFinal, "Fin del Juego",
JOptionPane.INFORMATION_MESSAGE);

//Valores por defecto para volver a jugar
pok1.setVida(100);
pok1.suTurno = true;
pok2.setVida(100);
pok2.suTurno = true;
pnPok1.actualizarPanel();
pnPok2.actualizarPanel();
pnMensajes.setMensaje("¡Comienza la batalla!");
}


}
}

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

}

Y esto es a grandes rasgos el funcionamiento del programa y la composición básica de la interfaz gráfica.
Respecto a esta, no entro a explicar cada detalle ni cada elemento porque sería extenderse mucho y no creo que sirva para aprender. Insisto que solo practicando y experimentando por uno mismo conseguimos familiarizarnos con la librería Swing.

Pero si tienes alguna pregunta concreta, adelante, no te cortes.

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

Mario R. Rancel

  • Administrador
  • Experto
  • ********
  • APR2.COM
  • Mensajes: 1942
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #5 en: 11 de Febrero 2021, 20:36 »
Ya que nuestro 'máster' Kabuto se ha tomado el trabajo de preparar el código (gracias como siempre), me permito subirlo para que pueda ser accedido fácilmente y se mantenga aunque se rompan los links si esto ocurriera.
Si me equivoco en algo corrígeme Kabuto. El proyecto consta de dos packages con diferentes archivos como se indica a continuación:

package batalla: clases BatallaSwing.java (clase con el main) y Pokemon.java
package gui: clases PanelPokemon.java, PanelVS.java y PanelMensajes.java

Para que el código funcione es necesario poner las imágenes en la ruta adecuada y con los nombres adecuados, o bien modificarlo. Las imágenes están referenciadas en las líneas

pnPok1 = new PanelPokemon(pok1, "img/pikachu.png");
pnPok2 = new PanelPokemon(pok2, "img/bulbasaur.png");
ImageIcon imagen = new ImageIcon(PanelVS.class.getClassLoader().getResource("img/vs.png"))


CLASE BatallaSwing

Código: [Seleccionar]
package batalla;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import gui.*;

@SuppressWarnings("serial")
public class BatallaSwing extends JFrame{

    private Pokemon pok1;
    private Pokemon pok2;
    private PanelMensajes pnMensajes;
    private PanelPokemon pnPok1;
    private PanelPokemon pnPok2;

    public BatallaSwing() {

        pok1 = new Pokemon("Pikachu", 100, true);
        pok2 = new Pokemon("Bulbasaur", 100, false);
        pnMensajes = new PanelMensajes();
        pnPok1 = new PanelPokemon(pok1, "img/pikachu.png");
        pnPok1.actualizarPanel();
        pnPok1.agregarAccion(new AccionAtacar());
        pnPok2 = new PanelPokemon(pok2, "img/bulbasaur.png");
        pnPok2.actualizarPanel();
        pnPok2.agregarAccion(new AccionAtacar());

        setLayout(new BorderLayout());

        add(pnMensajes, BorderLayout.NORTH);
        JPanel pnCentro = new JPanel();
        pnCentro.add(pnPok1);
        pnCentro.add(new PanelVS());
        pnCentro.add(pnPok2);
        add(pnCentro, BorderLayout.CENTER);

        setTitle("Batalla Pokemon");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        setLocationRelativeTo(null);
        setVisible(true);
    }

    private class AccionAtacar implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {

            if (pok1.suTurno) {
                pok2.perderVida(pok1.atacar());
                pnMensajes.setMensaje(pok1.mensaje);
                pok1.suTurno = false;
                pok2.suTurno = true;
            }
            else {
                pok1.perderVida(pok2.atacar());
                pnMensajes.setMensaje(pok2.mensaje);
                pok1.suTurno = true;
                pok2.suTurno = false;
            }

            pnPok1.actualizarPanel();
            pnPok2.actualizarPanel();

            if (pok1.getVida() <= 0 || pok2.getVida() <= 0) {
                String mensajeFinal = "Juego Terminado\n Ha ganado: ";
                mensajeFinal += pok1.getVida()>0?pok1.getNombre():pok2.getNombre();

                JOptionPane.showMessageDialog(null, mensajeFinal, "Fin del Juego",
                    JOptionPane.INFORMATION_MESSAGE);

                //Valores por defecto para volver a jugar
                pok1.setVida(100);
                pok1.suTurno = true;
                pok2.setVida(100);
                pok2.suTurno = true;
                pnPok1.actualizarPanel();
                pnPok2.actualizarPanel();
                pnMensajes.setMensaje("¡Comienza la batalla!");
            }

        }
    }

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

}


CLASE Pokemon

Código: [Seleccionar]
package batalla;

import java.util.Random;

public class Pokemon {

private String nombre;
private int vida;
private Random azar = new Random();

public String mensaje; //Mensaje que se genera tras un ataque
public boolean suTurno; //Indica si es el turno de ataque de este Pokemon

public Pokemon(String nombrePok, int vidaPok, boolean turno) {
nombre = nombrePok;
vida = vidaPok;
mensaje = "";
suTurno = turno;
}

public String getNombre() {
return nombre;
}

public int getVida() {
return vida;
}

public void setVida(int v) {
vida = v;
}

public int atacar() {

mensaje = ""; //Se generará un nuevo mensaje
int ataque = azar.nextInt(11) + 10;

if (esCritico()) {
mensaje += "¡Ataque crítico! ";
ataque =  (int)(ataque * 1.5);
}

mensaje += "Daño: " + ataque + " puntos";
return ataque;
}

public void perderVida(int ataque) {
vida -= ataque;
}

private boolean esCritico() {
int valor = azar.nextInt(100);
/*
* Entre 0 y 100 hay diez valores que son
* múltiplos de 10.
* Es decir, hay un 10% de posibilidades
* de obtener un valor múltiplo de 10.
*/

return valor%10 == 0;
}

@Override
public String toString() {
return "Nombre: " + nombre + "\nVida: " + vida;
}

}


CLASE PanelPokemon

Código: [Seleccionar]
package gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.BevelBorder;
import javax.swing.border.TitledBorder;

import batalla.Pokemon;

@SuppressWarnings("serial")
public class PanelPokemon extends JPanel{

    private Pokemon pokemon;
    private JButton btAtacar;
    private JProgressBar barraVida;

    public PanelPokemon(Pokemon pok, String rutaImagen) {

        pokemon = pok;

        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
        add(new PanelNombre());
        add(new PanelImagen(rutaImagen));
        add(new PanelVida());
        add(new PanelBoton());

    }

    private class PanelNombre extends JPanel {

        public PanelNombre() {
            JLabel nombre = new JLabel(pokemon.getNombre());
            nombre.setFont(new Font("Consolas", Font.BOLD, 34));
            nombre.setForeground(Color.WHITE);
            add(nombre);
            setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.WHITE, Color.BLACK));
            setBackground(new Color(23, 151, 238));
        }
    }

    private class PanelImagen extends JPanel {

        public PanelImagen(String rutaImagen) {
            ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(rutaImagen));
            JLabel icono = new JLabel(imagen);
            icono.setBorder(BorderFactory.createLoweredSoftBevelBorder());
            add(icono);
        }
    }

    private class PanelVida extends JPanel {

        public PanelVida() {

            barraVida = new JProgressBar();
            barraVida.setMaximum(pokemon.getVida());
            barraVida.setValue(pokemon.getVida());
            barraVida.setPreferredSize(new Dimension(200, 30));
            barraVida.setForeground(Color.BLUE);
            add(barraVida);
            barraVida.setBorder(BorderFactory.createRaisedSoftBevelBorder());

            TitledBorder bordeTitulado = new TitledBorder("Nivel de Vida");
            bordeTitulado.setTitleFont(new Font("Consolas", Font.ITALIC, 24));
            setBorder(bordeTitulado);
        }
    }

    private class PanelBoton extends JPanel {

        public PanelBoton() {
            btAtacar = new JButton("¡ATACAR!");
            btAtacar.setFont(new Font("Consolas", Font.BOLD, 30));
            add(btAtacar);
            setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 30));
        }
    }

    public void actualizarPanel() {
        barraVida.setValue(pokemon.getVida());
        btAtacar.setEnabled(pokemon.suTurno);
    }

    public void agregarAccion(ActionListener accion) {
        btAtacar.addActionListener(accion);
    }

}


CLASE PanelVS

Código: [Seleccionar]
package gui;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class PanelVS extends JPanel{

    public PanelVS() {
        ImageIcon imagen = new ImageIcon(PanelVS.class.getClassLoader().getResource("img/vs.png"));
        JLabel icono = new JLabel(imagen);
        add(icono);
    }
}


CLASE PanelMensajes

Código: [Seleccionar]
package gui;

import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;

@SuppressWarnings("serial")
public class PanelMensajes extends JPanel{

    private JLabel mensajes;

    public PanelMensajes() {
        JPanel display = new JPanel();
        mensajes = new JLabel("¡Comienza la batalla!");
        mensajes.setFont(new Font("Calibri", Font.PLAIN, 38));
        mensajes.setForeground(new Color(243, 81, 55 ));
        display.add(mensajes);

        setLayout(new GridLayout(1, 1));
        add(display);
        setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10, 15, 10, 15),
                BorderFactory.createEtchedBorder(EtchedBorder.RAISED)));
        display.setBackground(new Color(230, 234, 160 ));
    }

    public void setMensaje(String mensaje) {
        mensajes.setText(mensaje);
    }

}
« Última modificación: 12 de Febrero 2021, 20:50 por Mario R. Rancel »

Weirdo

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 4
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #6 en: 14 de Febrero 2021, 18:11 »
Muchísimas gracias a los dos, siguiendo tu consejo he estado viendo videos donde enseñan lo básico y he estado tratando de aprender todo lo que pueda. Ahora tengo una duda, que programas utilizan para programar y/o que programas debo aprender a usarlos para mejorar como alguien que recién empieza en este mundo de la programación?
Gracias por su tiempo y disculpen por molestarles otra vez  :)

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 574
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #7 en: 14 de Febrero 2021, 20:00 »
En principio sirve cualquier IDE (entorno de desarrollo integrado) como NetBeans, Eclipse o IntelliJ.
Yo uso Eclipse, pero más que nada porque es con el primero que empecé y me siento más a gusto con él.
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

Mario R. Rancel

  • Administrador
  • Experto
  • ********
  • APR2.COM
  • Mensajes: 1942
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #8 en: 15 de Febrero 2021, 19:47 »
Hola Weirdo, no sé el punto de partida en el que te encuentras. Cuando se trata de empezar desde cero recomendamos leer https://aprenderaprogramar.com/foros/index.php?topic=1313.0 y artículos de la sección Cómo Empezar (https://aprenderaprogramar.com/index.php?option=com_content&view=category&layout=blog&id=84&Itemid=18)

Y si se trata de empezar desde cero pero con Java, recomendamos seguir este curso paso a paso: https://aprenderaprogramar.com/index.php?option=com_content&view=category&id=68&Itemid=188

Weirdo

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 4
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #9 en: 23 de Febrero 2021, 16:22 »
Muchas gracias a los dos, he estado usando eclipse y NetBeans últimamente y probando otros programas como BlueJ y jGRASP pero aun voy recién empezando en este mundo, también empecé con el curso que me recomendó Mario. Aunque ya había terminado con esto de la batalla Pokémon, me surgió una duda, que pasaría si existieran 3 en vez de solo 2 pokemones y que se deba elegir solo 2 para batallar entre si y el tercero se deje lado por esa batalla, como se haría eso gráficamente? Igual disculpen otra vez por las molestias pero se agradece mucho lo que hacen por lo demás :).

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 574
    • Ver Perfil
Re: Ayuda, programar una batalla pokemon en JAVA NETBEANS POO
« Respuesta #10 en: 25 de Febrero 2021, 00:46 »
Eso se puede hacer de mil formas diferentes, el límite lo pone la imaginación y los conocimientos sobre Java Swing.

Antes habíamos hecho que el programa comenzase ya con los dos únicos Pokemon listos para pelear.
Si queremos elegir entre tres (o más) habrá que cambiar varias cosas, ya que ahora el programa no puede comenzar listo para el combate.

Hay que dar alguna forma de que el usuario escoja los Pokemon, y como digo, hay mil formas.
Se puede mostrar un JComboBox con los nombres de los Pokemon, o un simple JOptionPane mostrando los nombres disponibles y que el usuario teclee el nombre del Pokemon escogido.

O podemos mostrar un ventana de dialogo con las imágenes de los Pokemon disponibles y que el usuario haga click en el que quiera.

La interfaz puede comenzar como antes, con los dos "paneles Pokemon", pero mostrando de inicio unas "PokeBall" y que el usuario haga click en ellas para escoger un Pokemon para cada panel.



Al clickar una Pokeball, aparece un JDialog, una ventana similar a un JFrame, y mostraría los Pokemon disponibles. Sean tres, sean cuatro, sean los que sean..



Al clickar un Pokemon, queda seleccionado dentro de ese PanelPokemon.
Luego clickamos en la otra Pokeball para elegir el oponente, y ahora ya sí, se puede combatir.



Como digo, para todo esto se requiere hacer varios cambios en lo que ya teníamos escrito, para facilitar el añadido.

Una de las cosas que añadiremos, será un ArrayList que contenga los Pokemon que hay disponibles en el juego.
La clase Pokemon habrá que modificarla. Como ahora habrán varios Pokemon, cada uno con su foto, conviene que cada Pokemon tenga la ruta a su foto correspondiente.
Antes solo teníamos dos Pokemon, así que la ruta a sus fotos la poníamos "a mano" directamente en los PanelesPokemon, porque no había elección posible.
Ahora sí que hay elección, así que no sabemos de antemano que foto mostrarán los PanelesPokemon y lo mejor es que al asignarle un Pokemon a estos paneles, sea este Pokemon quien le diga donde encontrar la foto.

De hecho, le vamos a proporcionar dos fotos a cada Pokemon, una mirando a la izquierda y otra mirando a la derecha. Según a que PanelPokemon se asignado, se mostrará una imagen o la otra, para que en la interfaz los Pokemon siempre queden encarados, mirándose el uno al otro.

También he quitado del constructor el boolean para decidir si es su turno de ataque o no.
Esto antes lo decidíamos en el momento de la creación del Pokemon, por eso estaba en el constructor.
Ahora esto no se decide al construir el Pokemon, se decidirá cuando sea seleccionado desde los PanelesPokemon por el usuario, así que no tiene sentido indicarlo en el constructor.

Marco en negrita los cambios:
Citar
public class Pokemon{
   
   private String nombre;
   private int vida;
   private Random azar = new Random();
   private String imgP1; //Ruta a la imagen cuando es player1
   private String imgP2; //Ruta a la imagen cuando es player2, misma que P1, pero invertida

   
   public String mensaje; //Mensaje que se genera tras un ataque
   public boolean suTurno; //Indica si es el turno de ataque de este Pokemon
   
   public Pokemon(String nombrePok, int vidaPok, String img1, String img2) {
      nombre = nombrePok;
      vida = vidaPok;
      mensaje = "";
      imgP1 = img1;
      imgP2 = img2;

   }
   
   public String getNombre() {
      return nombre;
   }
   
   public int getVida() {
      return vida;
   }
   
   public void setVida(int v) {
      vida = v;
   }
   
   public String getImgP1() {
      return imgP1;
   }


   public String getImgP2() {
      return imgP2;
   }


   public int atacar() {
      
      mensaje = ""; //Se generará un nuevo mensaje
      int ataque = azar.nextInt(11) + 10;
      
      if (esCritico()) {
         mensaje += "¡Ataque crítico! ";
         ataque =  (int)(ataque * 1.5);
      }
      
      mensaje += "Daño: " + ataque + " puntos";
      return ataque;
   }
   
   public void perderVida(int ataque) {
      vida -= ataque;
   }
   
   private boolean esCritico() {
      int valor = azar.nextInt(100);
      /*
       * Entre 0 y 100 hay diez valores que son
       * múltiplos de 10.
       * Es decir, hay un 10% de posibilidades
       * de obtener un valor múltiplo de 10.
       */
      
      return valor%10 == 0;
   }
   
   @Override
   public String toString() {
      return "Nombre: " + nombre + "\nVida: " + vida;
   }

}

Sobre la clase PanelPokemon, es la que va a requerir más cambios. Antes al comenzar ya estaba plenamente configurada, con su Pokemon asignado, la barra de vida ajustada al nivel de vida del Pokemon, el nombre del Pokemon,...
Ahora no, ahora ha de comenzar mostrando la foto de la Pokeball y cuando se seleccione un combatiente, ajustar nombre, foto y barra de vida según el Pokemon escogido.

Obtiene un atributo nuevo, un valor int que podrá tener dos valores: 1 ó 2.
Esto es para identificar cuál es el panel que está a la izquierda (1) y cuál a la derecha (2).
Así sabremos en que panel tenemos que poner la foto en la que el personaje mira a la derecha y en cuál la que mira a la izquierda.

Los "subpaneles" que componen este panel ahora estarán referenciados como atributos de clase para poder interactuar con ellos. Antes los instanciábamos directamente sin referenciarlos, porque no se requería acceder a ningún método que pudieran tener.

Ahora estos paneles si tendrán métodos.
PanelNombre tiene un método para configurar el nombre y PanelImagen otro para cambiar la imagen.
A esta clase interna, PanelImagen, le vamos a implementar un MouseListener para detectar cuándo el usuario hace click en la imagen.
Cuando esto ocurre, es cuando hacemos aparecer el JDialog que permite seleccionar Pokemon.

A este JDialog hay que pasarle unos parámetros, uno de ellos va a ser precisamente una referencia al PanelPokemon desde el cuál ha sido invocado.
De este modo, desde el JDialog podremos setear un Pokemon para el PanelPokemon e invocar a los métodos necesarios para que se configure respecto al Pokemon asignado.

Esta es la clase PanelPokemon reescrita. Atención especial al MouseListener de la subclase PanelImagen
Código: [Seleccionar]
public class PanelPokemon extends JPanel{

public Pokemon pokemon;
private int numPanel;
private JButton btAtacar;
private JProgressBar barraVida;
private PanelNombre pnNombre;
private PanelImagen pnImagen;
private PanelVida pnVida;

//Autoreferencia para poder transmitirsela al JDialog que selecciona Pokemon
private PanelPokemon estePanel;

public PanelPokemon(String rutaImagen, int numero) {
numPanel = numero;
pnNombre = new PanelNombre();
pnImagen = new PanelImagen(rutaImagen);
pnVida = new PanelVida();

setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(pnNombre);
add(pnImagen);
add(pnVida);
add(new PanelBoton());
estePanel = this;
}

public void reiniciar() {
if (numPanel == 1)
pnImagen.setImagen("img/pokeball_215.png");
else
pnImagen.setImagen("img/pokeball_215_p2.png");
btAtacar.setEnabled(false);
pnNombre.setNombre("Escoge Pokemon");
barraVida.setValue(0);

}

/*
* Este método lo invocará el JDialog que permite
* seleccionar Pokemon, para setearle un Pokemon
* a esta clase PanelPokemon.
* Para poder invocar este método, el JDialog necesita
* recibir una referencia a esta clase para que haya
* visibilidad entre ellas.
*/
public void setPokemon(Pokemon pok) {
pokemon = pok;
pnNombre.setNombre(pokemon.getNombre());
if (numPanel == 1) {
pnImagen.setImagen(pokemon.getImgP1());
pokemon.suTurno = true;
}
else {
pnImagen.setImagen(pokemon.getImgP2());
pokemon.suTurno = false;
}
barraVida.setMaximum(pokemon.getVida());
barraVida.setValue(pokemon.getVida());
btAtacar.setEnabled(pokemon.suTurno);
}

private class PanelNombre extends JPanel {

private JLabel nombre;

public PanelNombre() {
nombre = new JLabel("Escoge Pokemon");
nombre.setFont(new Font("Consolas", Font.BOLD, 34));
nombre.setForeground(Color.WHITE);
add(nombre);
setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.WHITE, Color.BLACK));
setBackground(new Color(23, 151, 238));
}

public void setNombre(String nombrePok) {
nombre.setText(nombrePok);
}
}

private class PanelImagen extends JPanel implements MouseListener{

private JLabel icono;

public PanelImagen(String rutaImagen) {
ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(rutaImagen));
icono = new JLabel(imagen);
icono.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(icono);
addMouseListener(this);
}

public void setImagen(String ruta) {
ImageIcon imagen = new ImageIcon(PanelPokemon.class.getClassLoader().getResource(ruta));
icono.setIcon(imagen);
}

@Override
public void mouseClicked(MouseEvent e) {
new Selector(null, true, estePanel); //JDialog recibe referencia a esta misma clase
}
@Override
public void mousePressed(MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent e) { }
@Override
public void mouseExited(MouseEvent e) { }

}

private class PanelVida extends JPanel {

public PanelVida() {

barraVida = new JProgressBar();
barraVida.setMaximum(0);
barraVida.setValue(0);
barraVida.setPreferredSize(new Dimension(200, 30));
barraVida.setForeground(Color.BLUE);
add(barraVida);
barraVida.setBorder(BorderFactory.createRaisedSoftBevelBorder());

TitledBorder bordeTitulado = new TitledBorder("Nivel de Vida");
bordeTitulado.setTitleFont(new Font("Consolas", Font.ITALIC, 24));
setBorder(bordeTitulado);
}
}

private class PanelBoton extends JPanel {

public PanelBoton() {
btAtacar = new JButton("¡ATACAR!");
btAtacar.setEnabled(false);
btAtacar.setFont(new Font("Consolas", Font.BOLD, 30));
add(btAtacar);
setBorder(BorderFactory.createEmptyBorder(30, 20, 30, 30));
}
}

public void actualizarPanel() {
barraVida.setValue(pokemon.getVida());
btAtacar.setEnabled(pokemon.suTurno);
}

public void agregarAccion(ActionListener accion) {
btAtacar.addActionListener(accion);
}

}

Como hemos dicho, al hacer click en PanelImagen, se instancia la clase Selector que es un JDialog que aparece en pantalla con 8 Pokemon seleccionables.
Esta clase será quien tenga un ArrayList con 8 objetos Pokemon ya preconfigurados y listos para ser seleccionados.
En pantalla se muestran en una grilla de 2 filas y 4 columnas.

Cada imagen de estos Pokemon seleccionables, se construye mediante la subclase PanelPersonaje la cuál también implementa un MouseListener.
En esta ocasión, el MouseListener no solo responderá ante el click del ratón, también responde al momento en que la flechita del ratón pasa por encima.
De este modo, al pasar el ratón por un personaje, el fondo cambia de color para que quede resaltado respecto a los demás.
Cuando el ratón abandona su espacio, recupera el color habitual.

Cuando se hace click, se setea el Pokemon seleccionado mediante la referencia al PanelPokemon que lo ha invocado y se cierra el Selector de Pokemon.

Código: [Seleccionar]
public final class Selector extends JDialog{

private ArrayList<Pokemon> pokedex;
private PanelPokemon panelPokemon; //Referencia al panel de jugador que invoca este selector

public Selector(Frame parent, boolean modal, PanelPokemon pnPok) {
super(parent, modal);
panelPokemon = pnPok;
construirPokedex();

setLayout(new BorderLayout());
PanelMensajes pnMensaje = new PanelMensajes();
pnMensaje.setMensaje("Selecciona un personaje Pokemon");
add(pnMensaje, BorderLayout.NORTH);

JPanel roaster = new JPanel();
roaster.setLayout(new GridLayout(2, 4)); //Grilla para los 8 pokemons
for (Pokemon pok: pokedex)
roaster.add(new PanelPersonaje(pok));

add(roaster, BorderLayout.CENTER);

setTitle("Seleccionar Pokemon");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setResizable(false);
setVisible(true);
}


private void construirPokedex() {
pokedex = new ArrayList<Pokemon>();
pokedex.add(new Pokemon("Bulbasaur", 200, "img/bulbasaur_p1.png", "img/bulbasaur_p2.png"));
pokedex.add(new Pokemon("Caterpie", 200, "img/caterpie_p1.png", "img/caterpie_p2.png"));
pokedex.add(new Pokemon("Charmander", 200, "img/charmander_p1.png", "img/charmander_p2.png"));
pokedex.add(new Pokemon("Pidgey", 200, "img/pidgey_p1.png", "img/pidgey_p2.png"));
pokedex.add(new Pokemon("Pikachu", 200, "img/pikachu_p1.png", "img/pikachu_p2.png"));
pokedex.add(new Pokemon("Rattata", 200, "img/rattata_p1.png", "img/rattata_p2.png"));
pokedex.add(new Pokemon("Squirtle", 200, "img/squirtle_p1.png", "img/squirtle_p2.png"));
pokedex.add(new Pokemon("Weedle", 200, "img/weedle_p1.png", "img/weedle_p2.png"));
}

private void cerrarDialogo() {
dispose();
}

private class PanelPersonaje extends JPanel implements MouseListener{

public Pokemon personaje;

public PanelPersonaje(Pokemon pok) {
personaje = pok;
ImageIcon imagen = new ImageIcon(
Selector.class.getClassLoader().getResource(personaje.getImgP1()));

add(new JLabel(imagen));
setToolTipText(personaje.getNombre());
setBorder(new MatteBorder(15, 15, 15, 15, Color.DARK_GRAY));
setBackground(Color.WHITE);
addMouseListener(this);
}

@Override
public void mouseClicked(MouseEvent e) {
panelPokemon.setPokemon(personaje);
cerrarDialogo();
}
@Override
public void mousePressed(MouseEvent e) { }
@Override
public void mouseReleased(MouseEvent e) { }
@Override
public void mouseEntered(MouseEvent e) {
setBackground(Color.YELLOW);
}
@Override
public void mouseExited(MouseEvent e) {
setBackground(Color.WHITE);
}
}

}

Cuando ya se han seleccionado los Pokemon, se puede comenzar la batalla.
La lógica de la batalla se lleva a cabo en la clase principal, BatallaSwing.
Esta clase requiere pocos cambios, total, en realidad tiene muy poco código.

Antes teníamos a dos Pokemon como atributos, ahora ya no.  Interactuaremos directamente con los objetos Pokemon que contiene cada PanelPokemon.

Código: [Seleccionar]
public class BatallaSwing extends JFrame{

private PanelMensajes pnMensajes;
private PanelPokemon pnPok1;
private PanelPokemon pnPok2;

public BatallaSwing() {

pnMensajes = new PanelMensajes();
pnPok1 = new PanelPokemon("img/pokeball_215.png", 1);
pnPok1.agregarAccion(new AccionAtacar());
pnPok2 = new PanelPokemon("img/pokeball_215_p2.png", 2);
pnPok2.agregarAccion(new AccionAtacar());

setLayout(new BorderLayout());

add(pnMensajes, BorderLayout.NORTH);
JPanel pnCentro = new JPanel();
pnCentro.add(pnPok1);
pnCentro.add(new PanelVS()); //PanelVS no requiere referencia, no se interactúa con ella.
pnCentro.add(pnPok2);
add(pnCentro, BorderLayout.CENTER);

setTitle("Batalla Pokemon");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

private class AccionAtacar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {

if (pnPok1.pokemon.suTurno) {
pnPok2.pokemon.perderVida(pnPok1.pokemon.atacar());
pnMensajes.setMensaje(pnPok1.pokemon.mensaje);
pnPok1.pokemon.suTurno = false;
pnPok2.pokemon.suTurno = true;
}
else {
pnPok1.pokemon.perderVida(pnPok2.pokemon.atacar());
pnMensajes.setMensaje(pnPok2.pokemon.mensaje);
pnPok1.pokemon.suTurno = true;
pnPok2.pokemon.suTurno = false;
}

pnPok1.actualizarPanel();
pnPok2.actualizarPanel();

if (pnPok1.pokemon.getVida() <= 0 || pnPok2.pokemon.getVida() <= 0) {
String mensajeFinal = "Juego Terminado\n Ha ganado: ";
mensajeFinal += pnPok1.pokemon.getVida()>0?pnPok1.pokemon.getNombre():pnPok2.pokemon.getNombre();

JOptionPane.showMessageDialog(null, mensajeFinal, "Fin del Juego",
JOptionPane.INFORMATION_MESSAGE);


pnMensajes.setMensaje("Clicka las Pokeball para escoger Pokemon");
pnPok1.reiniciar();
pnPok2.reiniciar();
}


}
}

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

}

Y eso es todo.
Tiene algunos fallos, por ejemplo al seleccionar un Pokemon en el lazo izquierdo, ya podemos pulsar el botón ATACAR sin haber seleccionad un rival en el otro lado.
Visualmente parece que no pasa nada pero en realidad ocurre una NullPointerException cada vez que se pulsa el botón sin tener rival escogido.

Otro fallo es que durante el combate, en cualquier momento podemos volver a escoger otro Pokemon, o el mismo, y el nuevo Pokemon comienza con la vida entera mientras el rival sigue con la barra de vida reducida por los daños.

Pero bueno, son pequeños detalles que se pueden ir puliendo.

He intentado explicar los cambios hechos lo mejor posible, pero preguntad cualquier cosa que no se entienda.

Proporciono el código fuente de dos formas:
- Una, solo el código, adjuntándolo en este mismo mensaje del foro
- Otra, código y las imágenes que he usado, en este enlace Google Drive

Se pueden usar las imágenes que uno quiera, basta con incluirlas dentro de un package en nuestro proyecto. Yo he seguido esta estructura:
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

 

Esto es un laboratorio de ideas...
Aprender a programar

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".