Diseño Juegos Tablero

Para implementar juegos de tablero, habrá que extender de dos clases:

  • JuegoTableroAbstracto (situada en el paquete IStation.Juegos.Tablero.Cliente del proyecto FrameworkClienteTablero), y
  • JuegoTableroAbstracto (situada en el paquete IStation.Juegos.Tablero.Servidor del proyecto FrameworkServidorTablero).

De la primera extenderá la clase JuegoNombreJuego de la parte del cliente del juego de tablero, mientras que de la segunda, deberá hacerlo la clase PartidaNombreJuego de la parte del servidor.

Vamos primero con la parte del cliente:

FrameworkClienteTablero contiene tres clases: la mencionada JuegoTableroAbstracto, PanelBaseTablero y Ficha.

La primera de ellas, JuegoTableroAbstracto, extiende de JuegoAbstracto y se encargará de dar cierta funcionalidad común a los juegos de tablero. Contiene tres atributos: el panel de juego, un diálogo de configuración y un HashMap que contiene, por cada jugador, un array de enteros de n posiciones (siendo n el número de fichas que tiene el jugador) con el número de casilla en el que se encuentra cada una de las n fichas.
JuegoTableroAbstracto tiene dos métodos abstractos que deberán ser definidos en cada juego concreto:

  • void inicializarFichas(): se utilizará para insertar en el HashMap anteriormente descrito el nombre de cada jugador, junto con el array de enteros. En el caso de que las posiciones iniciales de las fichas en el tablero tengan alguna peculiaridad (por ejemplo en el parchís las fichas inicialmente no "se encuentran" en el tablero propiamente dicho), deberá inicializarse debidamente el array antes de agregarlo al HashMap.
  • Ficha correspondencia(int posicion): dada una posición del tablero devuelve un objeto de tipo Ficha que contendrá las posiciones, X e Y, en las que se deberá dibujar la ficha, vamos, las coordenadas en las que las veremos en la pantalla.

Además de estos dos métodos, la clase JuegoTableroAbstracto ya se encarga de implementar el procesarMensaje() y el procesarEvento() heredados, por lo que no habrá que hacerlo en el juego concreto.

La clase PanelBaseTablero es la parte gráfica común a todos los juegos de tablero. De ella deberá extender la clase PanelBaseNombreJuego de cada juego concreto. La clase del panel del juego en concreto no deberá implementar ningún método adicional. Aún así, conviene conocer algunos métodos de la clase PanelBaseTablero:

  • void inicializarFichas(String[] jugadores, int numfichas): en este caso vamos también a inicializar un HashMap, pero esta vez será de nombre de jugadores (ojo que esta vez se pasan los jugadores como parámetro) y array de PanelSprite. Este array contendrá las imágenes de las fichas de cada jugador.
  • void ponerTableroJuego(String ruta): pasamos por parámetro la ruta en la que se encuentra el dibujo del tablero de juego y se encarga de ponerlo en un panel.
  • void modificarFicha (InfoFicha modificacion): dada una modificación de las fichas de un jugador (la clase InfoFicha se explicará después), actualiza el HashMap de los jugadores y los sprites de sus fichas para que aparezcan en el lugar correspondiente.

La clase Ficha contiene cuatro atributos: coordenadas X e Y de la ficha y su ancho y su largo. Estos atributos se refieren al dibujo de la ficha. Las coordenadas X e Y son la posición de la esquina superior izquierda de la imagen. El ancho y el largo son constantes, por lo que no se podrán modificar. La clase contiene mutadoras para los dos primeros atributos y accesoras para todos. El hecho de usar esta clase es la de agrupar en un objeto información relativa a la posición de una ficha cuando se necesite pintar.

Ahora la parte del servidor:
FrameworkServidorTablero sólo contiene una clase: JuegoAbstractoTablero. En este caso, será la clase PartidaNombreJuego de la parte del servidor del juego concreto la que tendrá que extender de ella.
La clase JuegoAbstractoTablero extiende a su vez de Partida. Posee como atributo un HashMap con el nombre de cada jugador y un array de enteros con la posición en el tablero de cada ficha. No contiene ningún método abstracto adicional para que sea definido en la clase concreta, sin embargo, sí que proporciona ciertas funcionalidades comunes:

  • int dondeEstaFicha(String jugador, int numFicha): dado un jugador y el número de ficha, devuelve su posición. Es una consulta del HashMap. El parámetro numFicha se refiere al índice dentro del array de enteros asociado al nombre de cada jugador.
  • int tirarDado(): genera un número aleatorio para el dado y lo devuelve.
  • void moverFicha(String jugador, int numFicha, int desplazamiento): extrae el array de las posiciones del jugador pasado por parámetro del HashMap y le suma a la ficha indicada en numFicha el desplazamiento. Ojo que le suma siempre un desplazamiento, por lo que si queremos que la ficha retroceda posiciones habrá que pasar un número negativo como parámetro en desplazamiento.
  • void inicializarFichas(List<Jugador> jugadores, int numFichas): inicializa el HashMap con los nombres de los jugadores y los arrays de tamaño numFichas, pasado por parámetro.
  • void ganar(String jugador): comunica a todos los jugadores que el jugador pasado por parámetro ha ganado.

Bueno, a parte de los framework, hay otra clase que merece que se mencione, que es InfoFicha. Se encuentra en el paquete IStation.Juegos.Tablero.Compartido en el proyecto JuegosTableroComun. Esta clase contiene tres atributos: nombre (nombre del jugador sobre el que vamos a realizar una modificación en alguna de sus fichas), numFicha (posición en el array de las fichas del jugador) y dondeEstaFicha (el contenido del array, es decir, su posición en el tablero). Todos estos atributos se inicializarán en la constructora. La clase sólo contiene accesoras para dichos atributos.
Esta clase se utiliza en el servidor y en el cliente, por ello la denominación de "común". La idea es que el servidor le indique al cliente a través de un objeto InfoFicha los cambios que debe realizar para que, en la interfaz gráfica, el usuario vea el resultado de un movimiento de fichas. Por sintetizar todo este lío: es una forma de pasar un tocho de información del servidor al cliente.
De todas formas, sólo habrá que preocuparse de la clase PartidaNombreJuego en el servidor del juego concreto a implementar, de manera que al recibir un mensaje de que el dado ha sido tirado hagamos los cálculos oportunos sobre la nueva posición de las fichas del jugador, metamos toda esa información en un objeto InfoFicha y lo difundamos a todos los clientes para que así puedan "pintar" el nuevo estado de las fichas. La parte del cliente está cubierta por el framework, por lo que en principio no habrá que hacer nada más que la parte del servidor de la manera indicada.

Y por último, mencionar los mensajes que se pasan cliente y servidor:

  • Cuando se pulsa el botón de tirar dado, el cliente le pasa al servidor un mensaje "tirarDado". Esto lo tendrá que tener en cuenta la clase PartidaNombreJuego a la hora de implementar el método procesarDatos().
  • A su vez, el servidor deberá responder a dicho mensaje "tirarDado" con un "resultadoDado" (entre otras cosas), que incluirá en los datos un Integer con el valor del dado.
  • Cuando el servidor le pase al cliente un objeto InfoFicha con las modificaciones para repintar las fichas sobre el tablero, en realidad le pasará un mensaje "ficha", siendo el objeto InfoFicha los datos de ese mensaje.
  • Si se necesita pasar cierta información (por ejemplo para la Oca, conviene indicar si el usuario ha caído en el pozo o en el laberinto), se utilizará un mensaje "info" con un String como datos del mensaje.
  • Y por último, cuando un jugador gana, el cliente recibe del servidor un mensaje "ganar".

A la hora de implementar juegos de tablero, lo que único que debe preocupar, como ya se ha mencionado, es el procesamiento del mensaje "tirarDado" en el servidor, ya que del resto se encarga el framework en el cliente.

Diagramas UML

Diagrama del Framework del Servidor

FrameworkServidorTablero.png

Diagrama del Framework del Cliente

FrameworkClienteTablero.png

Diagrama de JuegosTableroComun

JuegosTableroComun.png