[DESARROLLO] Guía para la creación de objetos ambientales reutilizables en escenarios

Queremos crear una serie de elementos ambientales, para reutilizarlos ampliamente en los distintos escenarios. Así, los escenarios estarán mas ricamente decorados y podrán tener una mayor flexibilidad al cambio. Consideramos elemento ambiental cualquier objeto definido por unas dimensiones, con unas áreas de movimiento definidas y una representación gráfica. Unos ejemplos serían: una caja de suministros, una estatua, un vehículo ardiendo…

Ahora, aprenderemos como construir uno desde cero.

Para crear un objeto escénico, será necesario definirle:

  1. Sus dimensiones.
  2. El sitio donde se va a colocar en el escenario.
  3. Un área de colisiones (donde no puedan entrar otros objetos, personajes y proyectiles)
  4. Las imagenes que aparecerán en pantalla para su representación en el escenario.

Para ilustrar los pasos mejor, abordaremos un caso sencillo de ejemplo: un típico bidón de combustible o agua Wehrmachtskanister, posteriormente conocido como «JerryCan» . Como diseño y ayuda, podemos empezar por componer la imagen, utilizando tres capas, una para la imagen que quedará por encima del escenario, otra para la que quedará en el fondo del escenario, y otra con los rectángulos que representan las áreas bloqueadas a que entren otros objetos, personajes y proyectiles.

Aquí tenemos nuestra imagen con el bidón.

Creamos una clase nueva que herede de ObjetoEscenico. Creamos un constructor que recibe el parámetro Coordenada, que marca la posición donde se colocará en el escenario. Para  indicarle las dimensiones, utilizamos inmediatamente después la sentencia

private static int DIMENSIONX = 49;
private static int DIMENSIONY = 66;
/**
  *
  * @param coordenadaInicial coordenada donde se situará en el Escenario
  */
    public MiObjetoEscenico (Coordenada coordenadaInicial) {
        super(coordenadaInicial,
new Coordenada( coordenadaInicial.getX()+DIMENSIONX,
coordenadaInicial.getY()+DIMENSIONY ));

//otras inicializaciones…
}

El objeto escénico por defecto, tiene todo el área que ocupa, marcado como aŕea no permitida de movimientos. Podemos cambiar esto sobreescribiendo el método «hayColisión»:

public boolean hayColision(Rectangulo rectangulo){
      return false;
}

En nuestro caso de ejemplo, nos interesa que los personajes pasen por detrás del bidón, sin que sea un obstáculo a bordear, por tanto nunca colisionaran entre ellos, y debemos devolver «false» para todos los casos

Ahora toca el apartado gráfico. vamos a crearnos otra clase que herede de ObjetoEscenicoVista (que esta a su vez hereda de java.awt.image.BufferedImage)

public class MiObjetoEscenicoVista extends ObjetoEscenicoVista {
private BufferedImage imagen;

public MiObjetoEscenicoVista (ObjetoEscenico bidon, MediaTrackerHW mediaTracker){
super(bidon);
//–pasamos la ruta de la imagen que creamos a MediaTrackerHW para cargarla
imagen
= mediaTracker.addImage( «archivos_graficos/escenarios/objetosEscenicos/bidon.png»);

}

Ahora, como queremos que la imagen del bidón, aparezca delante de los personajes y del fondo del escenario, sobreeescribimos el método que llamará la pantalla cuando quiera pintar lo que va delante:

public BufferedImage getImagePost(){
return imagen;
}

Perfecto, ya tenemos las dos clases necesarias para nuestro nuevo objeto. Ahora sólo nos queda incluirlas en la FábricaObjetosEscenicos, que es a la clase que recurren los escenarios, cuando tienen que crear un objeto. Añadiríamos simplemente un par de líneas en el método creaObjetoEscenico:

else if (clase.equals(MiObjetoEscenico.class))
return new MiObjetoEscenico(coordenadaInicial);

y las siguientes dos en el método creaObjetoEscenicoVista

else if (clase.equals(MiObjetoEscenico.class))
return new MiObjetoEscenicoVista(objetoEscenico, mediaTracker);

LLegó la hora de probarla en un escenario, por ejemplo, utilizaremos el escenario «Nimega». Así pues, le decimos en que sitio del escenario debe aparecer el bidón, añadiendo la siguiente línea de código en el método inicializarObjetosEscenicos (y no hay que hacer nada en la parte visual gracias a la FábricaObjetosEscenicos)

public void inicializarObjetosEscenicos(){
getArrayObjetoEscenico().add( new MiObjetoEscenico( new Coordenada(1200,1940)));
}

 Resultado de poner nuestro bidon en el escenario

E incluso, si ponemos varios, podemos hacer composiciones con ellos, por ejemplo:

public void inicializarObjetosEscenicos(){
getArrayObjetoEscenico().add(new MiObjetoEscenico(new Coordenada(1200,1940)));
getArrayObjetoEscenico().add(new MiObjetoEscenico(new Coordenada(1150,1940)));
getArrayObjetoEscenico().add(new MiObjetoEscenico(new Coordenada(1175,1880)));
}

 Resultado de colocar adecuadamente varios bidones


Publicado en Desarrollo | Etiquetado , | Deja un comentario

Java RMI. Use lo de allí, como si lo tuviera aquí.

-¿Cual es al diferencia entre programar un juego con modo individual y uno con modo multijugador?
-Ahora, con Java RMI (Java Remote Method Invocation), ninguna.

-¿Y qué es eso Java RMI?
Es un mecanismo de los programas en Java, para comunicar diferentes máquinas, utilizado en las aplicaciones distribuidas.  Para saber mas [1]

-¿Como empiezo a utilizar Java RMI en mis aplicaciones distribuidas?
-Muy fácil, el guión a seguir, sin entrar detalles técnicos, sería:

  1. Codifique su clase, instánciela y márquela como remota para que esté accesible a través de la red.
  2. Invoque un método de dicha clase desde cualquier otra máquina.
  3. Espere el resultado del método (si es que devuelve algún valor) Bibliografia [2][3]
Después de haberte vendido la moto, vamos a lo interesante, ¿qué tiene que ver Java RMI con los videojuegos on-line multijugador? ¿Cómo puedo usar/aplicar esta tecnología?
Tengo la necesidad de comunicar los distintos jugadores, para que cada uno conozca la situación actual del juego y, por ejemplo, pueda informar cada uno de los movimientos que realizan a los demás. Pues yo, me planteo crear un diagrama de clases olvidándome de donde estará cada clase instanciada (vamos, todo como si estuviera en local) y prestando  especial interés en las relaciones de uso/asociación que hay entre las clases (porque alguna de ellas, se harán vía invocación remota)
Me fijaré, para ilustrar con un ejemplo, en la funcionalidad de comunicar el movimiento que quiere realizar un jugador con su personaje/ficha/vehículo:

En la situación anterior, si nos encontramos en una partida on-line, sabemos:

  1. El servidor, el escenario/tablero/circuito y los personajes/fichas/vehículos estarán en el servidor.
  2. En cada máquina del jugador tendremos un aplicación cliente, que controla los eventos de teclado/ratón/gamepad para enviarlos al servidor.

Para realizar esta comunicación, el caso normal es generar un sistema de comunicación UDP o TCP, con sus socket y métodos,  para cuando el jugador quiera comunicar a su personaje (alojado en el servidor)

Pues, Java RMI, ya nos da esa implementación sobre TCP, y gracias a ello nos olvidamos de socket y demás. Simplemente debemos de definir mediante una interfaz, que métodos (en el ejemplo setMovimiento(tipoMovimiento)) podrán ser utilizados remotamente. Creamos el objeto que implemente esa interfaz, en caso del ejemplo: Personaje, creamos una instancia y la registramos en rmiregistry, obtendremos una referencia de objeto remoto, la cual usará el Jugador, para llamar a los métodos remotos que definimos anteriormente. Además, los métodos remotos definidos, pueden lanzar RemoteException, para dar soporte a cualquier tipo de error que pudiera surgir en la llamada, o dentro de la ejecución del método.

Lo importante de todo esto, no es intentar explicar qué y cómo es Java RMI (hay miles de tutoriales por internet), si no saber lo fácil y cómodo que resulta utilizarlo. Me parece  muy simple el diseño de las aplicaciones, y por extensión, rápido de entender. En contra veo, puede ser mas lento que la implementación tradicional mediante socket UDP/TCP, ya que cada llamada a un método remoto, implica establecimiento de comunicación TCP, envio y confirmación.

En mi proyecto en cuestión, lo estoy utilizando en muchas funcionalidades a implementar, por ejmplo:

  1. Un Jugador comprueba si existe un servidor de partidas en una IP determinada, se traduce en un metodo remoto, devolviendo información a cerca del servidor, o RemoteException, si no existe o está disponible dicho servidor
  2. El jugador anterior, pide unirse al servidor anterior (después de comprobar que está disponible) el método remoto del servidor, le devuelve la referencia del Personaje asignado que entra en la partida.
  3. En el desarrollo de una partida, un jugador pide constantemente la situación de la partida, devolviendo el servidor un objeto con toda la información de situación de los demás contrincantes.
Espero haber dejado claro mis intenciones de como estoy afrontando mi proyecto, así podéis dar vuestras opiniones y consejos.

Páginas web:
[1] Java RMI en la web de Oracle Documentación, artículos técnicos, bugs, tutoriales…
Bibliografia:
[2] Java RMI. Autor: William Grosso Muy bueno, te lo explica desde las bases de RMI sin dejarte ninguna duda técnica.
[3] Aplicaciones distribuidas en Java Bueno para una primera toma de contacto con RMI, se centra demasiado en el ejemplo central. En español.

Publicado en Uncategorized | Etiquetado , , | Deja un comentario

Programación extrema…¿Quieres salir conmigo?

Bien, tengo la idea clara de hacer un videojuego, un montón de ideas alrededor (funcionalidades, artísticas, posibles mejoras…) y tan sólo un par de cosas claras sobre como hacerlo:

  • Simplicidad de las cosas que haga, es la mejor manera para adaptarse a los previsibles cambios que se van a dar, y necesaria para que otros programadores (que no sean el autor) entiendan fácilmente el código, lo puedan utilizar, ampliar o mejorar.
  • Clara definición de hitos, si me propongo desarrollar una funcionalidad, quiero saber en qué momento exacto he cruzado la línea de meta. Nada de liarse a “pues esto estaría bien incluirlo”, o “seguro que en un futuro me sirve…”
  • Lo primero, a por los riegos ¿Juego Multijugador online en java? Eso suena a muchos problemas que urgen ver como se van a abordar (porque afectan a partes fundamentales del juego o pueden modificarlas), o incluso ¡si no tiene una solución viable!

Es obvio enfocarlo sobre un proceso ágil y ¡carajo! los puntos mencionados arriba, son valores dentro de la metodología programación extrema, asi pues… programación extrema ¿Quieres salir conmigo?

Toca buscar y adaptar todas las herramientas/procesos que aporta la metodología y me puedan ser útiles . Dicho esto, paso a «la chicha». De momento me desenvuelvo con los siguientes elementos:

Grandes objetivos del juego: Caja de pandora de todas los aspectos generales para el videojuego. Cada uno de ellas, aglutinará historias de usuario para implementar (hay que decidir cuales van dentro y cual no) Cada uno de estos grandes objetivos, se presenta como un spring. Simplemente lo tengo guardado en un archivo tipo word un ejemplo sería:

  1. Definición de lógica y reglas del propio juego.(Movimientos permitidos, consecuencias de cada uno e interacciones entre objetos)
  2. Cliente visual (el cliente ya puede ver algo en su pantalla e interactuar)
  3. Multijugador (Comunicación entre una partida y varios clientes)
  4. Servidores de partidas (Gestion Nuevo/Modif/Elimina/Busqueda de servidores que crean partidas)
  5. Perfiles de los jugadores (Gestion N/M/E/B de perfiles de jugadores)

Historias de usuario (HU): elemento típico de las metodológías ágiles, podéis encontrar mucha info en cualquier buscador, o si es la primera vez que os cruzais, entrar en wikipedia: Historias de usuario que biene bastante bien explicado. Os dejo un ejemplo de una de mi proyecto:

User Story Nº 001 Nombre: Reglas y lógica del espacio
Tipo de actividad: Nueva □ Modificacion □ Extension de US nº:
Prioridad Negocio ALTA Prioridad Desarrollo ALTA
Fecha Inicio Fecha Fin Estimación
Descripción:Las partidas se desarrollan en un escenario. El escenario tiene áreas de movimiento y áreas bloqueadas.Los personajes se pueden mover por las áreas permitidas, previamente definidas por el escenario.Si entran en áreas bloqueadas el movimiento cesará para impedirlo.Los proyectiles, si entran en áreas bloqueadas, tienen que desaparecer.
Observaciones de software: Los objetos y áreas bloqueadas serán tratados como conjuntos de rectángulos

Tareas de ingeniería (TI): para cumplir los requisitos definidos en las historias de usuario, se crean las tareas de ingenierías, que definen, a nivel técnico, funcionalidades a implementar de una historia de usuario determinada. A continuación, un ejemplo de TI que esta asociada a la HU de arriba:

Eng. Task Nº 00111 Nombre: Colisiones de rectángulos
Tipo: Nueva □ Modificacion □ Extension de ET nº:
User Story Nº 001 Nombre US: Reglas y lógica del espacio
Fecha Inicio Fecha Fin Estimacion 6horas
Descripción:
Definir un rectángulo en un espacio de 2 dimensiones.
Función: dado dos rectángulos en un espacio, saber si hay colisiones entre ellos.
Observaciones:

Prueba: pruebas unitarias y generales, pruebas y comprobaciones de todo y para todo. Que quede bien claro, si planteas hacer algo, primero creas la prueba y la lanzas (si, y te fallara evidentemente) Tendrás así, perfectamente definido cual es tu propósito, además, sabrás exactamente cuando lo has conseguido, y puedes pasar a otra cosa, te ayudará a la hora de plantear como vas a implementar tu función (ya sabes: tener claro un problema es tener la mitad de él resuelta). Yo utilizo JUnit, creo una clase test por cada clase que tenga, y luego otra test extra que agrupe las clases de un paquete o secuencia de procedimientos.

No espero tener la forma de trabajo perfectamente definida desde el primer día, también este es un proyecto de descubrir/aprender/experimentar/desarrollar una metodología de trabajo útil y eficaz, asi os pido comentar/debatir sugerencias y propuestas.

Publicado en Uncategorized | Etiquetado , , | 3 comentarios

Función de detección de colisión entre dos rectángulos de un plano

Nota antes de empezar: Esta entrada es una explicación de cómo, partiendo de ningún conocimiento, voy comprendiendo y aproximándome a una solución óptima para detectar colisiones. El fin, es ayudar a otros navegantes, a realizar dicho camino y comprender la lógica de dicha solución.

El primer problema a enfrentarse: un algoritmo de resolución de colisiones entre rectángulos en un plano de dos dimensiones.

He decidido que todos los objetos (personajes, obstáculos, proyectiles…) en el videojuego se representen mediante un conjunto de rectángulos, y por tanto, debo de tener una función que me diga si dos rectángulos han colisionado, o no.

Primeramente, hago un acercamiento con una idea simple, aunque sé que en un futuro, debo de optimizar la función, porque la mayoría de los cálculos en medio de una partida, serán llamadas a la función de si hay colisiones. Paso a explicarla:

Separo las dos dimensiones y analizo si hay colisiones por separado, es decir, tomo los valores de un eje (por ejemplo,  el horizontal) quedaría algo así:

aplano todos los puntos sobre un eje, y me quedo con los puntos extremos de cada rectángulo.

Me quedan un par de puntos por cada rectángulo. Ahora toca saber si alguno de los puntos «aplanados» de un rectángulo está entre los dos puntos del otro rectángulo, y para evitarnos preguntar por cada uno de ellos, me vale con preguntar por los dos extremos, en el ejemplo gráfico sería:

¿Esta el punto r1 entre los puntos v1 y v2? Y el punto r2, ¿está entre los puntos v1 y v2?

¿Esta el punto v1 entre los puntos r1 y r2? Y el punto v2, ¿está entre los puntos r1 y r2?

Por cada respuesta afirmativa, acumulamos un minipunto. Y sabemos que podemos conseguir cero minipuntos o dos en total.

Una vez terminado de preguntar por los puntos de un eje, hacemos lo mismo con el otro eje y acumulamos el total de los minipuntos. Necesitaremos tener un total de cuatro para poder asegurar que hay colisiones entre los dos rectángulos, si no, es que no hay colisión.

Podemos evitarnos hacer las preguntas propias de un eje, si en el otro no hemos conseguido ningún minipunto. Todo esto es muy fácil si vamos representando gráficamente los diferentes casos posibles, y no es desperdiciar tiempo, pues realmente estamos ideando el plan de pruebas (muy necesario si estás empleando la metodología Extreme Programing para tu proyecto, pero eso es ya otra entrada al blog…)

Ahora pego el código de mi función «hay colisiones entre dos rectángulos» de mi clase Rectángulo. Recordaros, que todo el fuente de mi clase, también está disponible en el link de la forja rediris: https://forja.rediris.es/plugins/scmsvn/viewcvs.php/tronco/src/nucleo/Rectangulo.java?root=cusl6-helmetwar&view=markup

[EDITO EL CÓDIGO, que ha sido refactorizado, con la ayuda de David Saltares. Ahora queda mucho más ‘bonico’] Si queréis probar vuestra propia versión, utilizar el test https://forja.rediris.es/plugins/scmsvn/viewcvs.php/tronco/test/nucleo/RectanguloTest.java?root=cusl6-helmetwar&view=markup para saber si la función funciona correctamente

/**
* Definición: Se considerará que hay colisión entre dos rectángulos cuando, exista un punto del espacio tal que corresponda a los dos rectángulos.
* Aclaración: Se considerán puntos de un rectángulo, al conjunto de puntos encerrado entre sus lados, al propio de los lados y sus vértices.
* @param rectangulo Rectangulo con el que queremos saber si colisiona en algún punto.
* @return boolean True si colisiona el objeto y el rectángulo en algún punto, False en caso contrario.
*/
public boolean hayColision(Rectangulo rectangulo){
 //–utilizo la lógica inversa: voy comprobando los casos que conozco que sé que no son colisión, para ir descartando

//–primero compruebo el eje X
if
( (rectangulo.getCoordenadaFinal().getX() < this.getCoordenadaInicial().getX())
|| (rectangulo.getCoordenadaInicial().getX() > this.getCoordenadaFinal().getX()) )
    return false;

//–ahora compruebo el eje Y
else if
( (rectangulo.getCoordenadaFinal().getY() < this.getCoordenadaInicial().getY())
|| (rectangulo.getCoordenadaInicial().getY() > this.getCoordenadaFinal().getY()) )
    return false;
//–podríamos seguir comparando mas dimensiones si las tuvieramos, pero como sólo hay 2, estamos antes una colisión.
else
    return true;
//NOTA: getCoordenadaFinal().getX()/getY() devuelve el valor mas grande del rectángulo de la correspondiente dimensión
// getCoordenadaInicial().getX()/getY() devuelve el valor mas pequeño del rectángulo de la correspondiente dimensión

}

Publicado en Desarrollo, Uncategorized | Etiquetado , , | 9 comentarios

¡Pero voy hacer algo que me guste y cómo yo quiera!

Tenía claro desde el principio, que mi proyecto fin de carrera (PFC) iba a ser de lo que más me gustase, aun a pesar de encontrarme más problemas de lo normal, o tener que aprender nuevas cosas que se requisieran. Todo casi para darme un homenaje (se puede pensar que es algo raro, después de tantas asignaturas cursadas) ¡Pero voy hacer algo que me guste y cómo yo quiera!

Después de la primera reunión con mi tutor del proyecto fin de carrera, y de plantearle cómo iba a querer el videojuego, y de enfocar una meta,  tuve conocimiento del «Concurso Universitario de software Libre» http://www.concursosoftwarelibre.org y por eso, aquí estamos, inaugurando este blog para ir contándoos como va gestando mi jueguecito.

Aprovecho  para comunicar a cualquier compañero de concurso que me lea, que tuve problemas con los correos de registro en la forja «@forja.rediris.es», retrasos por estar su IP en alguna lista negra, en cuestión. Aviso para evitarles sustos.

Publicado en Uncategorized | Deja un comentario