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