HTML 2D Game Engine

En esta entrada se muestra un recopilatorio de herramientas y las técnicas utilizadas para implementar este pequeño motor de juegos en 2D. Se basa en el dibujado de graficos usando el canvas de HTML5 y Javascript.

Live Demo

arrows = move, x or space = Jump

Código fuente

Este es el código fuente del proyecto, recordar que requiere de un servidor web para su correcta ejecución:

https://github.com/sergiss/html-2d

Implementación: WebSockets Parte 2 – agar.io

En esta segunda parte de la entrada WebSockets, he desarrollado una pequeña versión del juego agar.io. Al igual que en la primera parte del post (WebSockets Parte 1), el juego se ha realizado con la arquitectura cliente-servidor, pero con la diferencia que este incorpora la posibilidad de multijugador.

Mejoras:

Aunque la implementación es 100% funcional, se pueden aportar las siguientes mejoras.

  • Implementación de sistema de suavizado de movimiento en la parte del cliente.
  • Nombre y puntuación de los jugadores.
  • Ranking de puntuación.

Código fuente: https://github.com/sergiss/agario-ws

Implementación: WebSockets Parte 1 – Arkanoid

Como ejemplo de una aplicación web en tiempo real, he implementado el juego Arkanoid utilizando WebSockets. A diferencia de una arquitectura clásica, donde el programa corre íntegramente en la máquina que ejecuta el código, en esta implementación la lógica del juego corre en la parte del servidor, y el cliente se dedica a comunicar a este el estado de los controles y a dibujar los gráficos en el navegador (thin client).

Arkanoid Web Sockets
Arkanoid Screenshot

Entre otras, esta estrategia suele utilizarse para la creación de juegos multijugador. Como se ha explicado, las reglas del juego y el estado de los jugadores – posición, puntuación, nivel, vida, etc. – son controlados por el servidor. La carga gráfica y el estado de los controles es manejada por el cliente.

Aunque este juego es de un solo jugador, sirve de muestra de aplicación distribuida usando tecnología WebSockets. En una segunda parte implementaré el juego agar.io como ejemplo de juego multijugador.

Código fuente: https://github.com/sergiss/arkanoid-ws

Librería: HTTP Server

Esta es una pequeña librería que he implementado para Java, la cual permite desplegar un servidor HTTP en cualquier dispositivo que ejecute JVM, permitiendo a sistemas embebidos con recursos limitados o dispositivos móviles que precisen de servicios web, aumentar las posibilidades de comunicación e integración entre sistemas.

Pero dejémonos de palabras y vamos a ver como funciona 🙂

IDE Eclipse – Servidor Web para aplicación de escritorio

  • Primero que nada vamos a crear un nuevo proyecto y añadiremos la librería ‘http-server’ al build path:
Añadir librería al proyecto

En la imagen se muestra la creación del directorio lib y como se ha añadido el jar http-server.1.1.0.jar en el mismo. Finalmente haciendo click derecho sobre el jar se ha seleccionado Build Path > Add To Build Path y así ya está disponible para usarse en nuestro proyecto.

  • A continuación añadimos el contenido de la página que queremos publicar.

De forma homologa como se hace en un «Dynamic web project», reservamos el directorio WebContent para el contenido del sitio web.

Este directorio será la raíz desde la que se atenderán las solitudes de ficheros. Por ejemplo, para atender una petición GET de un navegador en la que se solicita el fichero index.html, el path deberá de ser /miContexto/index.html

Nota: Más adelante se muestra un ejemplo de como registrar los contextos y los ficheros de inicio (i.e. index.html).

  • Por último solo queda escribir el código fuente del programa. 🤓
String host = "127.0.0.1";
int port = 8080;

HttpServer httpServer = new HttpServerImpl(host, port);
WebHandler webHandler = new WebHandler() {
    @Override
    public HttpResponse handleQuery(HttpRequest httpRequest) {
        // Método donde recibiremos las peticiones GET y POST
        // que contengan parámetros, por ejemplo de un formulario
        return HttpResponse.build(Status.NOT_FOUND);
    }

    @Override
    public InputStream toStream(File file) throws Exception {
        return new FileInputStream(file); // Fichero a Stream
    }
};
// Establece el directorio donde están los recursos web
webHandler.setContentFolder("WebContent");
// Registro de contexto de inicio
webHandler.getIndexMap().put("/", "/index.html");
// Establece el HTTP listener, en este caso webHandler
httpServer.setHttpListener(webHandler);
// Conectamos el servidor
httpServer.connect();

try (Scanner scanner = new Scanner(System.in)) {
    System.out.println("Press Enter to exit...");
    scanner.nextLine();
}

Tras la ejecución del código podremos acceder a la página introduciendo en el navegador la URL:

http://localhost:8080

Android Studio – Servidor Web para aplicación móvil

Los pasos a seguir para utilizar la librería en Android son los mismos a excepción de los siguientes puntos:

  • Carga de recursos:

Primero deberemos de almacenar la carpeta WebContent como recurso en nuestro proyecto de Android Studio y modificamos la implementación del método toStream:

@Override
public InputStream toStream(File file) throws Exception {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  String path = file.getPath();
  return classLoader.getResourceAsStream(path);
}
  • Permisos

Por último recordar que para realizar operaciones de red hay que añadir los siguientes permisos en el archivo AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Página del proyecto: https://github.com/sergiss/http-server

Como se puede apreciar en el código fuente, la librería no tiene dependencias con otros proyectos y está implementada usando Java puro, permitiendo la compatibilidad con todas las plataformas existentes.

Espero que esta herramienta sirva de ayuda 😉

SGDK – Implementando Tetris

Introducción

Hace unos años decidí comprar en el mercado de segunda mano la Mega Drive, consola que disfruté de pequeño y que por desgracia dejé de tener en época de los 3D, cuando a casi nadie le importaban ya los 16 bits. Con ella, adquirí un clon chino del Everdrive con la intención de poder recuperar mi colección y jugar a todos los juegos del catalogo.

Cartucho Everdrive clonico
Cartucho Flash

Para quien no lo conozca y en resumen, es un cartucho que consiste de dos partes, la tarjeta SD donde se almacenan los juegos (en concreto ROMS en formato .bin) y el cartucho en si, con su memoria ROM donde se vuelca el juego elegido a través de la pequeña interface que aparece al encender la consola. Este cartucho te permite jugar a cualquier juego de la plataforma y permite revivir la experiencia al 100% del juego original.

Con todo esto y debido a mi necesidad de aprender cómo funcionan las cosas, se me ocurrió realizara un pequeño juego para la Megadrive, un port del juego Tetris de la NES.

Antes de empezar

SGDK es un kit de desarrollo gratuito y abierto para Sega Megadrive, el cual a partir de código en C compila el videojuego en una imagen ROM. Para desarrollar con SGDK, se pueden utilizar varios entornos de desarrollo, pero por experiencia personal recomiendo Code::Blocks, ya que por la cantidad de información disponible en la red, es el más indicado para este menester.

IDE Code Blocks
IDE Code::Blocks

Para agilizar el proceso de desarrollo y debug, recomiendo el uso de algún emulador, ya que con la creación de un script (ver start.bat del código fuente), podemos ejecutar fácilmente el juego tras cada compilación.

Encajando piezas

El juego de Tetris tiene un conjunto de siete piezas formadas por cuadrados (tetrominós), donde cada una de las piezas tiene 4 posibles rotaciones.

Tetromino Tetris
Tetrominós

Para implementar el sistema de piezas utilizo un array de una dimensión, en el que el valor cero indica un espacio vacío, y un valor mayor que cero indica el ‘tile’ que se mostrará en pantalla.

const u8 piece[64] = {
    0,0,0,0,
    0,0,0,0,
    2,2,2,0,
    0,0,2,0,

    0,0,0,0,
    0,2,0,0,
    0,2,0,0,
    2,2,0,0,

    0,0,0,0,
    2,0,0,0,
    2,2,2,0,
    0,0,0,0,

    0,0,0,0,
    0,2,2,0,
    0,2,0,0,
    0,2,0,0
};

En el Array 1D del código anterior se almacenan los datos de la pieza «L». La longitud del mismo es de 64 (4 columnas x 4 filas x 4 rotaciones). Para acceder a cada una de las celdas en un espacio de 2 dimensiones, la estrategia a seguir es la siguiente:

rot; // Rotación de la pieza (0-4)
...
int x, y, v, off = rot * 16;
for(x = 0; x < 4; ++x)
  for(y = 0; y < y; ++y)
  {
    v = piece[x * 4 + y + off];
    ...

  }

Donde la variable ‘rot‘ con valor de 0 a 4, indica la rotación de la pieza y se usa para calcular el desplazamiento dentro del array en saltos 16 espacios (4 x 4). Al iterar las 4 filas y columnas obtenemos el valor ‘v‘, recogido de la posición obtenida por la operación ‘x * 4 + y + off‘, en el que podremos saber si se debe dibujar un cuadrado y de que color (en este caso el tile) será.

Tablero y colisiones

Una vez más el tablero esta formado por un Array de una dimensión de longitud 200 (10 filas y 20 columnas), en el que se irán almacenando las piezas tras su inserción.

Tetris board
Tablero Tetris

Para la detección de colisiones de la pieza actual realizo la siguientes estrategia:

// Movimiento horizontal
if (left pulsado && !get_collision(x - 1, y)) // si se mueve a la izquierda
   x--;

if (right pulsado && !get_collision(x + 1, y)) // si se mueve a la derecha
   x++;

// Movimiento vertical
if(tiempo_gravedad || down pulsado) { // si desciende una posición
   tiempo_gravedad = 0;
   if(get_collision(x, y + 1)) {
      y++;
   } else { // la pieza ya no puede avanzar y se inserta
      inserta_pieza();
      crea_nueva_pieza();
   }
}

El método para la detección de colisiones itera los elementos de la pieza teniendo en cuenta la posición y rotación de la misma y los compara con el contenido del tablero, como se muestra a continuación:

int get_collision(int posx, int posy, int rot) {
    const u8* piece = pieces[current_piece];
    int local_x, local_y;
    int off = piece_off * rot;
    int i;
    for(i = off; i < off + piece_off; ++i) {
        if(piece[i]) {
            local_y = (i - off) / piece_size + posy;
            if(local_y > 19) return 1;          // bottom collision
            local_x = (i % piece_size) + posx;
            if(local_x < 0) return 2;           // left collision
            if(local_x > 9) return 3;           // right collision
            if(board[local_y * 10 + local_x]) { // board collision
                return 4;
            }
        }
    }
    return 0;
}

Conclusiones

Sin entrar en muchos detalles se ha explicado las claves de la implementación del Tetris que realizado para la Megadrive usando el kit de desarrollo SGDK. Como la naturaleza de este post es de carácter orientativo, se recomienda la visualización y manipulación del código fuente que dejo a continuación. Un saludo y larga vida a Sega.

Código fuente

A continuación dejo el enlace de github para quien desee trastear con el código fuente:

https://github.com/sergiss/sgdk/tree/master/tetris