Scripts poderosos y donde encontrarlos

Juan Pablo
Programando un motor de juegos de aventura
5 min readOct 14, 2016

Grab a stool and relax while I spin a tale of mystery and deceit. A tale of adversity overcome and triumph of the good. A tale of… oh who am I kidding, it’s a bunch of source code. — Ron Gilbert

Estos son los archivos –en su forma original– de Day of the Tentacle, otro juego de aventura genial (también de Lucas Arts).

TENTACLE.exe es el motor.
El juego™ en realidad está en esos .000 y .001.

Todas las imágenes, sonidos, voces, animaciones y los scripts del juego están concatenados en esos archivos (y reducidos al máximo por que eran los 90's y cada disquete costaba).

De vuelta a mi motor, poder cargar imágenes, scripts etc. de alguna parte es indispensable para avanzar… pero todo este empaquetado/desempaquetado me aburre muchísimo… así que haré trampa y lo cargaré —por ahora— desde un folder ‘source’ . Con un API adelante como load_image o similar, puedo cambiar la implementación luego sin cambiar el resto del código.

Pero de lo que quiero realmente escribir es sobre el sistema de scripting.

Scripting: que hay que tener

Esto es lo primero que tenemos que hacer funcionar:

  • Cada escena/cuarto/pantalla tiene propiedades definidas en un archivo: la imagen de fondo, definición de zonas caminables, etc.
  • Cada escena también tiene un poco de código: que debe ejecutarse al mostrar la escena y/o para interactuar con objetos en ella, diálogos, etc.
  • De una escena debes poder pasar a otra(s), así que deben tener nombres.

Hay un caso especial, llamémoslo “el problema del hotel”, y va así: En un juego hay un hotel con cien habitaciones; Casi todas las habitaciones funcionan igual, los mismos objetos, los mismos diálogos, etc., las únicas diferencias son cosas como el color de la cama o la imagen de un cuadro.

Es un caso raro y la mayoría de juegos simplemente hará que tu personaje se no tenga la llave de las habitaciones 😉… pero no es un caso teórico: en Thimbleweed Park hay un hotel así. Su lenguaje de scripting permite “instanciar” una escena, así que les basta un bucle para crear cien… Sería bueno poder hacerlo yo también (sin tener que copiar y pegar el código cien veces).

Y después de darle vueltas por días…, ¡ya tengo diseñada la primera versión del sistema de scripting que cumple con todo esto!

El plan

game.json

(1) Lo primero que hace el motor del juego es cargar un archivo game.json. Este va contener opciones globales como la resolución del juego y otras que aún no decido.

Lo que si tendrá es el nombre/llave de la escena inicial del juego, que también podría ser un intro o un menú, peor el principio es el mismo.

(2) Lo que sigue es buscar entre los scripts los archivos de la escena. Son al menos dos:

  • Un .json que dice cual es la imagen de fondo, que zonas son caminables, la música, etc.; y
  • El código del script. Lo importante es que no estoy usando un import de Python, sino que cargo su versión compilada desde un lugar genérico… ahora es un folder pero más adelante podría ser el dichoso empaquetado con el resto de recursos.

Como ves, hay un solo archivo de script por escena, no hay clases que instanciar (por que si no tendría que explicar lo del self en los métodos) o diccionarios que definir, así el código es lo más legible que puede ser.

Además estoy desactivando los import desde dentro de los scripts; Cualquier funcionalidad especial debería venir del motor.

(3) Con el .json de la escena, cargo los recursos (como la imagen de fondo) y la dibujo en la pantalla. ¡Esto ya empieza a parecer un juego!

antes era “on_start”, pero “on_enter” me parece más apropiado

(4) Ahora ejecuto el script y su función on_enter (si la encuentro en el código) y él se encarga del resto de la lógica.

Como no estoy ejecutando el script en Python de la forma tradicional, puedo insertarle al vuelo todas las variables globales que quiera. Y es justo lo que haré para que tenga cosas como Actor, Image o las que invente (todavía no lo he pensado). El tema es que deja de ser un script de Python y se vuelve mi propio lenguaje basado en Python.

También le agrego una variable game, con el estado global del juego y otra this, con el estado de esa escena. Así, si un jugador rompe un florero, puedo recordarlo con this.broken_vase = True y dibujarlo roto también la próxima vez que se cargue la escena.

Internamente game es un diccionario global this es solo una referencia a game[‘scenes’][<nombre de la escena>]. En el futuro podría serializar game + la posición del personaje en la escena y tener –así de fácil– la funcionalidad de guardar/cargar el juego 😎.

(5) El bucle de eventos (event loop) ya está corriendo.

La escena debe redibujarse en cada pasada, pero el código del script solo se carga y ejecuta una vez.

Ante acciones del usuario, se ejecutarán partes del código, ysi tu personaje sale por una puerta y la escena cambia, regresamos a (2) y repetimos los pasos.

¿Y el problema del hotel?

No puedo instanciar una escena, por que no son clases, pero puedo usar el estado global para simularlo… tengo una idea pero los detalles no están formados del todo. Cuando pueda tener un demo de juego para mostrarlo volveré a hablar de esto.

Gracias a la magia de CodePicnic, puedes correr código de prueba aquí abajo, en tu navegador. Solo anda a la pestaña de Terminal y ejecuta

python game.py

El resultado no es No es nada impresionante ¡falta mucho código! como el que dibuja la escena y el bucle de eventos. De hecho, si lo haces te sentirás estafado/a… excepto por que estamos hablando de ejecutar código Python en tu navegador! Igual se siente como un avance enorme.

NOTA: Si revisas el código — aunque tengas mucha experiencia con Python — hay cosas que quizás te parezcan chino (¿o solo soy yo?). ¿‘marshal’, ‘compile’, ‘exec’? Acabo de aprender que existen. Son funciones que Python usa internamente para importar módulos, no es algo que usas todos los días ;)

Esta es la base de todo. Lo que sigue es escribir código para mostrar (¡al fin!) algo en una ventana. Y de eso tratará el siguiente artículo. ¡Nos vemos!

--

--