De Guante Glanco – Gameplay

Añadimos un primer Gameplay del primer nivel del juego, desarrollado en la Universidad de Cádiz durante el año 2012 en la asignatura Diseño de Videojuegos.

Desarrolladores: Javier Dávila, Juan Manuel Coello, Jose Pablo Tur and Michaël Martin.

Autómatas de enemigos

Autómatas de enemigos

Autómata de estados explicando el comportamiento de dos de los enemigos:
Guardias:
– Patrulando: el guardia se mueve desde todos los puntos definidos en su patrulla Activado por: el estado por defecto
– Búsqueda: Qué: el guardia pase al último lugar que ha visto al jugador en Accionado por: colisión entre linternas limitan triángulo y rectángulo de selección del jugador.
– Vuelta a Patrullando: Qué: el guardia retorna a su estado de última patrulla. Accionado por: El guardia llega al sitio donde vio el jugador por última vez.

Sensores de movimiento:
– ON: Qué: Si el jugador choca con el rayo, los guardias cercanos serán alertados. Activado por: el estado por defecto, o un temporizador parpadeante situado en el fichero de configuración.
– OFF: Qué: El rayo está oculto y no pasa nada si el jugador choca con él. Accionado por: El reloj de parpadeo encuentra en el archivo de configuración.

Colisiones

Una vez tenemos creado el nivel con Tiled, lo exportamos en formato JSON. A continuación, lo cargamos en el motor del juego haciendo lo siguiente:
def tilemap
    file: your_map.json
end
En este momento, es cuando tiene que tenerse en cuenta las posibles colisiones entre las paredes del escenario y los personajes. Para ello, se ha de añadir una línea al código anterior indicando los identificadores de los tiles no traspasables tal que así:
def tilemap
    file: your_map.json
    walls: 1,2,3,4,6,12,15,23
end

Los identificadores de los tiles se obtienen contando de arriba a abajo y de izquierda a derecha en los tilesets.

Yendo más allá, ¿cómo funciona nuestro sistema de colisiones?

Si sólo usáramos una grilla para todo, sería bastante complicado detectar de manera precisa la interacción entre personaje y guardias. Es por eso por lo que tenemos creado un sistema de detección de colisiones más complejo, haciendo uso de una abstracción mayor mediante el uso de elementos geométricos y otras cosas.
Nuestra API geométrica se compone de tres elementos:
– Puntos,
– BoundingBoxes,
– BoundingTriangles
Somos capaces de detectar un número bastante alto de colisiones 2D con este sistema, como línea con línea, línea con caja, caja con caja, punto con caja, triángulo con caja, punto con triángulo, y con la posibilidad de añadir más de manera fácil si fuera necesario.
 Mostramos parte del código de detección de colisiones:
Línea con línea:
static bool LineIntersection(Point p1, Point p2, Point p3, Point p4)

{
float x1 = p1.x, x2 = p2.x, x3 = p3.x, x4 = p4.x;
float y1 = p1.y, y2 = p2.y, y3 = p3.y, y4 = p4.y;

float d = (x1 – x2) * (y3 – y4) – (y1 – y2) * (x3 – x4);
if (d == 0)
return (false);// parallels

// Get the x and y
float pre = (x1*y2 – y1*x2), post = (x3*y4 – y3*x4);
float x = ( pre * (x3 – x4) – (x1 – x2) * post ) / d;
float y = ( pre * (y3 – y4) – (y1 – y2) * post ) / d;

// Check if the x and y coordinates are within both lines
if (x < MIN(x1, x2) || x > MAX(x1, x2) || x < MIN(x3, x4) || x > MAX(x3, x4)) return (false);
if (y < MIN(y1, y2) || y > MAX(y1, y2) || y < MIN(y3, y4) || y > MAX(y3, y4)) return (false);
return (true);
}
Caja con triángulo:
bool Intersection(TBoundingBox<T> box) const

{
Point<T> box1, box2, box3, box4;

box1 = box.GetPosition();
box2 = box1;
box2.x += box.GetSize().x;
box3 = box1 + box.GetSize();
box4 = box1;
box4.y += box.GetSize().y;
return (Point<T>::LineIntersection(box1, box2, vertex1, vertex2)
|| Point<T>::LineIntersection(box1, box2, vertex2, vertex3)
|| Point<T>::LineIntersection(box1, box2, vertex3, vertex1)
|| Point<T>::LineIntersection(box2, box3, vertex1, vertex2)
|| Point<T>::LineIntersection(box2, box3, vertex2, vertex3)
|| Point<T>::LineIntersection(box2, box3, vertex3, vertex1)
|| Point<T>::LineIntersection(box3, box4, vertex1, vertex2)
|| Point<T>::LineIntersection(box3, box4, vertex2, vertex3)
|| Point<T>::LineIntersection(box3, box4, vertex3, vertex1)
|| Point<T>::LineIntersection(box4, box1, vertex1, vertex2)
|| Point<T>::LineIntersection(box4, box1, vertex2, vertex3)
|| Point<T>::LineIntersection(box4, box1, vertex3, vertex1));
}
Debido a que todas nuestras formas están compuestas por puntos, podemos rotarlas usando el método siguiente del objeto Punto:
inline Point<T> RotateRad(float angle, Point<T> origin) const

{
Point<T> p(*this);
float s = sin(angle);
float c = cos(angle);

p.x -= origin.x;
p.y -= origin.y;
float xnew = p.x * c + p.y * s;
float ynew = -p.x * s + p.y * c;
p.x = xnew + origin.x;
p.y = ynew + origin.y;
return (p);
}
Este sistema de colisión es usado por los guardias para detectar al jugador de la siguiente manera : se genera un triángulo imaginario creado por la luz de la linterna del guardia, el cual es rotado según el movimiento del propio guardia. Luego una detección de colisión de caja con triángulo se dispara cuando existe interesección con la BoundingBox del jugador, y si la comprobación es positiva, es mejor que salgas corriendo lo más rápido que puedas.
Código de detección del guardia:
void Guard::ExecuteFlashlight(void)
{
RotateFlashlight();

BoundingTriangle triangle = FlashlightTriangle();
BoundingBox box = _player.GetUnitSprite().GetBoundingBox();

box.SetPosition(_player.GetPosition());
if (FlashlightTriangle().Intersection(box))
SeekPlayer();
}

BoundingTriangle Guard::FlashlightTriangle(void) const
{
Point<int> center = Point<int>(84, 60);
Point<int> position = this->GetPosition() + _light.GetPosition();
BoundingTriangle triangle;

triangle.vertex1 = Point<int>(20, 30);
triangle.vertex1 = (center + (triangle.vertex1 – center) * _flashSizeFactor);
triangle.vertex2 = Point<int>(20, 90);
triangle.vertex2 = (center + (triangle.vertex2 – center) * _flashSizeFactor);
triangle.vertex3 = Point<int>(79, 60);
triangle.vertex3 = (center + (triangle.vertex3 – center) * _flashSizeFactor);
triangle = triangle.RotateDeg(-_light.GetRotation(), center);
triangle.vertex1 += position;
triangle.vertex2 += position;
triangle.vertex3 += position;
return (triangle);
}

Subimos un gráfico inicial del Gameloop del juego:

Image

Subimos un grá…

Internacionalización

Todas y cada una de las partes textuales del juego se han implementado usando un módulo de internacionalización que puede ser configurado a través del propio menú principal.

Cada cadena de caracteres es referenciada en el juego mediante el uso de una clave. Cuando se necesita mostrar el contenido de la misma, se busca en un archivo de configuración: si la clave tiene su correspondiente traducción en el idioma actual del juego, se muestra la cadena traducida por pantalla. En el caso de que no sea así, el propio nombre de la clave será el que se muestre.

Este sistema permite que un juego pueda ser traducido a diferentes lenguajes incluso después de su lanzamiento, y por otra parte permite que un juego pueda ser jugado incluso con traducciones incompletas.

Tilemaps

Para elaborar los tilemaps, hemos decidido utilizar el famoso editor de tiles Tiled. Este programa nos da la posibilidad de elaborar directamente los mapas en el editor y exportar los mismos en JSON, evitando el uso de XML por ser éste un formato más pesado y necesitado de parseo.

Varios objetos se han desarrollado para llevar a nuestro soporte del motor del juego casi completamente en Tiled: tilemaps, tilesets,capas y opacidad.

Motor del juego

Con el fin de facilitar el desarrollo del juego, hemos trabajado en un motor de juego propulsado por el poder de Gosu.

Se trata de implementar una gran cantidad de funciones no compatibles con una biblioteca simple de gráficos como Gosu: archivos de configuración, sprites, cámaras, detección de colisiones, pathfinding, gestión de tiempo, organización de eventos… Todas estas características han sido implementadas en el motor del juego, por lo que se pueden utilizar directamente en nuestro proyecto, incluso si se plantea su exportación para su uso en otros juegos.

Comportamiento de los guardias

En cada uno de los niveles tenemos que evitar a los guardias que están buscando a intrusos como nosotros.

Los guardias pasan por diferentes estados con el fin de encontrarte. El primero de estos estados es el de patrulla, en el cual recorren diferentes puntos del mapa especificados en el archivo de nivel. Para localizarte usan una linterna que mueven en varias direcciones con un haz de luz en forma de triángulo, que se utiliza para detectar colisiones con el jugador.

En el caso de que el jugador colisione con el haz de luz, el guardia pasa a estado de búsqueda: avanza más rápido, dirigiéndose a toda prisa hacía el último punto en el que se encontró el jugador. Para la búsqueda se usa internamente el algoritmo A*.

El último estado por el que pasa un guardia es el de tranquilidad: una vez perdida la vista del jugador, el guardia volverá a patrullar tranquilamente por su zona.

Cómic del videojuego, presentación

Como idea para complementar el juego, vamos a incluir un pequeño cómic de cuatro viñetas que aparecerá una vez se le de a Jugar, en el Menú Principal, y antes de que aparezca la primera pantalla de juego.

El cómic es totalmente original; cada viñeta ha sido individualmente vectorizada en su totalidad con el programa de software libre Inkscape, y las viñetas han sido posteriormente montadas con GIMP , y se ha tomado algunos rasgos inspirados en el dibujante Francisco Ibañez, y sirve para introducir al jugador al juego, con una breve historia.

El cómic se podrá saltar pulsando la tecla Escape (Esc), para no aburrir al usuario por cada vez que quiera empezar el juego de nuevo.

1. Una noche tranquila de verano.

Image

2. Un ruido rompe el silencio en la noche.

Image

3. Un atraco se está produciendo.

Image

4. Manolo ‘el Cluni’ se ha colado en el Museo sin ser visto.

Image

Diseña un sitio como este con WordPress.com
Comenzar