29 agosto 2025

Herramientas para desarrollar el argumento de un videojuego

La importancia del argumento depende del tipo de juego y su público objetivo. Hay juegos como Tetris o Among Us, en los que la narrativa es mínima, y la diversión radica en las mecánicas. Este tipo de juegos no necesitan un argumento complejo. 

Sin embargo, el argumento es un pilar fundamental para la experiencia en juegos de tipo narrativo, RPG o de aventuras. En estos juegos, el argumento es importante por múltiples razones. Un buen argumento crea un mundo creíble y personajes con los que los jugadores pueden empatizar, aumentando la inmersión. Historias bien desarrolladas generan emociones, motivando a los jugadores a continuar para descubrir qué pasa. El argumento proporciona un marco narrativo que da sentido a las acciones del jugador. Sin una historia, las mecánicas pueden sentirse vacías o repetitivas. Por ejemplo, en juegos como The Last of Us, el argumento impulsa las decisiones y hace que las mecánicas tengan peso emocional. Una narrativa sólida ofrece objetivos claros y razones para avanzar, ya sea salvar un mundo, vengar a un personaje o desentrañar un misterio. Esto mantiene el interés, especialmente en juegos largos. En un mercado saturado, un argumento original o bien ejecutado puede destacar un videojuego frente a otros con mecánicas similares. Títulos como Hollow Knight o Disco Elysium son ejemplos donde la narrativa eleva la experiencia. Un argumento con ramificaciones, elecciones morales o finales múltiples (como en Mass Effect o The Witcher 3) fomenta la rejugabilidad, ya que los jugadores quieren explorar diferentes desenlaces.

Por tanto, el proceso de desarrollo del argumento del juego es tan importante como el de la programación o el modelado de assets. En este artículo vamos a ver algunas herramientas especializadas que nos pueden ser útiles para crear el argumento de nuestro juego, así como para diseñar sus conversaciones.

Obsidian

Obsidian es una aplicación de toma de notas y gestión del conocimiento diseñada para crear y organizar contenido a través de notas interconectadas, utilizando un sistema basado en Markdown y un enfoque de "grafo de conocimiento" que visualiza las relaciones entre ideas. Aunque no está específicamente diseñada para el desarrollo de videojuegos, su flexibilidad, personalización y capacidad para manejar grandes cantidades de información la convierten en una herramienta valiosa para escritores, diseñadores narrativos y desarrolladores que trabajan en la planificación de historias, lore, guiones y documentación de proyectos de videojuegos.

Logo de Obsidian
Logo de Obsidian

Obsidian destaca como una solución ligera y versátil para la preproducción y organización, especialmente para proyectos indie o equipos pequeños. 

Con Obsidian cada nota puede enlazarse a otras mediante vínculos bidireccionales (similar a un wiki personal). Su vista de grafo muestra conexiones visuales entre notas, facilitando la exploración de ideas complejas. Por tanto, se trata de una herramienta ideal para mapear relaciones entre personajes, eventos y quests.

A la hora de editar, tienes dos modos principales: un editor de texto plano en el que empleas formato Markdown, y un modo Canvas que te ofrece un lienzo visual para conectar notas con flechas, similar a un tablero de ideas. Al crear una nueva nota, los enlaces a otras notas se crean usando enlaces de Markdown. Cuando creas un Canvas puedes crear dos tipos de nodos: cards, que vienen a ser nodos pequeños con apenas un título, y notas que ya tienen un texto completo de Markdown (puedes incluso importar una nota que haya creado aparte). Al final, el Canvas es un espacio visual para organizar notas como nodos conectados, ideal para mapear historias ramificadas o flujos narrativos.

Editando notas en Obsidian
Editando notas en Obsidian



El modo Canvas de Obsidian
El modo Canvas de Obsidian

Un posible flujo de trabajo podría ser hacer una lluvia de ideas y recoger las ideas sueltas en notas, para luego estructurarlas en un Canvas. 

Luego, al margen del Canvas, si necesitases una vista de alto nivel de todas tus notas, puedes abrir la vista de grafo que que muestra el árbol de una nota y sus enlaces con otras notas.

Otra característica muy potente es que permite etiquetar los elementos lo que facilita búsquedas avanzadas usando operadores booleanos.

Por defecto, Obsidian lo guarda todo en local, en archivos locales de texto plano que aseguran la portabilidad y el bajo consumo de recursos. Esos datos se pueden exportar a Markdown, HTML, PDF o Word. Sincroniza con Dropbox, iCloud.

Obsidian es gratuito, pero si quisieses sincronización entre todos tus dispositivos tendrías que pagar una pequeña subscripción. Si quisieses publicar notas en el sitio web de Obsidian Publish habría que pagar otra subscripción.

Todo lo anterior ya lo hacen una opción muy interesante para lo que buscamos en este artículo, pero es que, además, Obsidian cuenta con una comunidad hiperactiva en foros, discord y Reddit, donde ofrecen plantillas, ejemplos de flujos de trabajo específicos (por ejemplo para narrativa de videojuegos). Dicha actividad ha creado ya más de 2.600 plugins comunitarios, como por ejemplo:

  • Dataview: Crea tablas dinámicas para gestionar personajes, quests o ítems (e.g., "muestra todos los NPCs con estado 'aliado'").
  • Obsidian Tasks: Seguimiento de tareas para planificación de desarrollo.
  • Kanban: Tableros para gestionar sprints o milestones.
  • Excalidraw: Dibujos para storyboards o mapas.

Al final, no hay dos instalaciones de Obsidian iguales. Casa usuario se lo personaliza con múltiples plugins para personalizarse la herramienta a sus gustos particulares, lo que la hace una herramienta tremendamente potente.

Ink

Inkle Studios es un estudio independiente de desarrollo de videojuegos. Se especializan en juegos narrativos interactivos, es decir, títulos centrados en historias ramificadas, decisiones del jugador y narrativas no lineales, donde el texto y las elecciones del usuario son elementos clave para avanzar en la trama. 

El fruto de su labor no se limita a los videojuegos, sino que también han creado herramientas para su desarrollo. Entre ellas se encuentra Ink, un lenguaje de scripting narrativo open-source (bajo licencia MIT), diseñado específicamente para escribir historias altamente ramificadas en juegos. Ink no es un lenguaje de programación tradicional, sino un markup simple y elegante que permite a escritores y desarrolladores crear diálogos, elecciones y tramas complejas de manera eficiente. Ink se integra fácilmente con motores como Unity (a través de un plugin gratuito), Unreal Engine y Godot, y ha sido adoptado por estudios grandes e independientes.

Logo de Ink
Logo de Ink

Para que nos entendamos, Ink sería lo que utilizarían los autores de la popular serie de libros "Elige tu propia aventura" si quisieran escribir sus historias hoy. Con él puedes ofrecer elecciones al lector y dejar que la historia se vaya ramificando conforme se sucedan dichas elecciones.

Ink ofrece una sintaxis fácil de aprender para no programadores, pero potente para estructuras avanzadas (como bucles, listas y lógica condicional). Además, es muy ligero, por lo que el flujo de trabajo con él es muy rápido permitiendo ver los cambios prácticamente conforme se escriben.

Sintaxis de Ink
Sintaxis de Ink

Todas esas ventajas han propiciado que haya sido ampliamente usado en multitud de juegos (como Haven, NeoCab, Sable, Sea of Thieves y The Banner Saga 3, por ejemplo) y que haya abundantes recursos en Internet para aprender a usarlo.

Para facilitar más su uso, Inkle ofrece también un editor gratuito llamado Inky, disponible en la misma página que Ink. Es un editor minimalista, que ofrece una ventana de preview en la que puedes probar las opciones y ramificaciones que vayas escribiendo. También te alerta de los errores de sintaxis de Ink que cometas al escribir. Tiene versiones tanto para Windows, como para Linux y Mac.Y finalmente, lo más importante, te permite exportar a JSON para que puedas cargar toda la información de la historia desde el juego que estés desarrollando. 



Twine

Twine es una herramienta gratuita y de código abierto diseñada para crear ficción interactiva y juegos narrativos basados en texto, especialmente historias no lineales donde las elecciones del jugador determinan el rumbo de la narrativa. Creada en 2009 por Chris Klimas, es ampliamente utilizada tanto por desarrolladores principiantes como por profesionales en el desarrollo de videojuegos, escritura creativa, educación y proyectos artísticos. Lo que ofrece es similar a Ink, en el sentido de que permite crear historias interactivas en formato hipertextual, donde el jugador navega a través de nodos de texto (llamados "pasajes") conectados por enlaces. Cada pasaje contiene texto, opciones y, opcionalmente, código para manejar lógica, variables o multimedia. 

El problema que tiene es que no hay un camino claro y fácil para importar la historia desde un game engine (como sí había con Ink), por lo que el uso de Twine está más indicado para limitarse a darle forma al guion del juego o para crear un prototipo hiper sencillo para mostrar las posibles ramificaciones de dicho guion.

Logo de Twine
Logo de Twine

Twine es conocida por su simplicidad, accesibilidad y flexibilidad. Su editor visual permite arrastrar y conectar nodos, creando un mapa de la narrativa. También tiene un modo de código para personalizar la lógica del juego.

Los historias interactivas creadas en Twine se publican como archivos HTML, jugables en navegadores web, lo que los hace altamente portátiles. También se pueden exportar a otras plataformas con herramientas adicionales. Los archivos de salida se pueden volcar en varios formatos soportados, para permitir el posterior tratamiento y personalización de la historia. 

El uso de un editor visual permite que la edición de texto en sí sea mucho más sencilla que en le caso de Ink. Al final, la redacción se limita a escribir texto y añadir enlaces simples (por ejemplo, [[Ir a la cueva]]) para conectar pasajes. Aunque también se puede recurrir a JavaScript y macros para hacer cosas más avanzadas. 

El editor de Twine
El editor de Twine

Con todo ello se pueden crear historias altamente ramificadas, desde aventuras de "elige tu propia aventura" hasta narrativas complejas con variables (por ejemplo, puntos de salud, inventarios) y lógica condicional. 

La herramienta y su código están disponibles bajo licencia abierta GPL-3. Se puede usar en línea (twinery.org) o descargar para Windows, macOS y Linux. Cuenta, además, con una comunidad activa, tutoriales (como los de twinery.org), foros, y plantillas en plataformas como itch.io. También hay extensiones y complementos creados por la comunidad.

Yarn Spinner

Yarn Spinner es una herramienta desarrollada originalmente por el estudio australiano Secret Lab (creadores de Night in the Woods), aunque posteriormente parte del equipo se escindió del estudio de videojuegos, para centrarse en el desarrollo de la herramienta, pasando a llamarse como esta.

A medio camino entre el lenguaje de scripting de Ink, y la herramienta visual de Twine, utiliza un lenguaje de scripting simple llamado Yarn, inspirado en formatos como guiones de cine o teatro, que permite escribir diálogos en texto plano con elementos de control de flujo, variables y comandos. Luego, un plugin integrado con el engine se encarga de actuar como un intérprete que ejecuta estos scripts en tiempo de juego, enviando líneas de diálogo, opciones y comandos al engine. 

Portada de la página web de Yarn Spinner
Portada de la página web de Yarn Spinner

Algunos juegos realizados con Yarn Spinner son Night in the Woods, A Short Hike, DREDGE, Lost in Random, Frog Detective, Button City o Escape Academy.

Para la edición de los scripts, tienen una extensión para Visual Studio Code que ofrece un editor de texto con resaltado, para crear los scripts en Yarn, así como una ventana de grafo con la que ver las ramificaciones de la historia. 

Extensión de Yarn Spinner para Visual Studio Code
Extensión de Yarn Spinner para Visual Studio Code

También cuentan con una página web, en la que editar y probar scripts sencillos.

Las principales ventajas de Yarn Spinner son:

  • Flexibilidad Narrativa: Soporta historias altamente ramificadas, con memoria de elecciones previas (por defecto), storylets (contenido dinámico basado en saliencia) y grupos de nodos/líneas para organización.
  • Personalización: Integra multimedia (imágenes, audio, video) vía comandos. Incluye UIs prehechas (e.g., burbujas de diálogo, ruedas de opciones) y samples para customización.
  • Localización: Soporte robusto para traducciones, extrayendo strings a archivos separados (e.g., JSON o CSV) para herramientas como Unity Localization o I2 Localization, facilitando workflows multilingües.
  • Depuración y Herramientas: Errores con números de línea, vista previa en navegador, y soporte para voice-over (sincronización con líneas). Comunidad activa en Discord y GitHub.

Si usas Unity, estás de enhorabuena, porque es el único engine con el que tienen un plugin de integración oficial. El código del plugin para Unity está disponible en GitHub, con licencia MIT, por lo que puedes descargártelo e instalarlo en Unity de manera gratuita. Otra opción es recurrir al Unity Asset Store para comprar plugin para Unity, su importe no es muy elevado y sirve para financiar el desarrollo de la herramienta. También ofrecen otros complementos para Unity, como uno que permite mostrar el texto de las conversaciones en bocadillos tipo comic, y otro que permite tomar las decisiones usando una rueda de selección.

También hay plugin para Unreal, y otro para Godot, pero aún se encuentran en fase Beta y no los recomiendan para producción.

Articy Draft

Articy:draft (ahora conocido principalmente como articy:draft X en su versión más reciente) es una herramienta profesional de diseño narrativo y organización de contenido interactivo, desarrollada por Articy Software GmbH, una empresa alemana fundada en 2009.

Se trata de un software visual que actúa como una base de datos centralizada para crear, gestionar y planificar historias ramificadas, diálogos, misiones, personajes y otros elementos narrativos en proyectos interactivos, con un enfoque especial en el desarrollo de videojuegos. A diferencia de herramientas de scripting puro como Ink o Yarn Spinner, Articy:draft es más un entorno visual integral (similar a un "CMS narrativo" o sistema de gestión de contenido para juegos), que combina edición de flujos de historia, bases de datos de objetos y colaboración en equipo. 

Logo de Articy Draft
Logo de Articy Draft

Es ampliamente utilizado en la industria por estudios indie y AAA para manejar narrativas complejas, y ha sido clave en títulos como Disco Elysium, Suzerain, HipWitch, Saint Kotar, Garden Scapes y Spell Force III

Cuenta con un editor visual no lineal para el diseño de contenido interactivo, que unifica editores especializados (para diálogos, misiones, objetos, ubicaciones) en una sola interfaz. Permite representar gráficamente entidades de juego (como NPCs, ítems o quests) con automatizaciones, variables y lógica condicional, actuando como un "frontend visual" para datos de juego. No genera código ejecutable directamente, sino que exporta datos estructurados para integrarlos en motores de juego.

El editor visual de Articy Draft
El editor visual de Articy Draft

Utiliza vistas anidadas (Flow View) para mapear historias de alto nivel (capítulos) a detalles finos (líneas de diálogo). Incluye un editor de plantillas flexible para objetos de juego, un simulador para probar lógica (con soporte para voz y localización), y herramientas como un editor de ubicaciones vectorial para planificar mapas 2D.

Anidación de vistas en ArticyDraft
Anidación de vistas en ArticyDraft

Ofrece un modo de simulación que reproduce la historia con voz, idiomas y lógica, ideal para iterar antes de exportar. El modo de simulación permite comprobar la evolución de las variables que hayamos ligado a los diálogos y a las distintas elecciones, así como comprobar el efecto que tienen estas variables en la ruta a través de las historias.

Modo simulación de Articy Draft
Modo simulación de Articy Draft

Otras herramientas adicionales con las que cuenta son un panel de storyboarding; un editor de ubicaciones (útil para planificar niveles o quests); soporte para voz en off (generación TTS para prototipos y sincronización); localización integrada (exporta strings para traducción y reimporta); y extensiones de IA para generar diálogos, imágenes o traducciones automáticas (e.g., con DeepL).

Storyboarding en ArticyDraft

Editor de ubicaciones
Editor de ubicaciones

También sirve de base de datos en la que guardar la configuración de todos los NPC y objetos del juego. La idea es que los diseñadores del juego puedan manipular los datos desde la interfaz de Articy Draft, para luego exportar todos los datos de una vez y cargarlos desde el game engine.

Base de datos de personajes y objetos del juego
Base de datos de personajes y objetos del juego

En cuanto a integraciones con game engines, Articy Draft cuenta con assets oficiales para integrarse tanto con Unity como con Unreal. Para otros engines, Articy Draft también permite exportar a XML y a JSON. Si no fuera suficiente con lo anterior, también ofrecen un paquete de NuGet para hacerte tu propio paquete de integración.

Articy Draft sigue un modelo de licenciamiento por subscripción, pero tiene una modalidad gratuita, con todas las funcionalidades, y que incluso puede usarse para proyectos comerciales, con la única limitación de un máximo de 700 objetos a gestionar por proyecto. Aun así, esa cantidad de objeto es muy generosa, y probablemente más que suficiente para la mayor parte de los proyectos medianos y pequeños. En todo caso, es más que suficiente para probar la herramienta y decidir si merece la pena. Si necesitásemos más de 700 objetos en nuestro juego, podríamos acudir a cualquiera de las licencias de pago (las individuales son bastante asumibles).

Dialogic

La herramienta anterior sólo tenía integración directa con Unity y Unreal, así que, para equilibrar la cosa, ahora vamos a hablar de una que sólo existe para Godot: Dialogic.

Dialogic es un plugin para Godot orientado a hacer sistemas de diálogo interactivos, novelas visuales (visual novels), RPGs y gestión de personajes.  Desarrollado inicialmente por Emilio Coppola en 2021 como una solución para agregar conversaciones ramificadas de manera sencilla, ha evolucionado hasta convertirse en una herramienta madura y popular en la comunidad de Godot, especialmente para desarrolladores indie que buscan integrar narrativas complejas sin escribir código extenso desde cero. Está disponible bajo licencia MIT, lo que permite modificaciones y uso comercial libre. 

Logo de Dialogic
Logo de Dialogic

Proporciona un editor integrado en Godot para diseñar "timelines" (líneas de tiempo de diálogo), que son secuencias de eventos como texto, opciones del jugador, animaciones, condiciones lógicas y señales. Estos timelines se ejecutan en tiempo de juego para mostrar conversaciones dinámicas y con ramificaciones, eventos y elecciones. Se pueden añadir condiciones (if/else con variables), señales (para triggers en el juego), animaciones y fondos a dichas conversaciones.

Editor del Timeline de Dialogic
Editor del Timeline de Dialogic

Incluye un sistema de gestión de personajes con retratos, expresiones y colores personalizados, y soporta extensiones para eventos customizados.

Editor de personajes de Dialogic
Editor de personajes de Dialogic

Visualmente es completamente personalizable, con temas editables (colores, fuentes, estilos de burbujas de texto), soporte para audio (voz en off, SFX), animaciones (e.g., shake de pantalla) y eventos customizados con poco código GDScript. Incluye estilos prehechos como "text bubble" para novelas visuales.

También contempla la localización, permitiendo traducciones integradas con glosarios traducibles.

Cuenta con una comunidad muy activa que lo ha dotado de extensiones para que pueda interactuar con otros plugins como SmartShape2D para terrenos o Godot Steamworks para logros.

Si vas a desarrollar con Godot, Dialogic es la opción más sencilla y económica dado que está repleta de funcionalidades, ya está más que probada en multitud de juegos de Godot y es gratuita.

Scrivener

Todas las herramientas anteriores, salvo quizás Twine, contemplan cierto nivel de integración o compatilidad con game engines. Scrivener es una algo muy diferente. Se trata de una herramienta 100% pensada en escritores y guionistas.

Desarrollada por la compañía Literature & Latte, aunque no esté específicamente creado para el desarrollo de videojuegos, es una herramienta versátil que puede ser extremadamente útil para escribir y estructurar guiones de videojuegos, especialmente para proyectos narrativos como RPGs, aventuras gráficas, novelas visuales o cualquier juego con historias ramificadas

Logo de Scrivener
Logo de Scrivener

Toma la forma de un procesador de texto avanzado y herramienta de gestión de proyectos que combina escritura con organización de notas, investigación y estructuración no lineal. Permite dividir un proyecto en secciones manejables (capítulos, escenas, etc.), gestionar materiales de referencia (imágenes, PDFs, notas) y exportar en múltiples formatos. Además cada uno de los contenidos que se creen con Scrivener pueden ser enriquecidos con metadatos, notas, etiqueta y referencias.

Para planificar arcos narrativos ofrece un modo de visualización similar a un corcho en el que cada una de las secciones se visualiza como una tarjeta.

El corcho de Scrivener
El corcho de Scrivener

Todo el interfaz es muy flexible, jugando con él, y haciendo un uso adecuado de las etiquetas, se pueden configurar líneas de tiempo con la que desarrollar líneas argumentales paralelas.

 

Líneas de tiempo con Scrivener
Líneas de tiempo con Scrivener

No esperes herramientas para darle formato. En ese sentido, Scrivener es muy sencillo, casi primitivo. En realidad, está pensado para concentrarse en generar el contenido y luego exportarlo para darle formato en otras herramientas. Por ejemplo, mucha gente configura Scrivener para exportar el contenido generado a LaTeX y luego, desde un editor de LaTex, exportan a PDF con todo el formato de la plantilla de LaTeX empleada.

Por tanto, Scrivener es una excelente herramienta para escribir y organizar el guion de un videojuego en la fase de preproducción, especialmente para estructurar narrativas complejas y mantener el lore organizado. Sin embargo, no está diseñado para simular interactividad ni integrarse directamente con motores de juego, por lo que necesitarás combinarlo con herramientas como Twine (para prototipos rápidos), Dialogic (para Godot), Yarn Spinner (para Unity) o Articy:draft (para proyectos AAA). Si tu enfoque es escribir un guion detallado antes de la implementación técnica, Scrivener es una gran elección; si necesitas interactividad inmediata, opta por una herramienta específica para videojuegos.

21 agosto 2025

Los interfaces de C# y el inspector de Unity

Interface Contract
En C#, una interfaz es un contrato que define un conjunto de métodos, propiedades, eventos o indexadores que una clase o estructura debe implementar, sin que dicho contrato proporcione la implementación concreta. Es como un plano que especifica "qué" debe hacerse, pero no el "cómo". Cuando una clase se adhiere a una interfaz, se compromete públicamente a ofrecer, al menos, los métodos de dicha interfaz. Por tanto, dicha clase debe implementar el código de cada uno de los métodos del interfaz.

Las interfaces en C# ofrecen varias ventajas que las hacen fundamentales en el diseño de software. Las principales son:

  • Abstracción: Definen un contrato sin detalles de implementación, permitiendo centrarse en "qué" hace un objeto, no en "cómo" lo hace.
  • Flexibilidad y extensibilidad: Facilitan la adición de nuevas clases que implementen la misma interfaz sin modificar el código existente.
  • Herencia múltiple: Permiten que una clase implemente varias interfaces, superando la restricción de herencia única de clases en C#. Una clase sólo puede heredar de una clase madre, pero puede implementar varios interfaces simultáneamente.
  • Desacoplamiento: Reducen la dependencia entre componentes, promoviendo un diseño modular y facilitando la inyección de dependencias.
  • Mantenimiento y testing: Simplifican la sustitución de implementaciones (por ejemplo, en pruebas unitarias con mocks) al trabajar con contratos en lugar de clases concretas.
  • Consistencia: Garantizan que las clases que implementan la interfaz cumplan con un comportamiento estándar, mejorando la interoperabilidad.
  • Soporte a patrones de diseño: Son esenciales en patrones como Strategy, Factory o Repository, que dependen de abstracciones.

Todo esto se entiende mejor con ejemplos. En mi caso, he desarrollado una clase abstracta, denominada SteeringBehavior, que implementa la funcionalidad básica del movimiento de un agente inteligente. De esa clase heredan otras muchas hijas que implementan movimientos especializados. 

La clase abstracta SteeringBehavior
La clase abstracta SteeringBehavior

Algunas (no todas) de esas clases hijas necesitan que se les fije un blanco para poder calcular su movimiento, ya sea para alcanzar ese blanco, para perseguirlo, para mirar hacia él, etc. Ese blanco se define con un campo Target. 

Podría haber incluido ese campo dentro de la definición de las clases hijas, pero eso me habría restado flexibilidad. El problema habría venido al encadenar clases SteeringBehavior. Una clases SteeringBehavior ofrecen su funcionalidad componiendo las salidas de otras clases SteeringBehavior más sencillas. La flexibilidad de ese enfoque viene de poder construir en el inspector el árbol de dependencias de los diferentes SteeringBehavior.

Un agente, construido con la composición de varios SteeringBehavior
Un agente, construido con la composición de varios SteeringBehavior

Referencias en el inspector a los SteeringBehavior hijos
Referencias en el inspector a los SteeringBehavior hijos

Hay algunos SteeringBehavior para los que sabemos exactamente el tipo de los SteeringBehavior hijos que va a necesitar. En la captura anterior se puede ver que ActiveWallAvoiderSteeringBehavior necesita concretamente un hijo del tipo PassiveWallSteeringBehavior. Pero también hay casos en los que al SteeringBehavior padre le da igual el tipo concreto del hijo, siempre y cuando herede de SteeringBehavior y ofrezca un campo Target (al que pasarle su respectivo campo Target). En el caso de la captura, yo he pasado un SeekSteeringBehavior, pero podría haber pasado un ArriveSteeringBehavior o un PursuitSteeringBehavior, para obtener tipos de movimiento lgeramente diferentes. Si hubiera implementado el campo Target por herencia, habría perdido la flexibilidad de pasar cualquier tipo de SteeringBehavior con un campo Target, al verme obligado a especificar el tipo concreto, hijo de SteeringBehavior.

En vez de eso, lo que he hecho ha sido crear un interfaz.

El interfaz ITargeter
El interfaz ITargeter

Como se puede ver en la captura, el interfaz no puede ser más sencillo. Se declara con la palabra clave interface y, en este caso, se limita a señalar que las clases que se adhieran a este interfaz se comprometen a ofrecer una propiedad Target.

A una clase que se adhiriese a dicho interfaz le bastaría con implementar esa propiedad.

Una clase que implementa el interfaz ITargeter

Como se puede ver en la captura, la clase SeekSteeringBehavior hereda de SteeringBehavior y al mismo tiempo implementa el interfaz ITargeter.

Es posible que estés notando cierto solape entre las clases abstractas y los interfaces. Aunque ambas son mecanismos de abstracción, tienen diferencias importantes en su propósito y uso. Un interfaz define un contrato puro con firmas de métodos, propiedades, eventos o indexadores, sin ninguna implementación. Es ideal para especificar comportamientos que diferentes clases no relacionadas pueden compartir. Por su parte, una clase abstracta es una clase que no se puede instanciar directamente y puede contener tanto miembros abstractos (sin implementación) como miembros concretos (con implementación). Sirve como base para clases relacionadas que comparten lógica común. En general, si quisieras dotar de un comportamiento común a clases relacionadas por una madre, usarías clases abstractas (posiblemente haciendo abstracta a la clase madre); mientras que si el comportamiento común se diese en clases heterogéneas, lo habitual sería usar interfaces.

Mientras que una clase abstracta puede incluir métodos concretos, campos, constructores y lógica compartida que las clases derivadas pueden usar directamente; un interfaz no puede incluir implementación de métodos ni campos (solo a partir de C# 8.0 permite métodos por defecto, pero con restricciones, en general todo debe ser implementado por la clase que la adopta). Por otro lado, la clase abstracta permite modificadores de acceso (public, private, protected, etc.) para sus miembros; mientras que en en un interfaz todos los miembros son implícitamente públicos y no se pueden especificar modificadores de acceso. Todo ello sin olvidar la diferencia en cuanto la herencia: las clases abstractas sólo permiten herencia simple, mientras que los interfaces permiten herencia múltiple.

Unity añade una diferencia adicional, y que resulta determinante para este artículo, dado que permite mostrar en el inspector clases abstractas, pero no interfaces. En una de las capturas anteriores se veía el inspector de la clase ActiveWallAvoiderSteeringBehavior. Te pido que te fijes en su campo Steering Behavior. El código que declara ese campo y lo muestra en el inspector es el siguiente:

Declaración del campo steeringBehavior
Declaración del campo steeringBehavior

Fíjate en que steeringBehavior es del tipo SteeringBehavior (una clase abstracta) y en que el inspector no tiene problema en mostrarlo. Por tanto, podemos usar el inspector para meter en ese campo cualquier script que herede de SteeringBehavior.

Si quisiéramos que ese campo aceptase cualquier script que implementase ITargeter, deberíamos poder hacer [SerializeField] private ITargeter steeringBehavior. El problema es que aunque C# nos permite hacer algo así, Unity no permite exponer campos de tipo interfaz en el inspector.

Entonces, ¿cómo podemos hacer para asegurar que al campo steeringBehavior se le pase no sólo una clase que herede de SteeringBehavior, sino que además implemente el interfaz ITargeter? La respuesta es que Unity, no ofrece ninguna salvaguarda de serie, pero al menos podemos desarrollar la nuestra, tal y como te voy a explicar. 

Será más sencillo si te enseño primero lo que queremos conseguir y luego te explico cómo implementarlo. La solución que he encontrado ha sido diseñar mi propio PropertyAttribute para decorar con él los campos que quiero confirmar que cumplen uno o varios interfaces.

Atributo personalizado para confirmar el cumplimiento de interfaces
Atributo personalizado para confirmar el cumplimiento de interfaces

En la captura anterior se puede ver que he llamado a mi atributo InterfaceCompliant. En el ejemplo, el atributo verifica que el SteeringBehavior que se le pase al campo steeringBehavior cumple con el interfaz ITargeter.

Si el script que se le pasase al campo no cumpliese con el interfaz, el atributo se encargaría de mostrar un mensaje de alerta en el inspector.

Mensaje de alerta al pasar un script que no cumpliese con el interfaz
Mensaje de alerta al pasar un script que no cumpliese con el interfaz


Una vez que ya tenemos claro lo que queremos conseguir, vamos a ver cómo implementarlo. Como en el caso del resto de atributos, tenemos que crear una clase que herede de PropertyAttribute. Esta última es abstracta y nos obligará a implementar el método InterfaceCompliantAttribute, cuyos parámetros son los que se le pasarán al atributo cuando decore un campo.

Implementación de nuestro atributo personalizado
Implementación de nuestro atributo personalizado

Fíjate que el nombre de esta clase debe ser el de nuestro atributo, seguido del sufijo "Attribute". La clase debe ubicarse en dentro de la carpeta Scripts.

Esta clase sirve de modelo de datos del atributo. Guardaremos en ella, toda la información que necesitemos para hacer nuestra comprobación y mostrar el mensaje de advertencia si fuese necesario. En este caso, el atributo admite un número variable de parámetros (por eso la palabra clave params). Dichos parámetros son los tipos de nuestros interfaces y se pasarán en un arry que guardaremos en el campo InterfaceTypes.

Para representar al atributo personalizado en el inspectos, así como al campo que decora, utilizaremos un PropertyDrawer. Estas son clases que hereda, ¡oh, sorpresa!, de PropertyDrawer y que deben situarse en una carpeta Editor, al margen de la de Scripts. Podemos llamar a nuestra clase como queramos, lo importante es que la decoremos con un atributo CustomPropertyDrawer para señalar qué atributo es el que "dibuja" este PropertyDrawer en el inspector.

PropertyDrawer para dibujar nuestro atributo y el campo que decora
PropertyDrawer para dibujar nuestro atributo y el campo que decora

En nuestro caso, he empleado el atributo CustomPropertyDrawer para señalar que debe usarse este PropertyDrawer siempre que un campo aparezca decorado por un atributo InterfaceCompliantAttribute.

PropertyDrawer es otra clase abstracta que obliga a implementar un método denominado CreatePropertyGUI. Este método es al que llama el inspector cuando quiere dibujar en el inspector un campo decorado por este atributo. 

Implementación del método CreatePropertyGUI de InterfaceCompliantAttributeDrawer
Implementación del método CreatePropertyGUI de InterfaceCompliantAttributeDrawer

Una clase que herede de PropertyDrawer cuenta con una referencia al atributo que dibuja a través del campo attribute. Por eso, en la línea 22 se hace casting de él al tipo InterfaceCompliantAttributo que sabemos que es en realidad, para poder recuperar la lista de interfaces a comprobar en la línea 23.

Fíjate en que mientras el atributo se puede rescatar usando el campo attribute de PropertyDrawer, el campo decorado se pasa como un parámetro del método, property, encapsulado en un tipo Serialized Property (línea 19).

 Acto seguido, en la línea 26 generamos el contenedor visual en el que meteremos los widgets para representar nuestro campo en el inspector, así como, en su caso, el mensaje de advertencia. Por ahora, ese contenedor visual está vacío, pero en las líneas siguientes le iremos metiendo contenido. Conforme le vayamos añadiendo elementos al contenedor, estos se irán representando de arriba a abajo en el inspector.

Si miramos la captura en la que mostraba el mensaje de advertencia en el inspector, veremos que dicho mensaje iba antes del campo, así que por eso, el primer elemento que añadiremos al contenedor será dicho mensaje de advertencia, si fuese necesario (línea 28). Esta parte tiene bastante miga, así que la he sacado a su propio método (AddErrorBoxIfNeeded), para facilitar la lectura de CreatePropertyGUI y para permitirme reaprovechar ese método para volver a llamarlo en la línea 45. En cuanto acabe de explicar la implementación de CreatePropertyGUI veremos la de AddErrorIfNeeded.

Una vez que hemos añadido el mensaje de advertencia (si fuese necesario) al contenedor, hay que mostrar el campo decorado. Queremos mostrarlo con su aspecto por defecto en el inspector, así que nos limitamos a crear un PropertyField (línea 31). PropertyField detectará el tipo del campo encapsulado en property y los mostrará con el widget por defecto de Unity para ese tipo.

Atento ahora. Queremos que cada vez que metamos un nuevo script en el campo decorado, se vuelva a evaluar si el nuevo script cumple con los interfaces. Para eso, en la línea 43 le encargamos al contenedor que vigile el valor de property y que cada vez que este cambie vuelva a evaluar si mostrar o no el mensaje de advertencia (línea 45), redibujando el campo (línea 46). 

Hecho todo esto, añadimos el campo decorado al contenedor (línea 50) y retornamos este contenedor para que el inspector lo dibuje.

Como lo prometido es deuda, ahora vamos a analizar la implementación de AddErrorBoxIfNeeded.

Chequeo del campo decorado para ver si cumple con los interfaces

Para acceder al campo encapsulado dentro de un SerializedProperty hay que utilizar su campo objectReferenceValue (línea 70). 

Este campo es de tipo Object, así que luego habrá que hacer casting al tipo concreto que queremos comprobar. Eso lo hacemos en el método GetNotComplyingInterfaces de la línea 74. Ahora veremos su implementación, pero a alto nivel lo que hace ese método es coger una lista de interfaces (línea 75), y un objeto (línea 76), para luego hacer casting de ese objeto contra cada uno de los interfaces. Si alguno de esos castings resultase fallido, el tipo del interfaz que hubiese fallado se añadiría a una lista que se devolvería como salida del método (línea 74).

Dicha lista se pasa al método GenerateBox el cual dibuja el mensaje de advertencia, en caso de que la lista  tuviese algún elemento. En caso de estar vacía, el método no dibujaría nada. El mensaje dibujado se devolvería en un ErrorBox (línea 79) que se añadiría al contenedor (línea 85), tras borrar su contenido anterior (línea 82)

Por su parte, el chequeo de interfaces es muy sencillo.

Chequeo de interfaces sobre el script del campo
Chequeo de interfaces sobre el script del campo


A partir de la línea 120 se puede ver que, por cada uno de los interfaces de la lista (los pasados como parámetros del atributo), se comprueba si el script (checkedObject) que estamos chequeando lo implementa. Para ello, usamos el método de C# IsInstanceOfType (línea 123). Siempre he pensado que el método IsInstanceOfType es muy engañoso, porque leído de izquierda a derecha parece que quiere comprobar si "interfaceType-es-una-instancia-del-tipo-checkedObject", cuando, en realidad, lo que hace el método es comprobar si checkedObject es una instancia de interfaceType.

En caso de que checkedObject no implemente el interfaz, IsInstanceOfType devolverá falso y el tipo del interfaz se añadirá a la lista de interfaces no cumplidos (línea 125) que se devolverán como salida del método.

Por último, y por completitud, vamos a ver cómo se genera el mensaje de error en el método GenerateErrorBox.

Generación del mensaje de advertencia
Generación del mensaje de advertencia

Si el script a chequear cumpliese todos los interfaces, la lista que se le pasa como parámetro a GenerateErrorBox estaría vacía y el método se limitaría a devolver null (línea 90).

Sin embargo, si hubiese habido algún incumplimiento, se encadenarían los nombres de los interfaces no cumplidos, concatenándolos con comas entre ellos, como se puede ver en la líneas 93 a 95.

Esa lista de nombres de interfaces, concatenados con comas, se añadiría al final del mensaje de advertencia (línea 98) y con este se crearía un mensaje de error, que se devolvería para ser añadido al contenedor (línea 102).

Y con esto ya lo tienes todo. Es cierto que no vas a poder impedir que se metan en el campo decorado scripts que no cumplan con los interfaces, pero al menos saldrá un mensaje avisando del problema. Será muy difícil que no repares en el mensaje y soluciones el desliz. Por otro lado, todo este ejemplo ha servido para que veas cómo implementar un atributo personalizado con el que hacer uso de la flexibilidad del inspector de Unity. Espero que te haya resultado interesante.

09 agosto 2025

Luces y sombras de la iniciativa "Stop Killing Games"

La iniciativa Stop Killing Games es un movimiento de consumidores iniciado en 2024 por el youtuber Ross Scott, conocido por su canal Accursed Farms, en respuesta al cierre de los servidores de The Crew, un videojuego de Ubisoft que quedó injugable al requerir conexión constante a internet, incluso en su modo de un solo jugador. Esto generó indignación, ya que el título, aunque tenía componentes multijugador, también incluía un modo de un solo jugador que no tenía por qué depender de una conexión online. La iniciativa plantea un debate crucial sobre la propiedad digital en una era donde los juegos son cada vez más caros (con precios de hasta 80-100 euros) y dependen de servidores que pueden cerrarse. 

El logo de la iniciativa Stop Killing Games
El logo de la iniciativa Stop Killing Games

Ross Scott lanzó un video presentando la iniciativa y creó un sitio web (stopkillinggames.com) para recolectar firmas y promover peticiones gubernamentales en países como Francia, Reino Unido, Canadá, Australia y la Unión Europea.


Como respuesta a casos como este, la iniciativa exige preservar los videojuegos tras el fin de su soporte oficial, protegiendo los derechos de los consumidores y evitando que los juegos comprados se vuelvan inaccesibles por decisiones de las editoras. Su argumentación se basa en la oposición a que las editoras vendan los juegos como licencias temporales en lugar de productos permanentes. Frente a eso, defienden que los jugadores deberían tener derecho a disfrutar de lo que pagaron sin restricciones de tiempo. Además, reivindican los videojuegos como patrimonio cultural, comparándolos con películas o libros que no desaparecen tras su comercialización. 

Por eso, la iniciativa busca proteger legalmente a los jugadores ante cierres súbitos de servidores que dejen los juegos injugables. Para ello exigen que los juegos incorporen siempre un modo offline, con campañas de un solo jugador, para que no dependan de servidores activos; o bien que los juegos permitan a los usuarios crear servidores privados para mantener vivos los juegos cuando las empresas dejen de soportarlos.

Desde su arranque, la iniciativa ha sido apoyada por más de 1.4 millones de firmantes, medios de comunicación, y creadores de contenido como PewDiePie, MoistCr1TiKaL y Jacksepticeye. Incluso uno de los vicepresidentes del Parlamento Europeo (Nicolae "Nicu" Ştefănuță) mostró su apoyo a la iniciativa y unió su firma a las demás. No es determinante, porque el Parlamento Europeo es muy grande, pero si es la punta del iceberg de una tendencia cada vez más evidente de ponerle freno a ciertas prácticas abusivas.

Nicolae "Nicu" Ştefănuță
Nicolae "Nicu" Ştefănuță, uno de los vicepresidentes del Parlamento Europeo, apoyó publicamente la iniciativa

Frente a eso, se encuentran, como es lógico las principales empresas desarrolladoras. El lobby Video Games Europe, que representa a empresas como EA, Ubisoft y Activision, argumenta que las propuestas de la iniciativa harían el desarrollo de juegos "prohibitivamente caro" y defiende que los jugadores no son propietarios de los juegos, sino de licencias revocables. No es el único caso, otros influencers del mundo del desarrollo de videojuegos también han mostrado su oposición al movimiento de Ross Scott. El streamer Pirate Software (Jason Thor Hall) criticó la iniciativa, alegando que no es viable para todos los juegos y que podría dañar a los desarrolladores, lo que generó un enfrentamiento con Scott.

Está claro que las productoras defienden sus intereses, pero algunos de sus argumentos no carecen de base y merece la pena considerarlos para que iniciativas bien intencionadas no se acaben llevando por delante a los estudios indies o condicionando negativamente un mercado tan dinámico como el de los videojuegos.

Para empezar, la iniciativa peca de meter a todos los videojuegos en el mismo saco, cuando los hay de muy distintos tipos. La principal distinción es que hay juegos diseñados para funcionar íntegramente con los recursos locales de tu ordenador o consola (por ejemplo Half-Life: Alyx), mientras que otros juegos dependen de servidores online que son los que gestionan toda la lógica del juego y guardan el progreso de los jugadores (ejemplos evidentes Fortnite o World of Warcraft). 

Lo que parece olvidar esta iniciativa es que cuando un juego está diseñado de una manera, no es tan sencillo rediseñarlo de otra. Si un juego está construido para depender de servidores a veces es imposible rediseñarlo para que, llegado el caso, pueda redistribuirse localmente y funcionar con un servidor privado que se monte el usuario en casa. Los juegos online suelen contar con múltiples elementos licenciados en el extremo del servidor (motores físicos, frameworks para el mathmaking, etc). Esos elementos no son propiedad de los desarrolladores y, en muchos casos, no pueden redistribuirlos sin infringir los términos de sus licencias. Esto mismo se ve más claramente con el contenido audiovisual. Piensa en la música que te puedes comprar dentro de Fortnite, para amenizar las esperas en el lobby. Muchas de esas canciones son de artistas muy conocidos que han licenciado su obra para que esta sea escuchada dentro de Fortnite. Si esas canciones tuvieran que salir de los servidores centrales de Fortnite, para redistribuirse a copias locales del juego, el riesgo de divulgación incontrolada de esas canciones se dispararía, por lo que las productoras de los artistas querrían cubrirse las espaldas renegociando los derechos. También podría optarse por distribuir el juego sin ese contenido licenciado, pero eso ya iría en contra de la iniciativa, que defiende el derecho de los usuarios a quedarse con todo lo que hubieran pagado. Teniendo en cuenta lo anterior, si ya sería complicado redistribuir contenido audiovisual licenciado, quitar elementos funcionales sujetos a licencias puede resultar imposible al impedir la ejecución del juego. Por último, no hay que olvidar la potencia de cálculo. Guardar el estado y la evolución de un MMORPG requiere de mucho procesamiento, almacenamiento y de una red de datos poderosa. Los principales juegos de ese tipo contratan centros de proceso de datos completos. Replicar eso en algo que pudiera montar el usuario en su casa es prácticamente imposible.

CPD
Muchos juegos online se ejecutan en complejos centros de proceso de datos, imposibles de replicar en un domicilio

Luego está el paso del tiempo. A diferencia de una película, que sólo necesita un reproductor adecuado para poder verla, los videojuegos que dependen de servidores necesitan una labor continua, y experta, de mantenimiento que está más allá de las habilidades y conocimientos del común de los mortales. Esperar que cualquiera pudiera mantener en su casa los servidores de juegos tan complejos sería muy poco realista.

El mantenimiento de una arquitectura tan compleja está más allá de las posibilidades del ciudadano medio
El mantenimiento de una arquitectura tan compleja está más allá de las posibilidades del ciudadano medio

Por tanto, aunque la motivación que hay detrás de la iniciativa es perfectamente comprensible, hay muchos factores prácticos que no parecen haber tenido en cuenta y que se está discutiendo ampliamente en los foros de desarrollo de videojuegos. Al menos, esa discusión es sana y quizá alumbre un punto intermedio que sea asumible por todos. 

Algunos están proponiendo, por ejemplo, que los juegos cuenten con un etiquetado que advierta de la dependencia de servidores externos, de manera que el comprador pueda evaluar el riesgo y tomar una decisión informada.

El etiquetado, una posible solución
El etiquetado, una posible solución

Otros están proponiendo un marco de adhesión voluntaria para las desarrolladoras, en las que estás asuman a priori unos compromisos a la hora diseñar el juego, de manera que ofrezca una serie de garantías de preservación a largo plazo. Así, por ejemplo, se podría establecer una primera categoría de juegos que ofreciese modos de juego offline, aunque fuesen limitados, a base de bots, mapas específicos o conexiones locales con otros jugadores; una segunda categoría podría abarcar a juegos que llegasen a a acuerdos con entidades sin ánimo de lucro que se comprometiesen a conservar el juego con determinadas medidas de seguridad; una tercera categoría incluiría a todos aquellos juegos que pudieran ofrecer una instalación en un servidor local del jugador. Al igual que con la propuesta del etiquetado, la categoría se anunciaría junto con el juego, para que sus potenciales compradores pudieran evaluar el riesgo de su adquisición.

La categorización voluntaria puede ser otra solución
La categorización voluntaria puede ser otra solución

En todo caso, aunque la regulación que exige la iniciativa esté dirigida a proteger a los compradores, hay que diseñarla con mucho cuidado para que no suponga un freno a la iniciativa y la creatividad de un mercado tan pujante, económica y culturalmente, como el del videojuego. Determinadas condiciones, que pueden ser perfectamente factibles por el músculo de los grandes estudios, podrían ser inasumibles por los estudios pequeños e indies al disparar los costes y el riesgo legal. Se trata por tanto de proteger a los compradores, pero sin llevarse por delante a los pequeños estudios. Hay que preservar los juegos, pero de una forma que no limite seriamente la producción de nuevos juegos. En caso de asumir esta tarea, los legisladores tendrán que afrontar un equilibrio muy delicado que les va a exigir un conocimiento muy profundo de un mercado complejo y cambiante como es el de los videojuegos.

Equilibrio legislativo para esta iniciativa
Esta iniciativa exige una labor legislativa muy delicada