Como hacer una aventura gráfica (diálogos)
Uno de los juegos que tuvieron su edad de oro el siglo pasado y que se resisten a morir son las aventuras gráficas. En ejemplo clarísimo de un juego (que me encanta) es The Last Door, que tiene todos los elementos de los clásicos, pero adaptado a los nuevos tiempos. Otro ejemplo de un juego español sería Dead Synchronicity, que nos muestra un futuro distópico, y un clásico entre nosotros sería la compañía Double Fine.
Todos estos ejemplos, que se pueden comprar en varias plataformas, como Android y videoconsolas, nos indica que esta categoría de juegos todavía está muy vigente. Y aquí viene la pregunta: ¿se puede hacer una aventura gráfica en Game Maker Studio? No es el tipo de juego más sencillo de hacer, porque hay que pulir muy bien todos los detalles. Pero obviamente, se puede hacer, aunque hay que tirar más de GML que de acciones, porque requieres hacer cosas más complejas.
En este post intentaré ayudar para hacer una de las cosas que pueden parecer más complicadas: las conversaciones o diálogos que aparecen en una aventura gráfica. La conversación típica será la que aparece un texto o varios seguidos, y después se puede elegir entre dos o más opciones para que la conversación cambie dependiendo de la opción elegida.
Este tipo de conversaciones son típicas en aventuras gráficas, como hemos dicho, pero también lo podemos ver en juegos RPG, tipo Pokemon o Libroaventuras (y lo que se te ocurra). Así que también serán válidos para cualquier juego. Para crear las conversaciones, usaremos un programa externo que nos facilita la tarea, y luego veremos como podemos aprovecharlo en Game Maker.
Introducción a la aventura gráfica en Game Maker Studio
Veamos una imagen de un ejemplo de aventura gráfica que tengo en una de las muchas pruebas y minijuegos que tengo almacenados.
Vemos al protagonista, que podrá interactuar con tres elementos: la estantería, el calendario y el alien recepcionista. En los dos primeros, hará un comentario del tipo:
Un calendario desfasado.
o
Es el armario de las llaves de las habitaciones.
Antes de coger la única llave que queda, creo que es mejor hablar con el recepcionista.
Para indicar una pista con quién debe de hablar. Con el recepcionista será un poco más complicado: tendremos una conversación, podremos decidir si seguimos la conversación o no y habrá que tomar alguna decisión. Este tipo de conversación tiene una estructura de árbol: depende de por dónde vayamos la conversación (y decisiones cambia). Puede ser que de esta manera consigamos un objeto o no. Podemos representarlo de la siguiente manera:
Desde que empezamos la conversación, tenemos múltiples finales, depende de la elección tomada. Cuando hablamos de las estructuras de datos de Game Maker, hablamos de muchos tipos (pilas colas, mapas), pero no de un árbol. Pero tranquilo, se puede montar un árbol con los tipos de datos que ya tenemos.
En el ejemplo de la aventura gráfica, yo había montado todas las conversaciones en ficheros ini, y funciona de maravilla. Pero si la cosa se complica, editar directamente el fichero puede ser un poco caótico, porque cuesta un poco hacer el seguimiento de por dónde va la conversación. Pero hace poco descubrí un programa que sirve precisamente para eso. El programa se llama Twine.
¿Qué es Twine?
Twine es un programa opensource que te permite hacer historias interactivas, es decir, para los libro aventuras de siempre. Puedes probar la herramienta online, que tiene un manejo muy sencillo, y te ayuda a la hora de generar el árbol que hemos comentado. Tenemos un pasaje con un texto, que podemos darle el formato que queramos, y luego podemos poner enlaces a otros pasajes. Véamoslo con un ejemplo.
En las conversaciones simples que hemos dicho antes, tendríamos pasajes únicos con el texto. Creamos un pasaje, le damos un título (que lo usaremos como el identificador o palabra clave del pasaje para distinguirlo entre todos los demás), podemos añadir etiquetas, y el texto.
Como ves, en el título yo he puesto el objeto, así nos facilitará a la hora de asignar la conversación. También he puesto el carácter especial #, que en Game Maker nos indica el salto de línea, aunque yo lo usaré para mostrar la conversación en tramos diferentes. Me explico mejor en la siguiente imagen:
Ahora en Twine añadimos todas estas conversaciones simples. Las tendremos como pasajes sueltos.
Ahora empezaremos con el pasaje del recepcionista, que es un pelín más complicado. Para relacionar un pasaje con otro se utiliza doble corchete para que el programa lo interprete. Por ejemplo, en el primer pasaje del alien recepcionista sería:
Buenos días, señor turista.#
¿En que puedo ayudarle?[[Hola. Tengo una habitación reservada->temp_01]]
[[De momento nada. Voy a seguir rondando por aquí->temp_02]]
En el enlace al siguiente pasaje, ponemos el texto a mostrar, seguido de una flecha y el nombre del pasaje. Si no existe lo crea. En el siguiente pasaje lo mismo, y así hasta montar toda la estructura.
Los nombres de esos pasajes los he puesto como temp, porque al estar dentro del principal no haría falta poder usarlo en Game Maker (lo veremos más adelante). De esta manera, podemos poner todas las conversaciones que queramos en el fichero. Si nos fijamos, vemos que hay un icono de un cohete en el primer pasaje. Si le damos al botón de reproducir, podemos probar la conversación en el mismo programa, ya que lo exporta a HTML. Si tenemos varias conversaciones, podemos marcar la que nos interesa, o con el botón derecho nos aparece un menú, y podemos darle a la opción de Probar.
Ahora veremos como podemos aprovechar el potencial de Twine para nuestros juegos de Game Maker.
Exportar historia de Twine para Game Maker
¿Cómo podemos usar una historia hecha en Twine en Game Maker Studio? Al ser un proyecto Open Source, encontramos multitud de colaboraciones y proyectos que hace la gente para mejorar en la comunidad. Uno de esos proyectos es TwineJson, que permite exportar una historia de Twine a formato Json. Voy a explicar todos los pasos, aunque están en el enlace:
- En la pantalla principal de Twine, dónde aparecen todas las historias creadas, le damos al botón Formatos.
- En la pestaña Añadir formato nuevo, nos pedirá un URL, dónde pondremos la siguiente: https://cau.li/TwineJson/format.js.
- Una vez añadido el nuevo formato, podemos marcarla como Por defecto.
- Ahora entramos a nuestra historia, y en el menú inferior, elegimos Cambiar formato de Historia.
- Elegimos la última añadida, la TwineJson.
- Ahora le damos al botón Reproducir. Nos pedirá dónde queremos guardar los ficheros JSON.
Formato de fichero Json de Twine
Nos creará dos ficheros, uno con la jerarquía que le hemos dado a nuestra historia, y otro con los datos sin jerarquía. En nuestro ejemplo voy a mostrar los dos, primero el plano:
[ { "pid": 1, "position": { "x": 861, "y": 100 }, "name": "obj_estanteria", "tags": [ "" ], "content": "Es el armario de las llaves de las habitaciones.# Antes de coger la única llave que queda, creo que es mejor hablar con el recepcionista.", "childrenNames": [] }, { "pid": 2, "position": { "x": 1012, "y": 94 }, "name": "obj_calendario", "tags": [ "" ], "content": "Un calendario desfasado.", "childrenNames": [] }, { "pid": 3, "position": { "x": 700, "y": 99 }, "name": "obj_cuadro1", "tags": [ "" ], "content": "Un cuadro famoso, será una copia", "childrenNames": [] }, { "pid": 4, "position": { "x": 685, "y": 394 }, "name": "temp_01", "tags": [ "" ], "content": "Vamos a ver. ¿Como se llama? [[temp_01_01]]", "childrenNames": [ "[[temp_01_01]]" ] }, { "pid": 5, "position": { "x": 756, "y": 267 }, "name": "obj_alien_recepcionista", "tags": [ "" ], "content": "Buenos días, señor turista.# ¿En que puedo ayudarle? [[Hola. Tengo una habitación reservada->temp_01]] [[De momento nada. Voy a seguir rondando por aquí->temp_02]]", "childrenNames": [ "[[Hola. Tengo una habitación reservada->temp_01]]", "[[De momento nada. Voy a seguir rondando por aquí->temp_02]]" ] }, { "pid": 6, "position": { "x": 927, "y": 353 }, "name": "temp_02", "tags": [ "" ], "content": "De acuerdo. Nos vemos más tarde. ", "childrenNames": [] }, { "pid": 7, "position": { "x": 817, "y": 507 }, "name": "temp_01_01", "tags": [ "" ], "content": "Me llamo Alien. [[temp_01_01_01]]", "childrenNames": [ "[[temp_01_01_01]]" ] }, { "pid": 8, "position": { "x": 978, "y": 574 }, "name": "temp_01_01_01", "tags": [ "" ], "content": "Hola señor alien. Muchas gracias por elegir nuestro fantástico hotel.# Veamos si usted está aquí apuntado.# ...# Si, aquí está. Se queda la última habitación libre. Coja usted mismo la llave azul del estante.", "childrenNames": [] } ]
Y el jerárquico:
[ { "pid": 1, "position": { "x": 861, "y": 100 }, "name": "obj_estanteria", "tags": [ "" ], "content": "Es el armario de las llaves de las habitaciones.# Antes de coger la única llave que queda, creo que es mejor hablar con el recepcionista.", "childrenNames": [], "children": [] }, { "pid": 2, "position": { "x": 1012, "y": 94 }, "name": "obj_calendario", "tags": [ "" ], "content": "Un calendario desfasado.", "childrenNames": [], "children": [] }, { "pid": 3, "position": { "x": 700, "y": 99 }, "name": "obj_cuadro1", "tags": [ "" ], "content": "Un cuadro famoso, será una copia", "childrenNames": [], "children": [] }, { "pid": 5, "position": { "x": 756, "y": 267 }, "name": "obj_alien_recepcionista", "tags": [ "" ], "content": "Buenos días, señor turista.# ¿En que puedo ayudarle? [[Hola. Tengo una habitación reservada->temp_01]] [[De momento nada. Voy a seguir rondando por aquí->temp_02]]", "childrenNames": [ "[[Hola. Tengo una habitación reservada->temp_01]]", "[[De momento nada. Voy a seguir rondando por aquí->temp_02]]" ], "children": [ { "pid": 4, "position": { "x": 685, "y": 394 }, "name": "temp_01", "tags": [ "" ], "content": "Vamos a ver. ¿Como se llama? [[temp_01_01]]", "childrenNames": [ "[[temp_01_01]]" ], "children": [ { "pid": 7, "position": { "x": 817, "y": 507 }, "name": "temp_01_01", "tags": [ "" ], "content": "Me llamo Alien. [[temp_01_01_01]]", "childrenNames": [ "[[temp_01_01_01]]" ], "parentId": 4, "children": [ { "pid": 8, "position": { "x": 978, "y": 574 }, "name": "temp_01_01_01", "tags": [ "" ], "content": "Hola señor alien. Muchas gracias por elegir nuestro fantástico hotel.# Veamos si usted está aquí apuntado.# ...# Si, aquí está. Se queda la última habitación libre. Coja usted mismo la llave azul del estante.", "childrenNames": [], "parentId": 7, "children": [] } ] } ], "parentId": 5 }, { "pid": 6, "position": { "x": 927, "y": 353 }, "name": "temp_02", "tags": [ "" ], "content": "De acuerdo. Nos vemos más tarde. ", "childrenNames": [], "parentId": 5, "children": [] } ] } ]
¿Véis la diferencia entre uno y otro? Analicemos las claves:
- pid: un autonumérico interno que podemos usar como clave.
- position: la posición dentro de Twine, En nuestro caso esta información nonos itneresa.
- name: el título del pasaje. Podemos usarlo como identificador (o el pid).
- tags: si añadiésemos etiquetas, aparecerían aquí.
- content: el texto largo con los datos.
- childrenNames: Si tiene enlaces, nos aparecerá aquí.
- ParentID: en el Json jerárquico, indica el que nos enlaza.
- children: en el Json jerárquico, dentro hay el pasaje enlazado.
Ahora con estos datos, ya nos podemos imaginar como usarlos en Game Maker.
Añadir diálogos a Game Maker
Yo añadiré el fichero jerárquico a Game Maker Studio. Creamos un proyecto nuevo, y añadimos el fichero como un Included Files (lo podemos arrastrar directamente). Recuerda que si queremos actualizar el fichero, mejor eliminarlo del proyecto y volverlo a arrastrar. Para probar como se ven los datos, añadimos una room nueva y creamos un objeto llamado obj_json. Cuando hablamos sobre el experimento que hice sobre gestión de ficheros grandes, ya expliqué como leer el json y meterlo en un mapa, pero lo volveremos a hacer ahora.
Añadimos un evento Create , y una acción Execute Code
. El código es el siguiente:
///leer json a mapa var json = ""; var file = file_text_open_read("hierarchical aventuragrafica.json"); while (!file_text_eof(file)) { json += file_text_read_string(file); file_text_readln(file); } file_text_close(file); global.conversaciones = json_decode(json); show_debug_message("OK");
He añadido un BreakPoint (con la tecla <F9>) en la función show_debug_message()
, para ver los datos cuando depuremos. El mapa principal lo he puesto como una variable global, para que pueda usarlo en cualquier objeto. Añadimos el objeto a la room y ejecutamos debugando (con <F6>). En el debugger, pongo la opción para mostrar las opciones globales. si no ves los datos de la variable conversaciones, pulsa sobre ella con el botón derecho y elige ds_map.
Vemos que hay solo una clave “default”, y en ella cuatro elementos, así que esa clave corresponde a una lista. ¿Porqué 4 elementos? Veamos todos nuestros pasajes de Twine.
Efectivamente, la lista creada corresponde a los pasajes principales de nuestro historia. Si abrimos el primero en Game Maker, tenemos esto:
Si nos fijamos en las claves que hemos comentado en el Json, tenemos todos los datos aquí. Hay que jugar un poco con cada clave del mapa: name y content son textos, pid un número, childrenNames y children listas vacías. Dentro de content tenemos los textos enlazados también, así que se tendríamos que tratarlos para eliminarlos.
Si abrimos el último elemento (el que tiene un size=7), vemos la jerarquía:
Dentro del primer mapa, tenemos en children una lista con dos elementos, cada uno de ellos otro mapa, que contiene otros children con una lista de un elemento con otro mapa, y así hasta recorrer el árbol de conversaciones, sin que tengamos más hacia abajo.
Y así sólo faltaría programar para recuperar los textos y los datos que nos interese para nuestro aventura gráfica.
Añadir claves nuevas a nuestro fichero de Twine
Uno de las cosas que más me gusta de esta exportación a Json de Twine, es la posibilidad de añadir claves nuevas a cada pasaje. Por ejemplo, imagina que cuando hable nuestro protagonista tenga una fuente, y cuando hable el recepcionista tenga otra. Añadiríamos las dos fuentes al Game Maker, font_alien y font_recepcionista. Ahora en Twine, en el pasaje podemos añadir (el cambio en negrita):
{{fuente}} font_recepcionista {{/fuente}} Buenos días, señor turista.# ¿En que puedo ayudarle? [[Hola. Tengo una habitación reservada->temp_01]] [[De momento nada. Voy a seguir rondando por aquí->temp_02]]
Para no ponerlo en todos los pasajes, el font_alien será el por defecto si no hay nada. Si actualizo el fichero en Game Maker, y volvemos a ejecutar, vemos que tenemos la clave nueva:
El círculo azul con la clave fuente, y en círculo rojo la clave en el texto, además de los enlaces (que deberíamos de quitar). Tenemos que saber que las claves pueden cambiar entre un pasaje/mapa y otro, así que debemos de comprobar si existen antes de acceder a ellas para no tener errores de programación (por ejemplo con la clave fuente).
Resumen y petición final
Bueno, hasta aquí lo de hoy. Hemos visto como aprovechar el potencial de Twine para escribir nuestras historias interactivas. Podemos utilizarlo para exportarlo a un fichero Json, y ese fichero utilizarlo como un mapa en Game Maker Studio.
Ahora viene una pequeña petición. ¿Te interesa este proyecto? Quieres que continúe y haga una segunda parte? Por ejemplo, podría poner el código para formatear todos los campos, y como usarlo en una conversación de aventura gráfica. ¿Y montar un libro aventura? Dime en los comentarios todo lo que se te ocurre, y las posibles mejoras y sugerencias para continuar.
Yo si que estoy interesado en que continúes como realizar la aventura gráfica 🙂
Yo también estoy muy interesado en completar este estupendo tutorial. Saludos!
Le daré una vuelta a ver que puedo ofrecer 😉
Me encantaría ver más!?
pues yo me engancho ahora, así que no pares nuncaaaaaa XD
hola bro no podras hacer un tutorial para la version 1.3 ya que es la que yo uso porfas ya que me revuelvo un poco.
Buenas,
lo habitual es que los tutoriales sean compatibles con todas las versiones, excepto algunas cosas muy concretas que aparecen en un cambio de versión (como puede ser el uso de enums en la versión 1.4).
OK gracias, espero que hagas mas tutoriales de juegos tipo zelda o terror porfas me ayudas muchisimo con tus guias
Tutoriales tipo zelda te recomiendo el de HektorProfe, no creo que pueda superarlo 🙂
Juego RPG paso a paso
Cuando dices uno de terror, ¿tienes algún ejemplo?
Hola, cuando le doy a play no se descarga nada, me salen don recuadros en gris
¿Play? ¿Dónde?
como aplico yo ahora esto??
Bueno, aquí simplemente se habla de los diálogos, pero una aventura gráfica es mucho más.
Poquito a poco 🙂