Download Manual del Desarrollador de Juegos

Document related concepts
no text concepts found
Transcript
Manual del Desarrollador de
Juegos
Parte Compartida
y
Servidor
IStation
Página 1/12
Requisitos

Tener instalado un entorno de desarrollo (IDE) para JAVA (NetBeans, Eclipse,
JBuilder, y otros) con el último Java Development Kit (JDK) instalado.
Nosotros pondremos como ejemplo el IDE desarrollado por Sun bajo licencia
GPL, NetBeans 6.0.

Conocimientos de programación orientada objetos en un nivel medio.

Conocimiento del lenguaje de programación Java y su modelo de eventos en un
nivel medio.
Descargas
El software requerido se puede descargar gratuitamente de los sitios de Internet que a
continuación se detallan.
NetBeans:
http://download.netbeans.org/netbeans/6.0/final/
Eclipse:
http://www.eclipse.org/downloads/
JDK:
http://java.sun.com/javase/downloads/
IStation
Página 2/12
Parte Compartida
Parte de clases que estarán disponibles a la hora de crear un juego, la funcionalidad
principal radica clases envoltorio para el envío de mensajes y configuración de la
partida.
Este paquete se supone deberá cargarse en el ambos puntos de la aplicación, servidor y
cliente.
Clases a implementar
La única clase que es obligatorio implementar para este proyecto es:
1. Una clase que extienda DatosPartida.
Clase DatosPartida
Esta clase abstracta sirve para que el applet que crea la partida envíe al servidor los
datos básicos de la partida, que son los que van a quedar reflejados en la Base de Datos.
Para cada juego, se extenderá esta clase, agregando los datos que se precisen (ej: tiempo
máximo de turno) y definiendo el método abstracto getNombreJuego.
public abstract java.lang.String getNombreJuego()
Método que devuelve el nombre del juego (que debe ser ÚNICO) para que el
ModuloJuegos haga la carga dinámica correspondiente del juego.
El resto de la clase tiene los campos de datos públicos que representan lo que indican
sus nombres:
boolean _entrarAMitad
int _maxJugadores
int _minJugadores
int _nivelMax
int _nivelMin
boolean _observable //si permite observadores
boolean _privada
Clase MensajePartida
Clase para englobar los mensajes que envía una partida.
public MensajePartida(java.lang.String evento, java.lang.Object datos)
Constructora de MensajePartida. Que tendrá un String que será la descripción del tipo
de mensaje y un Object que será el mensaje concreto.
Clases propias del juego
En este proyecto también se deberán incluir el resto de clases que sean necesarias en
ambas partes del juego (servidor y cliente).
Por ejemplo si decidimos hacer una clase envoltorio que agrupe todos los datos que
incluirá un mensaje, para usarla para el pasaje de datos, debe estar aquí.
IStation
Página 3/12
Parte Servidor
Instrucciones a la hora de crear un juego
El framework del servidor permite controlar el curso de la partida, obteniendo
información de los clientes y notificándoles de los hechos que ocurren en dicha partida.
Las acciones de la partida están dirigidas por eventos, ya sean éstos la llegada de un
mensaje por parte de un cliente, o el transcurso de determinado período de tiempo.
Clases a implementar
Para implementar el controlador (servidor) de una partida, es necesario crear (como
mínimo) las siguientes clases:
1. Una clase que implemente IInicioPartida.
2. Una clase que extienda (herede) la clase abstracta Partida.
Estas clases se encuentran el la librería JuegosServidor.jar, que se debe importar en el
proyecto (como se explicó en la introducción del tutorial).
También es necesario implementar la librería: JuegosComun.jar.
Manifest
El módulo de juegos necesita ciertos datos de los proyectos para poder hacer la carga
dinámica y actualizar la información en la base de datos.
Los atributos que se deben agregar al archivo MANIFEST.MF que está en el directorio
del proyecto son:
1. ClaseInicio: Nombre completo de la clase iniciadora para la partida (incluyendo
el paquete). Recordamos que esta clase es la que implementa la interfaz
IInicioPartida.
2. NombreJuego: Nombre del juego que se mostrará tal cual en el portal web. Debe
coincidir con el valor devuelto por el método de DatosPartida.
3. RutaJar: Ruta del archivo JAR (binario) del applet cliente en el servidor web.
4. ClaseJuego: Nombre y ruta de la clase que extiende JuegoAbstracto en el
cliente.
5. Tipo: Por ahora los tipos contemplados son "cartas", "tablero", "otros".
6. RutaFoto: Ruta de la imagen en el servidor que se mostrará en el portal web.
Si en el directorio del proyecto no existe ningún archivo llamado MANIFEST.MF,
debe crearse un archivo de texto con este nombre y el contenido descrito. También hay
que asegurarse que el archivo llamado project.properties dentro de la carpeta
nbproject tiene una línea con el siguiente texto:
manifest.file=MANIFEST.MF
Ejemplo con el Servidor de la PatataCaliente
ClaseInicio:
IStation.Modulos.Juegos.Servidor.PatataCaliente.InicioPatataCaliente
NombreJuego: Patata Caliente
RutaJar: juegos/patata/JuegosClientePatataCaliente.jar
ClaseJuego: IStation.Cliente.Juegos.PatataCaliente.JuegoPatataCaliente
IStation
Página 4/12
Tipo: otros
RutaFoto: img/Patata.jpg
Incluir una descripción del juego
La idea es que en el portal se muestre una breve descripción del juego para tener una
idea general de qué va.
El portal va a acceder a dicha descripción a través de la base de datos. El encargado de
poner dicha información en la BBDD es el ModuloJuegos que va a leer del archivo JAR
que se encuentra en el servidor un fichero que se encuentra en la ruta
"/IStation/Descripcion.txt".
Aquí se muestra una imagen del proyecto JuegosServidorPatataCaliente en donde se
puede apreciar donde va ubicado el archivo de descripción.
Se puede ver el archivo dentro del paquete IStation.
Clase Jugador
Esta clase proporciona la funcionalidad para tratar un jugador. Permite realizar acciones
sobre los jugadores tales como enviar mensajes o alterar el número de puntos.
Los métodos útiles de la clase para un desarrollador son los siguientes:
public java.lang.String getNombre()
Accesora que devuelve el nombre del jugador.
public int obtenerPuntos()
Consulta los puntos actuales del jugador.
public void sumarPuntos(int puntos)
Suma a los puntos del usuario (jugador) una cantidad de puntos.
IStation
Página 5/12
public void enviarMensaje(String evento, Object msg)
Envía un mensaje (se envuelve con un MensajePartida).
Clase que implementará IInicioPartida
Esta interfaz tiene sólo un método tipo factoría que lo que hace es proporcionarnos una
nueva instancia de una partida concreta.
El prototipo de la función es el siguiente:
public Partida iniciarPartida(DatosPartida datos,
IModuloJuegos modJuegos, int idPartida)
Evidentemente lo que vamos a devolver será una instancia de una partida concreta (una
clase que extiende a Partida) y el parámetro datos será de clase que extienda a
DatosPartida, como se explicó en la sección anterior.
Clase que extenderá a Partida
La clase ABSTRACTA Partida, es la que proporciona la funcionalidad común para
todas las partidas (en el lado del servidor).
Esta clase deberá extenderse para crear las partidas de los juegos concretos.
La clase partida se comunica con el Módulo de Juegos a través de la interfaz
IModuloJuegos. Sin embargo, toda la interacción con el Módulo de Juegos es
transparente al desarrollador de juegos.
Hay métodos que se pueden redefinir y otros que no, éstos son los que llevan el
modificador final en su cabecera.
Métodos que se tienen que implementar obligatoriamente
protected void accionesInicioPartida()
En este método deben llevarse a cabo las acciones derivadas del inicio de la partida.
Estas acciones serán llevadas a cabo justo después de que la partida haya iniciado.
protected void accionesFinPartida()
En este método deben llevarse a cabo las acciones derivadas de la finalización de la
partida. Estas acciones serán llevadas a cabo justo antes de que la partida termine.
protected void jugadorAnadido(Jugador jugador)
Este método realiza acciones derivadas de la incorporación de un
jugador a la partida.
Aquí se deben implementar para añadir las acciones oportunas.
protected void jugadorAbandona(Jugador jugador)
Este método realiza acciones dependientes de la partida concreta cuando un jugador
abandona la misma. El motivo del abandono ha podido ser voluntario (por ejemplo, el
IStation
Página 6/12
usuario ha cerrado la ventana del juego) o involuntario (por ejemplo, por desconexión de
la red).
Se recibe por parámetro el jugador que ha abandonado.
protected void accionesNuevoTurno(Jugador jAnterior, Jugador jNuevo)
Este método realiza las acciones que provoca un cambio de turno. Cuando este método
se llama, todos los clientes ya han sido informados del cambio de turno, luego los
mensajes que se envíen en este método llegarán después de dicha información.
Parámetros: jugador anterior y jugador nuevo (el que tiene el turno)
protected void procesarDatos(Jugador jugador, String evento, Object
datos)
Este método recibe un mensaje enviado por un jugador. Si la variable
jugador es null y el evento es “temporizador", entonces el mensaje a procesar es un eco.
Métodos que se pueden sobrescribir
Si las acciones que proporcionan no son las deseadas para el juego que se esté
desarrollando, esta es la lista de métodos que se pueden sobrescribir para cambiar su
funcionalidad:
protected boolean permitirJugador(Jugador jugador)
Este método debe devolver un valor booleano que indique si un jugador (solicitante)
puede entrar en la partida. Redefinir esta función permitirá añadir condiciones
particulares para entrar en la partida concreta.
protected Jugador jugadorInicial()
Decide el jugador que comienza la partida. Por defecto, elegirá el jugador que creó la
partida. Si se desea cambiar el jugador inicial, puede redefinirse este método.
protected boolean permitirIniciarPartida(Jugador jug)
Decide si el jugador puede iniciar la partida. Por defecto, solamente el creador de la
partida puede iniciarla.
protected Jugador siguienteJugador(Jugador jAnterior)
Decide el jugador que jugará el siguiente turno. Esta implementación hace que el orden
de juego sea secuencial y circular según el orden de entrada a la partida.
Si se quiere definir otro orden, puede redefinirse este método.
Métodos que NO se podrán sobrescribir
Hay métodos que están declarados como final en la clase Partida. Se adjunta la
descripción de sus funcionalidades.
IStation
Página 7/12
public final void terminarPartida()
Hace que la partida termine, dejando de proporcionar todos los servicios a los clientes.
protected final void difundirMensaje(java.lang.String evento,
java.lang.Object datos)
Envía un mensaje a todos los observadores y jugadores.
Ésta es la única manera de enviar mensajes a los observadores, de manera que no es
posible enviar información a los observadores sin que sea enviada también a todos los
jugadores.
protected final Jugador jugadorActual()
Devuelve el jugador al que le corresponde el turno actual.
protected final java.util.List<Jugador> getListaJugadores()
Devuelve una lista con todos los jugadores de la partida.
protected final void generarEco(int milisegundos, Object datos)
Genera un mensaje con los datos pasados por parámetro que será enviado de vuelta a la
partida transcurrido el tiempo establecido.
protected final void cambioDeTurno(Jugador jugador)
Este método se llama cada vez que un jugador solicita el fin de su turno. Se comprueba
que el jugador que lo solicita estuviese en posesión del turno, y utiliza el método
siguienteJugador para averiguar el siguiente jugador que comenzará su turno.
Este método también puede ser llamado desde otro método de esta misma clase para
forzar el fin de turno de un jugador. Para implementar acciones que deban llevarse a
cabo en los cambios de turno, debe implementarse el método accionesNuevoTurno.
IStation
Página 8/12
Desarrollando un juego
Concepto del juego
Vamos a poner como ejemplo de desarrollo un juego sencillo, La Patata Caliente. Este
juego consiste en pasar el turno (la patata) al siguiente jugador sin que se acabe el
tiempo de la partida con la patata en tu poder.
Cuando el tiempo de la ronda (que se decide de forma aleatoria en el servidor) se acaba
el jugador que está en posesión del turno pierde.
Parte Común
Para realizar la parte común hay que seguir los pasos que se indicaron en la parte
correspondiente de este manual.
En resumen, estos eran:
1. Crear un proyecto que se llamara JuegosComunPatataCaliente.
2. Importar como biblioteca JAR el archivo JuegosComun.jar.
3. Crear una clase que extienda a DatosPartida e implemente su método
abstracto getNombreJuego.
Entonces, creamos la clase DatosPatataCaliente con el siguiente método:
/**
* Permite acceder al nombre del juego
* @return Cadena de caracteres que representa el nombre del juego
*/
public String getNombreJuego() {
return “Patata Caliente”;
}
Y un constructor por defecto que nos facilitará el ingreso de los datos de la partida.
/**
* Crea una instancia de DatosPatataCaliente con un número de
jugadores dado
* @param numJug : Numero maximo de jugadores permitidos
*/
public DatosPatataCaliente(int numJug) {
_maxJugadores= numJug;
_entrarAMitad= false;
_minJugadores= 1;
_nivelMax= Integer.MAX_VALUE;
_nivelMin= 0;
_observable= false;
_privada= false;
}
Con esta clase la parte común ya está lista, dado que no vamos a pasar mensajes de otro
tipo. Ya podemos empezar con la parte servidor.
IStation
Página 9/12
Parte Servidor
Para realizar la parte común hay que seguir los pasos que se indicaron en la parte
correspondiente de este manual.
En resumen, estos eran:
1. Crear un proyecto que se llamara JuegosServidorPatataCaliente.
2. Importar como biblioteca JAR el archivo JuegosComun.jar.
3. Importar como biblioteca JAR el archivo JuegosComunPatataCaliente.jar.
4. Importar como biblioteca JAR el archivo JuegosServidor.jar.
5. Incluir un TXT con la descripción del juego.
6. Crear un fichero MANIFEST.MF
7. Crear una clase que extienda a Partida e implemente sus métodos abstractos.
8. Crear una clase iniciadora que implemente la interfaz IInicioPartida
Interfaz iniciadora
Creamos una clase dentro del paquete IStation.Juegos.PatataCaliente.Servidor:
public class InicioPatataCaliente implements IInicioPartida {
public Partida iniciarPartida(DatosPartida datos,
IModuloJuegos mJuegos,
int idPartida) {
return new PartidaPatataCaliente(mJuegos,
idPartida,
(DatosPatataCaliente)datos);
}
}
Clase que implementa el servidor
La constructora de la clase, simplemente se limita a pasar los parámetros a su súper
clase (Partida).
public PartidaPatataCaliente(IModuloJuegos mJuegos,
int idPartida,
DatosPatataCaliente datos) {
super(mJuegos,idPartida,datos);
}
Vamos a analizar los métodos que hay que implementar obligatoriamente:
Cuando un jugador se una a la partida no hay que hacer nada para este juego, el
framework ya se encarga de mantenerlo en una lista, para saber que está unido a la
misma.
La misma situación se produce cuando un jugador abandona una partida, no tenemos
que hacer nada. Es cierto que podríamos haber optado por penalizar al jugador y restarle
IStation
Página 10/12
puntos por abandonar el juego antes de finalizar (para esto bastaría con agregar una
línea de código.
protected void jugadorAnadido(Jugador jugador) { }
protected void jugadorAbandona(Jugador jugador) {
//posible acción
// jugador.sumarPuntos(-1);
}
Dado que el tiempo que va a estar circulando la patata es aleatorio, cuando se da
comienzo a la partida, calculamos el tiempo en segundos que queremos que dure la
partida, o lo que es lo mismo que la patata esté circulando.
Y cuando la partida acabe no tendremos que realizar ninguna acción particular.
protected void accionesInicioPartida() {
int tiempoPartida = (int)(java.lang.Math.random()*50+10)*1000;
//Ejemplo de uso del módulo de LOG
getLogger().escribirInformacion( "Tiempo: " +
(tiempoPartida/1000) +" segundos");
//Genero un Eco para recibir un mensaje cuando
//haya transcurrido el tiempo de la partida
this.generarEco(tiempoPartida, null);
}
protected void accionesFinPartida() { }
Los mensajes de paso de turno los administra el framework de modo que no tenemos
que preocuparnos por los mismos. Desde los clientes no nos llegarán mensajes, por
tanto el único tipo de mensaje que nos queda por procesar en el del ECO, que
generamos cuando se inicia la partida.
protected void procesarDatos(Jugador jugador, String evento,
Object datos) {
if(evento != null && evento.equals(TEMPORIZADOR)) {
//Uso del LOG
getLogger().escribirInformacion("Se acabó el tiempo");
//Envío mensaje a todos los jugadores,
//indica que se acabó el tiempo
difundirMensaje("tiempo", null);
Iterator<Jugador> it = getListaJugadores().iterator();
while(it.hasNext()) {
Jugador jugAux = it.next();
//Ganan todos los jugadores que no tengan la patata
if(!jugAux.equals(jugadorActual())) {
jugAux.sumarPuntos(1);
}
}
//Llamo al framework para dar la partida por finalizada
terminarPartida();
}
}
IStation
Página 11/12
Por último nos queda ver que hacer cuando un jugador toma posesión del turno. Como
el framework se encarga de registrar este evento, y registrar qué jugador tiene el turno,
no haremos nada cuando pase el turno.
protected void accionesNuevoTurno(Jugador jugadorAnterior,
Jugador jugadorNuevo) { }
Este era el último método que teníamos que implementar obligatoriamente. Con lo cual
ya hemos terminado.
Si quisiéramos cambiar el comportamiento por defecto del framework tendríamos que
sobre escribir los métodos permitirJugador, jugadorInicial, siguienteJugador o
permitirIniciarPartida.
El equipo de IStation espera que manual te haya sido útil, si tienes cualquier tipo
de duda, entra a la sección del portal web dedicada a los desarrolladores, o al foro.
IStation
Página 12/12