Swipe en cualquier dirección. GM:S

En el ambiente de los juegos para dispositivos móviles, usar solamente teclas virtuales para interactuar con el juego probablemente suponga una limitación para muchos jugadores. Es común que el movimiento de objetos, menús y otros elementos esté asociado a un swipe o barrido. El barrido es un gesto que consiste en deslizar el dedo por una zona de la pantalla para controlar el movimiento de algún elemento. En este tutorial se presenta un ejemplo de Swipe en cualquier dirección para controlar el movimiento de un personaje.

Una pregunta bastante oportuna en este punto es ¿Cómo se implementa un barrido en GM:S?

Pues bien, de manera más que conveniente, resulta que en GM:S, el típico click izquierdo de toda la vida se reconoce como un toque en la pantalla en los dispositivos móviles. Situación bastante favorable ya que todo barrido comienza con un toque en algún punto de la pantalla. No es cosa trivial conocer el punto inicial donde se hace click izquierdo (el comienzo del barrido).

Después de hacer contacto con la pantalla, el dedo se desliza alejándose del punto de contacto inicial... ¿Qué tanta distancia debe deslizarse el dedo? Si fuera una distancia muy corta, un par de pixeles por ejemplo, el swipe sería demasiado sensible, el más ligero deslizamiento provocaría un movimiento o una reacción en el juego. Esto generalmente no es lo deseado. Por último, el barrido forzosamente termina en algún punto, cuando el usuario retira el dedo de la pantalla, o dicho en palabras familiares para un makero: Cuando ocurre un evento "Mouse left released" (Dejar de hacer click sobre el boton izquierdo del ratón).

Teniendo en consideración los puntos anteriores, comencemos ahora sí a programar el swipe en GM:S. En el proyecto hay un objeto llamado obj_mono que es el personaje principal. En su evento [CREATE] está definido lo siguiente:

dir = 0             // Dirección del swipe
avanzar = 0         // 0 = El personaje no avanza; 1 = El personaje avanza
recorrido = 0       // Distancia del swipe
image_speed = 0.2

//Punto inicial del swipe [0] = x; [1] = y
click_0[0] = 0
click_0[1] = 0

// Recorrido mínimo para considerar un swipe (y que el jugador comience a avanzar)
OK_swipe = 40

Como ya se mencionó, el barrido inicia con un toque en algún punto. Se agrega un evento [GLOBAL LEFT PRESSED] que registra el momento en que sucede el click izquierdo; el código de este evento es:

click_0[0] = mouse_x
click_0[1] = mouse_y

Con esto se logra que al hacer click en algún punto de la pantalla (inicio del barrido), ese punto se guarde en el arreglo click_0. Después de tocar la pantalla (o hacer click), el dedo se desliza contactando la pantalla, es decir, el botón izquierdo del ratón sigue presionado, pero avanza en alguna dirección. Que el botón siga presionado is indicio deq ue debemos usar el evento [GLOBAL LEFT BUTTON], en el cuál tengo lo siguiente:

//¿Cuántos pixeles se han recorrido?
recorrido = point_distance(click_0[0], click_0[1], mouse_x, mouse_y)

//¿En qué dirección?
dir = point_direction(click_0[0], click_0[1], mouse_x, mouse_y)

if ( recorrido >= OK_swipe ) and ( click_0[0] > -1 )
    avanzar = 1        //Bandera para avanzar, se usa en [STEP]

En el código anterior determina la distancia que va recorriendo paso a paso el puntero (mientras no se libere el botón del ratón). Nótese que las coordenadas iniciales x e y se leen del vector click_0[]. También se calcula la dirección del swipe, usando las mismas coordenadas que en la función anterior. Al final se valida el barrido, es decir, se verifica que sea al menos igual al valor de OK_swipe. La condicion ( click_0[0] > -1 ) nos sirve para asegurarnos que las coordenadas iniciales que se usan en "recorrido" y en "dir" sean actuales, y no coordenadas de swipes anteriores (más detalle en el siguiente párrafo). Si las dos condiciones del if se cumplen, entonces se "avisa" que se permite avanzar al jugador.

Recapitulando, ya tenemos el código que lee el punto inicial del swipe, y también el que calcula la magnitud (distancia) del barrido. Ahora resta el código en el que finaliza el swipe, es decir código para el evento [GLOBAL LEFT RELEASED]:

avanzar = 0          //Detener movimiento del jugador

//descartar el punto inicial al terminar el swipe (evita movimientos no deseados)
click_0[0] = -1
click_0[1] = -1

Resulta lógico que al terminar el swipe, detengamos el avance del jugador. También eliminamos las coordenadas del comienzo del swipe, sustituyendo sus valores por -1 (recordar la condición del if en el evento [GLOBAL LEFT BUTTON] ). Así nos aseguramos de que los barridos siempre usen los puntos iniciales más recientes

El sistema swipe está completo. Pero aun nos falta un paso muy importante: ¡Programar el movimiento del jugador! Hasta ahora sólo hemos hecho que GM:S detecte swipes en cualquier dirección, pero esos swipes no tienen (de momento) relación con el movimento del jugador. Bueno, vamos a ello. Voy a colocar todo el código restante, que va en el evento [STEP] del jugador:

if (avanzar) {
    image_speed = 0.2        //Animar sprite sólo si el jugador se desplaza
    direction = dir          //Dir se calculó en el evento GLOBAL LEFT MOUSE
    speed = 3                //Velocidad del jugador (cambiar al gusto ;)
}
else {
    image_speed = 0
    friction = 0.1             //No parar bruscamente.
}

//Impedir que el personaje salga de los límites de la habitación
x = clamp(x, 0, room_width)
y = clamp(y, 0, room_height)

//Elección de sprite según dirección
if (dir > 90) and (dir < 270)
    image_xscale = -1       // "espejear" (izquierda)
else
    image_xscale = 1        // derecha

if ( dsin(abs(dir)) <= 0.70 )
    sprite_index = spr_right //Animación lateral (izq y der)

if ( dsin(dir) > 0.70 )
    sprite_index = spr_up

if ( dsin(dir) < -0.70 )
    sprite_index = spr_down

Quiero mencionar es que a pesar de que el ejemplo realiza swipes en cualquier dirección, sólo uso 3 sprites para mostrar animaciones en 4 direcciones distintas: Un sprite animado para mostrar la animación derecha e izquierda. Un sprite para la animación hacia arriba y otro sprite para la animación hacia abajo. Obviamente lo ideal sería usar animaciones para los movimientos en diagonal, pero el objetivo del ejemplo es primordialmente explicar el concepto del barrido. El código [STEP] creo que tiene suficientes comentarios, por lo que omitiré su explicación, pero si tienes cualquier duda, no dudes en contactarme.

Aquí finaliza el tutorial sobre barridos. ¡Gracias por pasar y hasta la próxima Estupidez Artificial!

Penumbra.