Movimiento personaje basado en grid
El otro día publiqué un post sobre como movernos en grid en un juego tipo roguelike. Hoy programaremos un script que nos permitirá calcular el movimiento máximo de un personaje dentro de un tablero, que puede ser muy úti8l para un juego por turnos. Voy a explicarme un poco mejor de que va esto…
Imagina que quieres hacer un juego de rol, por turnos, como Heroquest o Warhammer Quest, dónde el protagonista se mueve en una grid predefinida, o las casillas de un tablero, y tiene un movimiento máximo de alcance. ¿Como calcularíamos ese alcance?
Ejemplos de movimiento
Un ejemplo sería si el personaje tiene un movimiento máximo de 1. Sería algo parecido a esto:
Vemos que no nos movemos en diagonales (no tratamos este caso, pero el código será facilmente modificable). Si el movimiento máximo fuera de 2:
Y así incrementamos cada vez. Como no nos podemos posicionar encima de otro jugador o escenario, así que el movimiento posible queda definitivamente así:
Qué haría el script que lo calcula
Y ahora explicamos el script tal y como lo he montado. Para cualquier movimiento, hay que calcular a partir del personaje principal e ir expandiendo cada vez. Por ejemplo, para un movimiento máximo de 1, el cálculo (con la casilla C) es sencillo:
Y esas casillas candidatas se quedan como definitivas para su movimiento (obviando si existiese algún elemento mencionado anteriormente, como elementos de escenografía).
Si el movimiento fuese de un máximo de 2, calculamos el movimiento para un tope de 1, y sobre esos cálculos, que hemos mostrado anteriormente, volveríamos a calcular cada uno de ellos. El cálculo para cada uno de ellos quedaría reflejado así:
Quedando definitivamente todos así:
Y todos estos movimientos ya serían definitivos. Si el máximo de movimiento es 3, habría que repetir el proceso una vez más.
El código del script
Para poder hacer lo comentado, es necesario que el código sea recursivo, así que para facilitarlo al máximo usaremos un script, que podrá ejecutarse, por ejemplo, cuando hacemos click en el objeto protagonista. Para que sea recursivo el script le pasaremos como parámetro el movimiento total del personaje y su situación con las variables x e y. Por cada sitio candidato crearemos un objeto de movimiento llamado obj_mov, que simplemente pintará una casilla de movimiento. El código será:
///scr_mov(total_movement, optional x, optional y) var total_movement = argument[0]; if total_movement == 0 exit; var xx = 0; var yy = 0; var x_next = 0; var y_next = 0; if argument_count > 1 { xx = argument[1]; yy = argument[2]; } else { xx = x; yy = y; } x_next = xx + GRID_SIZE; instance_create(x_next, yy, obj_mov); scr_mov(total_movement - 1, x_next, yy); x_next = xx - GRID_SIZE; instance_create(x_next, yy, obj_mov); scr_mov(total_movement - 1, x_next, yy); y_next = yy + GRID_SIZE; instance_create(xx, y_next, obj_mov); scr_mov(total_movement - 1, xx, y_next); y_next = yy - GRID_SIZE; instance_create(xx, y_next, obj_mov); scr_mov(total_movement - 1, xx, y_next);
Cosas a aclarar en el script
Con Game Maker Studio, podemos usar la variable argument_count para saber cuantos argumentos o parámetros nos pasan en el script. Yo he querido usar los parámetros de x/y como optativo, así en el evento del protagonista solo hay que pasarle el movimiento total mediante scr_mov(2). Por eso en la parte del código
if argument_count > 1 { xx = argument[1]; yy = argument[2]; } else { xx = x; yy = y; }
Detecta si le pasa esos parámetros y, si no es así, usar x/y por defecto.
En la variable x_next y en la variable y_next calculo los posibles candidatos, y en cada uno de ellos vuelvo a llamar a la función scr_mov() restándole al movimiento 1.
También hemos añadido una macro GRID_SIZE, que tiene el tamaño de la cuadrícula (en nuestro caso su valor es 64). Así la podemos usar en cualquier parte de nuestro proyecto.
Como eliminar los movimientos no posibles
Yo he decidido crear un objeto obj_mov para todos los movimientos posibles, y luego en su evento Create eliminaré todos los no posibles. Por ejemplo, usaremos un objeto que es el área del tablero para detectar que los movimientos esten en un área cerrada, y luego decidimos dónde no se puede mover. Otro ejemplo sería un objeto obj_mov, para que no haya dos objetos iguales en la misma casilla, otra unidad llamada obj_unit y un elemento de escenografia llamada obj_tree. El código del evento Create quedará así:
if (place_meeting(x, y, obj_board) == true) and (place_meeting(x, y, obj_mov) == false) and (place_meeting(x, y, obj_unit) == false) and (place_meeting(x, y, obj_tree) == false) { instance_destroy(); }
En la condición podemos añadir todos los objetos que consideremos necesarios (recordemos usar también la herencia para no tener que poner muchos objetos). Esta condición también podríamos haberla añadido en el script, para que no calcule un movimiento que luego toque eliminar, pero lo he hecho así para que quede todo más claro.
Como nota final, este script se puede utilizar de múltiples maneras. Por ejempo, si hay que calcular un alcance de disparo, el script sería el mismo y el objeto creado tendría que mirar solamente si en esa casilla hay un enemigo.
Para acabar del todo, os recomiendo este artículo de la web de Gamasutra, dónde se comenta cuál es el rango de movimiento ideal en una grid.
Si tenéis dudas, no os cortéis en preguntar en los comentarios.
Creo que sería de utilidad explicar en un libro electrónico como hacer juegos de combate por turnos basados en aletoriedad y movimientos basados en grid, gracias
Hola Dan!
Me lo apunto, sería interesante algún tutorial o curso completo sobre un juego de este tipo.
¡Nos vemos!
Muy bien