14 diciembre 2025

Análisis del libro "Game Design Workshop" de Tracy Fullerton

Portada del libroHay muchas maneras de entrar en el desarrollo de videojuegos. Todos lo hacemos desde nuestro placer por jugar, pero unos tenemos un trasfondo técnico y entramos directamente a programar con un engine, otros se meten después de hacer sus pinitos en el mundo del modding o de la creación de niveles para un juego específico y otros incluso se meten desde el mundo de los juegos físicos en tablero. 

Este libro es para todos ellos porque parte del concepto más abstracto de lo que llamamos juego y, si bien lo acaba concretando en un videojuego, se centra en el proceso creativo de generar ideas, encontrar las más prometedoras, probarlas con un prototipo que no tiene por qué ser como su forma final, iniciar un proceso iterativo de refinado hasta dar con algo genuinamente divertido por su concepto en sí (y no por su implementación) y, sólo entonces, empezar su desarrollo. Y es que el libro no empieza a tratar lo que conocemos por desarrollo hasta su tercer tercio, e incluso entonces no trata la parte técnica del desarrollo, en este libro no se ve ni una línea de código, sino su parte de planificación y gestión de la labor de un grupos de personas.

El libro adopta el punto de vista del diseñador de videojuegos. Si bien es un rol muy específico dentro de un grupo de desarrollo de videojuegos, se trata del que tiene un papel más transversal, como el libro se encarga de explicar, y por tanto cuenta con más visibilidad de todo el proceso, de ahí su importancia.

Los dos primeros tercios del libro se encargan de explicar el rol del diseñador de videojuegos; qué entendemos por juego; cuales son sus elementos fundamentales; qué elementos los enriquecen para darles el carácter que queremos; cuales son las dinámicas de juego más habituales según ese carácter; cómo podemos mantener el interés de los jugadores conforme pasa el tiempo; y cómo podemos mantener el juego equilibrado para evitar la frustración del jugador o, peor, su aburrimiento. Luego pasa a cómo filtrar y refinar las ideas de juegos que tengamos, dándole muchísima importancia (y capítulos) al proceso de prototipado y prueba de esos prototipos con jugadores reales (al margen del diseñador). Prueba del enfoque eminentemente "meta" del libro es la insistencia del libro en que los prototipos probados empiecen siendo físicos, en papel y cartón incluso, desde mucho antes de hacer el primer prototipo digital. Ese enfoque de evitar los prototipos digitales, hasta muy avanzado el proceso, puede resultar chocante para los que disfrutamos escribiendo líneas de código, pero el autor se encarga de razonar dicho enfoque de una manera muy convincente y realmente refrescante.

El último tercio es el que trata el desarrollo propiamente dicho, pero con un enfoque "alejado del teclado". Aquí, el libro trata de ponerte en la piel de un gestor de equipo que tiene que coordinar a multiples especialistas para dar forma a un juego que se pueda vender. Todo el libro tiene un enfoque muy realista, pero aquí es donde más se nota. El autor hace un esfuerzo notable en transmitirte el día a día de un estudio de desarrollo de videojuegos, tanto en sus labores internas cotidianas de creación del juego, como en sus interacciones con actores externos clave para su prueba, financiación y distribución, como son los testers o las casas publicadoras (publishers). Para ello, te explica cómo estructurar los equipos de desarrollo, qué roles son los habituales y cómo se puede relacionar entre sí de una manera eficiente y sana. Luego te explica cómo pueden ayudar las metodologías ágiles a humanizar el desarrollo y mejorar su producto. Me ha gustado mucho cómo te explica el funcionamiento del mercado del videojuego y te mentaliza sobre cómo aproximarte a una publicadora para que esta elija tu juego para financiarlo. Llega incluso a hablar sobre cómo suelen ser los contratos típicos entre publicadoras y equipos de desarrollo, para que entiendas cómo funciona el sistema de pago por hitos durante el desarrollo, y luego el posterior de royalties tras la publicación del juego. Es un interesantísimo baño de realidad, que sirve para desmitificar el mundo del desarrollo del videojuego, pero al mismo tiempo da una medida del enorme esfuerzo que hay detrás de un juego que consigue venderse.

Todo lo anterior ya tiene un valor enorme, pero además el libro está aderezado con los testimonios de muchos diseñadores de juegos. Te encontrarás con que más de uno habrá hecho juegos que hayas disfrutado. En esos testimonios, cada uno de ellos te cuenta cómo se introdujo en el mundillo del diseño y desarrollos de videojuegos, que es lo que les atrae de él y cual es el proceso creativo que siguen a la hora de darle forma a un juego. Leerlos, humaniza el papel del diseñador, pone rostro a las personas que están detrás de los juegos que más has disfrutado y te permite conocer el camino vital que han seguido hasta llegar al punto de poder crear los juegos que les han hecho famosos. Muchos de esos caminos vitales no han sido fáciles. Más de uno llega al punto de recomendarte que no te metas en el mundillo si tienes vocación o pasión clara, ya que no es una profesión fácil ni que te haga rico. Pero todos ellos confirman que si realmente quieres hacer juegos, y estás dispuesto a hacer el esfuerzo y los sacrificios, se trata de una profesión que puede llenarte plenamente.

Por tanto, si no sólo te gustan los juegos, sino como se hace, este es tu libro. Es un refrescante baño de realidad, que desmitifica, humaniza y reivindica el esfuerzo creativo que hay detrás de lo que disfrutas en to PC o consola. Un  libro realmente recomendable.

07 diciembre 2025

Serialización de diccionarios en Unity

En desarrollo, la serialización consiste cambiar el formato de un objeto de memoria para que pueda ser almacenado en disco. 

Uno de los formatos más populares de serialización es JSON, un estándar de texto con el que guardas datos en ficheros siguiendo una estructura de llaves muy similar a la de los diccionarios de Python. 

Cuando serializas un objeto, la instancia de una clase, a JSON lo que haces es seleccionar los datos claves del objeto y los conviertes en campos y valores de un fichero JSON. ¿Cuáles son los datos claves del objeto? es el mínimo subconjunto de datos que te permita reconstruir el objeto en el estado que necesitas. Ese proceso de reconstrucción es lo que se denomina deserialización y es el inverso al otro: lees el fichero y con su contenido creas una instancia de la clase con un estado lo más parecido posible al original.

Unity está constantemente serializando y deserializando. Cuando usamos el inspector para darle valor a un campo público de un MonoBehavior, Unity serializa ese valor a su propio formato para que lo tengas en el inspector la próxima vez que arranques el editor. Por defecto, Unity hace ese proceso con todos los campos públicos de los MonoBehaviour y los ScriptableObject, pero también puedes forzarlo en los campos privados si los defines precedidos del atributo [SerializeField]. Ese tributo tiene también el efecto de permitirnos editar en el inspector campos privados. Sin él, el inspector sólo muestra los públicos. Cuidado, eso no significa que tengas que usar ese atributo en todos los campos privados de tu MonoBehavior. Su uso sólo tiene sentido en aquellos campos que contienen valores de base de tu clase, es decir, aquellos que configurarías en el inspector antes de la ejecución del juego. Preceder un campo calculado de con [SerializeField] no tendría sentido, salvo que quisieras conservar ese valor calculado para una ejecución posterior.

Precisamente, ese era el caso que me movió a escribir este artículo. Estoy escribiendo una clase que me permita analizar un escenario y generar un grafo representando todas sus zonas transitables. El proceso de creación del grafo no viene a cuento, pero mi era que el grafo se generase en tiempo de desarrollo, se guardase y se cargase en tiempo de ejecución. Vamos, que quería que mi grafo se guardase en un ScriptableObject. Uno de los componentes de mi grafo se basa en un diccionario, cuyas claves son las coordenadas enteras de una malla rectangular, y sus valores los nodos con los enlaces a los nodos vecinos. Y el problema viene porque Unity no sabe serializar diccionarios. Puede serializar listas, pero no diccionarios. Ese es el motivo por el que puedes editar listas en el inspector, pero no diccionarios.

Los diccionarios son una de las estructuras de datos más comunes en desarrollo, así que no he sido el primero en encontrarse este problema. Es tan habitual, que otros engines, como Godot, llevan a gala poder serializar diccionarios.

¿Cómo se puede sortear ese problema? Bueno, puedes generar una clase que represente hacia fuera el comportamiento de un diccionario, pero que por dentro se base en dos listas, una de claves y otra de valores. A la hora de serializar, se guardarían esas dos listas que sí que son procesables por Unity. A la hora de deserializar, se leerían esas dos listas y se usarían para crear en memoria un diccionario interno desde el que la clase ofrecería su funcionalidad al resto de los componentes del juego. Esta solución es perfectamente legítima, pero a estas alturas ha sido tan usada que ya ha sido incluida en una herramienta de serialización de múltiples tipos de clases (no sólo diccionarios) como es Odin Serializer. Así que sería como reinventar la rueda. Si con todo y con eso, quieres hacértelo tú mismo, la misma página de Odin te explica cómo, aunque advierte de que el demonio está en los detalles y que puede haber situaciones raras al editar prefabs que pueden obligar sofisticar bastante la implementación inicial. Ese camino ya lo han recorrido los chicos de Odin Serializer, así que voy a explicar cómo utilizarlo.

Logo de Odin Serializer
Logo de Odin Serializer

Odin Serializer es una herramienta open source y gratuita. Te la puedes descargar de su página web, en formato de paquete de unity con todos los ficheros de código fuente para serializar todo lo que permite Odin. Odin Serializer es sólo el banderín de enganche gratuito de un conjunto de herramientas que, esas sí, son de pago. A partir del serializador gratuito abarcan una herramienta para crear inspectores personalizados y otra para encontrar errores en tus proyectos. Ambas son potentísimas. La primera te permite ahorrarte el paso por el UI Toolkit a la hora de implementar inspectores cómodos y eficientes. La segunda detecta los errores más típicos en el desarrollo con Unity y ofrece un conjunto de atributos personalizados para cargar de semántica tus campos y detectar casos en los que sus valores no se ajustes a su semántica (o directamente impedirte introducir esos valores). Aunque en el caso concreto de este artículo me ha bastado con usar el Serializer gratuito, te recomiendo que le eches un ojo a sus otras herramientas y precios. Son de pago único y de una cuantía tirada para un desarrollador indie.

La página de descarga sólo te pide el namespace base de tu juego, para personalizar los namespaces de todos los ficheros de código incluidos. El paquete incluye una carpeta OdinSerializer de la que cuelgan el resto de carpetas de la herramienta. Tienes que colocar esa carpeta dentro de la de Scripts de tu juego.

Una vez importado en tu carpeta de Scripts, OdinSerializer ofrece un conjunto de clases especializadas que heredan de las habituales en Unity:

  • SerializedBehaviour
  • SerializedComponent
  • SerializedMonoBehaviour
  • SerializedNetworkBehaviour
  • SerializedScriptableObject
  • SerializedStateMachineBehaviour
  • SerializedUnityObject

Te basta con sustituir la clase de Unity de la que herede tu componente por la equivalente para que Odin se encargue de serializar aquellos tipos de los que Unity no se ocupe. Todos los diccionarios, por ejemplo, serán serializados por Odin sin que haga falta nada más, de manera que si editas su contenido desde el editor, se conservará entre ejecuciones de este.

Sin embargo, hay que tener en cuenta que aunque serialices el contenido del diccionario con Odin, el inspector de Unity será incapaz de leerlo. Así que no te extrañe si tu diccionario público sigue sin aparecer en el inspector. Para eso haría falta instalarse Odin Inspector, que es uno de los componentes de pago. Sin ese componente, cualquier modificación desde el editor del contenido de un diccionario serializado debería venir de scripts personalizados para ejecutarse desde el editor. Mi caso era precisamente ese, ya que he configurado un botón en el inspector del componente de mi grafo para generar la malla al pulsarlo y guardar los valores creados en el diccionario interno del componente. Si modifico el escenario, no tengo más que volver a pulsar el botón para regenerar la malla del grafo.

Con Odin Serializer deberías ser capaz de resolver la mayor parte de tus necesidades para serializar diccionarios, pero no siempre es tan sencillo. Aunque ya había usado Odin Serializer en proyectos más simples, no he conseguido hacerlo funcionar en el proyecto que motivó este artículo. Generaba mi grafo correctamente al pulsar el botón, pero dicho grafo no estaba allí en el siguiente arranque del editor o al recargar el nivel. Por lo que quiera que sea, el diccionario que contenía el grafo no se salvaba al guardar el nivel. Le he dado muchas vueltas y no sé si se debe a que tengo varios niveles de diccionarios anidados unos dentro de otros, o porque los scripts que contienen esos diccionarios están colocados en prefabs también anidados. También puede ser porque mi componente usa un editor personalizado, lo que añade un nivel más de indirección a la serialización de sus datos. Sea por la razón que sea, al final no me ha quedado más remedio que implementar mi propia serialización. Precisamente esa serialización manual acerca de la que prevenía, si podías resolver tu problema con Odin. En esta ocasión no me ha quedado más remedio que reinventar la rueda, aunque he tenido la suerte de poder contar con el método explicado en la misma página de Odin Serializer. Te voy a contar cómo, por si te sirve para salir del paso en alguna ocasión.

Como decía antes, la clave está en crear una clase que herede de Dictionary para conservar su funcionalidad. Para dotar a esa nueva clase de la capacidad de serializarse dentro de Unity, hay que hacer que implemente el interfaz ISerializationCallbackReceiver. Cuando Unity recibe la orden de serializar una clase, porque el campo de esa clase sea público o esté marcado con el atributo [SerializeField], espera que dicha clase implemente el interfaz ISerializationCallbackReceiver. Ese interfaz consta de dos métodos, OnBeforeSerialize() en el que deberemos implementar cómo queremos guardar la información de nuestro objeto al serializar, y OnAfterDeserialize() en el que implementaremos cómo recuperar la información guardada para reconstruir con ella el estado del objeto.

La clase que he creado se basa en dos listas, una para las claves del diccionario y otra para sus valores. Será en esas listas donde guarde los datos a conservar, gracias a que Unity sí que serializa listas de manera nativa.

Los campos de mi diccionario personalizable
Los campos de mi diccionario personalizable

Ahora la implementación del interfaz. Primero para el proceso de serialización.

Proceso de serialización de mi diccionario personalizable
Proceso de serialización de mi diccionario personalizable

Cuando Unity llame a OnBeforeSerialize() será porque es hora de guardar la información del estado del objeto. Para ello, lo que hago es vaciar el contenido anterior de las listas (líneas 42 y 43) para volver a rellenarlas con el listado actualizado de las claves y los valores del diccionario. Cuando se cierre la instancia de la clase se perderá el contenido del diccionario, pero las listas habrán quedado serializadas junto con el resto de información de la escena.

Ahora el proceso inverso. Se abre de nuevo la escena y Unity llama a los métodos OnAfterDeserialize() de todos sus objetos para que estos restauren su estado anterior a partir de la información deserializada.

Proceso de deserialización de mi diccionario personalizable
Proceso de deserialización de mi diccionario personalizable

Como se puede ver en el listado, dado que las listas sí que serializaron, estarán con su contenido intacto al llamar a OnAfterDeserialize() lo que permitirá utilizarlo para restaurar las entradas del diccionario. En la línea 34 a 36 se puede ver que recorro ambas listas para regenerar las entradas del diccionario interno de la clase.

Ahora viene algo importante. Unity no puede serializar tipos genéricos y la clase recién creada (UnitySerializedDictionary) lo es. La solución es concretar la clase genérica para cada uno de los usos a los que la apliquemos. Una vez concretada, la clase resultante sí podrá ser serializada por Unity. Esa es la razón por la que tengo un fichero aparte, estático, con las diferentes versiones concretadas del diccionario genérico.

Clases concretas a partir de la genérica
Clases concretas a partir de la genérica

Para evitar cualquier tentación de usar directamente la clase genérica, sin concretarla, lo mejor es marcarla como abstracta.

Serán esas versiones concretas las que podamos usar en nuestro código, con la tranquilidad de que Unity conservará su contenido entre ejecuciones.

Uso del diccionario concretado
Uso del diccionario concretado

MonoBehaviour que utiliza nuestro diccionario concretado y serializable
MonoBehaviour que utiliza nuestro diccionario concretado y serializable

Espero que te haya resultado útil y que te sirva para no cortarte a la hora de usar diccionarios en tus componentes.