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);
}