Como usar mapas en nuestro juego
Hoy hablaremos de como usar un mapa en nuestro proyecto, ya que es la estructura de datos más usada cuando hacemos juegos móviles (luego veremos porque). Un mapa es un tipo de datos complejo, con una característica especial: para poder acceder a los valores de esos datos tenemos una clave que podemos definir como queramos. por ejemplo, podemos tener el siguiente inventario:
monedas : 1500 pociones : 10 escudo : 1
Estos datos se pueden almacenar en un mapa, donde las claves sería monedas, pociones y escudo, y cada clave tendría su valor correspondiente.
Los valores que se pueden almacenar son tanto números como texto, así que también podíamos asignar objetos. Un ejemplo de equipación de un personaje más inventario:
head : obj_casco body: obj_armadura pocion verde: 3 oro: 230
Con este par de ideas ya nos podemos imaginar las utlidades que tiene esto.
Los mapas no tienen ningún orden concreto, ya que accedemos directamente con su clave. Así que recorrer todo un mapa en un bucle es bastante lento. Otro tema a tener en cuenta es que una clave no puede tener dos valores, ni se pueden usar dos claves para la misma cosa.
Ahora vamos a ver un listado de todas las funciones para usar los mapas.
Funciones para gestionar mapas
Seguiremos con el ejemplo del inventario. Para crear un mapa sería así:
inventario = ds_map_create();
Para eliminar un mapa de la memoria, recordemos que no se destruye cuando eliminamos el objeto, sería
ds_map_destroy(inventario);
Ahora vamos a añadir unas monedas al inventario
ds_map_add(inventario, "monedas", 150);
Con esta función creamos la clave y su valor. También podemos tener una clave númerica, como si fuese una array
ds_map_add(inventario, 1, "nombre");
Recordemos que los mapas no tienen orden, no pensemos que al añadir un elemento se coloca al final del mapa.
Si la clave ya existe, podemos usar la siguiente función para reemplazar su valor
ds_map_replace(inventario, "monedas", 348);
Para eliminar la clave y valor de un mapa
ds_map_delete(inventario, "monedas");
Para gestionar el mapa, no me refiero a los datos sino a toda la estructura, tenemos una serie de funciones útiles. Por ejemplo, si queremos copiar un mapa en otra variable
ds_map_copy(inventario, inventario_copia);
Para vaciar el mapa entero
ds_map_clear(inventario);
Para saber si un mapa está vacío
if ds_map_empty(inventario) ...
También podemos saber el tamaño de un mapa, que nos devuelve el total de claves/valores que tiene (por si queremos limitar la capacidad de un inventario, como ocurre con Pokemon Go)
ds_map_size(inventario);
Ahora vamos a leer el mapa. Si conocemos la clave es muy fácil,
ds_map_find_value(inventario, "monedas");
Si no existiese la clave, la función devuelve <undefined>
, nada de 0 si es un número, o ” si es texto. Para poder tratar este valor, tenemos la función is_undefined()
.
Ahora vamos a recorrer todo el mapa. Recuerda que no tiene ningún orden, así que accedemos a la primera clave que tenemos y luego vamos buscando. Este proceso es muy lento, avisan los de Game Maker Studio, así que yo lo usaría solo para mostrar todos los valores. Aquí un ejemplo buscando un valor
var total = ds_map_size(inventario) ; var primero = ds_map_find_first(inventario); for (var i = 0; i < total; i++;) { if primero != "monedas" { first = ds_map_find_next(inventory, first); } else break; }
Vamos con las funciones comunes a todas las estruturas de datos, que es para leer y guardar un mapa en un fichero. Podemos usar tanto un fichero de texto estándar como un ini.
Para grabar el inventario en un fichero ini usaremos ds_map_write() de esta manera
ini_open("player.ini"); var mapa = ds_map_write(inventario); ini_write_string("Player", "inventario", mapa); ini_close();
En el caso contrario, para leer con ds_map_read()
, podemos usar el siguiente código:
inventario = ds_map_create(); ini_open("player.ini"); var map = ini_read_string("Player", "inventario", ""); if map != "" { ds_map_read(inventory, t_string); } ini_close();
Accesors de un mapa
Ya hablamos de los accesors en las listas. El accesor de un mapa es el símbolo ?, así que
map[? "monedas"] = 890;
De esta manera podemos crear claves, valores y reemplezar los valores de una clave ya existente sin tener que escribir mucho código.
Como encriptar un mapa en nuestro disco
Si os fijáis en muchos eventos y funciones de sistema, sobretodo los relaciones con eventos asíncronos, nos puede devolver un mapa, llamado async_load con varios valores como puede ser el id de un jugador, un mensaje, etc. ¡Así que conocer como funcionan los mapas es muy importante!
¿Por qué comento esto? Porque en los juegos que hagamos para móviles o tablets, hay una serie de datos que tenemos que manejar con especial cuidado. Obviamente, estamos hablando de cuando gestionamos dinero. ¡Money money!
Cuando hacemos un juego con posibilidad de hacer compras, lo que comunmente se llama in-app purchases o comprar in-app, vemos que las funciones relacionadas gestionan mapas. Estos datos es recomendable almacenarlos en el dispoitivo, por ejemplo, si nos quedamos sin conexión a la red (quedaría poco profesional que hayas comprado niveles extras de un juego y no aparezcan simplemente porque no tengas WIFI).
Para almacenar estos datos, Game Maker Studio nos proporciona funciones de lectura y guardado encriptadas de mapas. Estas funciones son ds_map_secure_save()
y ds_map_secure_load()
. Podemos ponerle cualquier nombre o extensión al fichero, para poder guardar varios, además que se guarda en un sitio seguro (así que seguramente no esté disponible en el directorio de trabajo.
Estruturas complejas de un mapa
Ahora que ya sabemos lo que es un mapa, podemos complicarnos un poco más. ¿Como se podría hacer? Poner en una clave muchos valores. La manera más común de hacerlo es añadir en una clave una lista ya creada.
Seguimos con el mismo ejemplo del inventario. Tendremos una clave que será “armadura”, y en ella almacenamos todos los elementos que vamos añadiendo: casco, coraza, brazos, pies, etc. Podemos tener una lista con todos los elementos de la armadura, porque sabemos que tendremos un elemento de cada, y luego añadimos toda la armadura a nuestro inventario con ds_map_add_list()
. Un ejemplo:
var list_armadura = ds_list_create(); ds_list_add(list_armadura, obj_casco_cuero); ds_list_add(list_armadura, obj_coraza_bronce); var inventario = ds_map_create(); ds_map_add_list(inventario, "armadura", list_armadura);
Podemos editar la lista con list_armadura y la tendremos actualizada en el mapa, para que sea más fácil acceder al inventario. Con la función ds_map_replace_list()
podemos sustituir una lista ya guardada.
¿Podríamos añadir en mapa dentro de otro mapa? Si, con la función ds_map_add_map()
. De esta manera podemos ir encandenando mapas y mapas y tener una estructura de datos muy parecida a una estructura de XML.
Mapas y ficheros JSON
Game Maker Studio no gestiona ficheros XML, pero si que gestiona ficheros JSON (son muy útiles sobretodo para juegos en HTML5). Son muy parecidos, solamente tiene otra estructura definida. ¿Y para que sirve un fichero JSON? Es una manera de tranportar datos vía web.
¿Os acordáis cuando hemos dicho más arriba sobre las compras in-app? Al llamar a un servidor externo, los datos viajan en formato JSON, y al leerlos con el evento Sociallo captamos en un mapa.
Es decir, podríamos leer y grabar datos de un servidor externo utlizando mapas. Game Maker Studio tiene dos funciones para hacer esta gestión:
json_encode()
. Cogemos un mapa y lo codificamos en una estructura JSON para enviar.json_decode()
. Tenemos una estructura JSON y la añadimos a un mapa.
Dependiendo de como sea esa estructura, que se supone debemos conocer, es posible que dentro de ese mapa tengamos listas y otros mapas tal y como hemos aprendido. Si queremos analizar el mapa obtenido, tenemos las funciones que hemos visto para recorrer el mapa y saber su tamaño.
Resumen final
Hemos aprendido a usar mapas en Game Maker Studio. Además de ver sus posibles usos, sabemos como guardarlos en disco, además de poder gestionar datos vía web mediante JSON.
¿Te ha parecido útil esta información? ¿Crees que es necesario un tutorial con un buen ejemplo?
Gracias por toda la información sobre GameMaker. Espero con ganas nuevas entregas. Un saludo.
Gracias por los ánimos!
Muchísimas gracias David por todos estos aportes, tanto para los que sabemos programación como los que no saben aún, todo esto nos ayuda a seguir haciendo nuestros proyectos.
Un gran saludo.
Gracias a ti por comentar. ¡Nos vemos!