Cómo hacer un GDD (parte 1)

Este artículo es una continuación de la breve guía para nuevos proyectos. Los documentos de diseño de juego, típicamente abreviados como GDD (Game Design Document), se crean durante las etapas previas al desarrollo. Cuando hablamos de diseño de juego, no nos referimos únicamente al diseño gráfico, sino al conjunto del juego, a la idea completa que tenemos en la cabeza y que es necesario organizar, dividir y documentar para ayudar al resto del equipo a entenderla. Y también para ayudarte a ti, el diseñador del juego, a comprender mejor todos los aspectos de tu propia idea.

Las imágenes que ilustran este artículo pertenecen al documento de diseño de Anima, uno de los videojuegos presentados a CPCRetroDev 2020, el concurso de creación de videojuegos de la universidad de Alicante.

El GDD de Anima está creado por Carlos Perezgrín, y la estructura inicial del mismo fue propuesta por Fran Gallego, también conocido como Profesor Retroman. Mis agradecimientos a Carlos por haber compartido su documento y darme permiso para usarlo como ejemplo.

Si quieres, puedes usar los siguientes puntos como ideas para empezar con tu GDD. No es necesario extenderse mucho, un GDD no es un trabajo de la escuela, no tenemos que impresionar al profesor con un número elevado de hojas. Es un documento para ayudarte a ti y a todo el equipo, tiene que ser práctico.

Nombre del juego

Elegir un nombre es más difícil de lo que parece. Si aún no está decidido el nombre definitivo, se puede utilizar un nombre provisional de proyecto.

Breve descripción general

Imagina que alguien te pregunta cómo es tu videojuego. Pues eso es lo que tienes que explicar aquí. ¿En qué consiste?, ¿cómo se juega?, ¿es un arcade?, ¿requiere reflejos y habilidad?, ¿es un juego de rompecabezas o puzzle?, ¿cuales son los principales atractivos para el jugador?, ¿a qué tipo de público podría gustar?, ¿cómo es el nivel de dificultad?, ¿cuánto dura una partida?, ¿son partidas cortas de 1 minuto, o tiene un desarrollo largo?

Ejemplo del juego Ánima (descripción general)

Argumento e historia

Si el juego tiene argumento e historia, incluye un breve resumen de la idea argumental y presenta a los protagonistas.

Ejemplo del juego Ánima (historia)

En algunos casos no hay historia. Puede ser el caso si tu juego es un puzzle o juego de mesa con fichas y tablero.

Protagonista, personajes y objetos

Necesitarás algunos bocetos de los protagonistas, personajes secundarios, enemigos u objetos importantes del juego. Se puede juntar con el apartado anterior si los protagonistas están muy unidos con la historia.

Ejemplo del juego Ánima (protagonista)

Al principio, los gráficos del juego todavía no estarán creados, es suficiente con hacer un dibujo sencillo de la idea aproximada. Un dibujo hecho a mano en un cuaderno es una buena opción, rápida y efectiva; haz una foto y adjúntalo al documento. Después, el artista gráfico podría encargarse de crear bocetos más elaborados si se considera oportuno.

Ejemplo del juego Ánima (enemigos)

Otra opción es hacer descipciones textuales detalladas de tus personajes y cooperar desde el comienzo con el artista gráfico para crear los bocetos.

A veces podría no haber personajes ni enemigos, sólo unos pocos objetos. Este también puede ser el caso de muchos juegos de mesa o puzzle, pero a cambio tendrás que trabajar las reglas del juego con mucho detalle en el apartado de las mecánicas, no conseguirás librarte del GDD por elegir un juego de tipo puzzle 😉

¿Un jugador o varios jugadores?

Si es para un jugador, podrías necesitar inteligencia artificial o patrones fijos de movimiento de los enemigos, objetos y personajes controlados por el ordenador. Los detalles van en el apartado de mecánicas y diseño de niveles, en este apartado es suficiente indicarlo a nivel general.

Si hay opción de varios jugadores, puede ser en local (en el mismo ordenador, por turnos o simultáneos) o por red. Cuidado con el modo multijugador por red, puede ser sencillo en algunos casos, pero en otros podría complicar las cosas hasta niveles insospechados. Si no tienes experiencia en desarrollo multijugador, te recomiendo consultar con otras personas del grupo y buscar apoyo en esa materia previamente.

¿Es necesario guardar y continuar la partida?

Esta pregunta puede parecer opcional, pero es muy importante pensarlo desde el principio. Si no se tuvo en cuenta la opción de guardar la partida, pero quieres agregarla posteriormente, podría suponer un gran esfuerzo o incluso tener que replantear una buena parte de la programación del videojuego.

¿Tendrá versión móvil o es exclusivo de PC?

Para tener una versión jugable lo antes posible, puedes empezar con la versión de PC. La versión móvil puede hacerse posteriormente, sin mucha dificultad, siempre que el juego haya sido diseñado para ello.

Si tienes intención de que el juego funcione en móviles, hay que evitar mecánicas o características que no son adecuadas para una pantalla táctil, y en especial cualquier acción que requiera un ratón (coordenadas del puntero, doble click, botón derecho…). Si el juego está pensado exclusivamente para PC, tendrás mayor libertad en el diseño de pantallas, en el control, y en las mecánicas del juego.

La pantalla de un dispositivo móvil tiene unas dimensiones reducidas, y eso afectará al diseño del interfaz de usuario, el HUD (Heads-Up Display, la información que se presenta al jugador), el texto, tamaño de los botones y áreas interactivas.

El juego en detalle

Llega el momento de la descripción detallada de tu juego, las mecánicas, el diseño de niveles y pantallas, el interfaz de usuario…

Para evitar alargar demasiado este artículo (y también porque ya me cansé de escribir por hoy, todo sea dicho), continuaremos con la creación del GDD en la segunda parte, próximamente. ¿Verdad que fastidia cuando se interrumpe un artículo en la parte más interesante? XD

Guía para nuevos proyectos

Este artículo está dedicado a las tareas de organización en Zenvoid Studio, para planificar y desarrollar nuestros proyectos. Habrá continuaciones posteriores. Los procedimientos expuestos aquí están basados en los consejos y experiencias que, amablemente, han compartido con nosotros otros grupos dedicados al desarrollo de videojuegos.

Puede haber varios proyectos abiertos en paralelo, en distintas fases de realización. Cada persona decide en qué proyecto participar, según sus intereses. Puedes participar en varios proyectos al mismo tiempo, si dispones de tiempo y ganas para ello.

Quien propone un proyecto asumirá el papel de game designer. Puede haber más perfiles de tipo game designer en el equipo, pero sólo uno será el coordinador general y defensor del proyecto, que se encargará de zurrar con un látigo a los otros participantes cuando se duerman. Bueno, lo de zurrar con el látigo quizás correspondería al productor, pero de esos no tenemos. Ni látigos tampoco, estamos en ello.

Algunos proyectos requieren colaboración de múltiples perfiles. Puedes centrarte en la parte que más te gusta y contar con apoyo en las áreas que no son tu especialidad, o simplemente distribuir tareas para avanzar más rápido.

Veamos a continuación las tareas que tenemos que hacer antes de aventurarnos con el desarrollo. No es necesario seguir un orden concreto, se pueden hacer en paralelo.

Crear un prototipo del juego

Es opcional, en algunos casos será conveniente y en otros no será necesario. El prototipo no es el juego, es simplemente una muestra de ideas muy simplificada. Se lo mostraríamos al productor, si lo tuviésemos. Como no tenemos, me lo puedes enseñar a mi, e iré rápidamente a comprar un látigo 😉

Con el prototipo, organizaremos una presentación en una próxima reunión, lo mostraremos a todo el grupo para que sepan que tu idea es genial y se animen a participar.

Si tu especialidad es el arte visual, en lugar de un prototipo podrías crear arte conceptual y bocetos de la idea, que serán igualmente efectivos.

Y si no puedes crear un prototipo ni tampoco arte conceptual, un documento o presentación con la explicación detallada de tu proyecto servirá para que se entienda la idea. Como no tenemos forma de extraer información directa de cerebros ajenos (todavía), debes comunicar tus ideas al grupo de alguna manera.

Formar el equipo y elegir los perfiles

Si tienes la intención de hacerlo tú todo, no sería necesario formar equipo. Incluso en ese caso, siempre puedes contar con sugerencias, pruebas y ayuda puntual del grupo.

Lo más habitual es que necesites colaboradores, excepto cuando el proyecto es muy sencillo. Es improbable dominar todas las áreas y materias necesarias para crear un videojuego. Haz una lista de los puntos en los que necesitarás apoyo.

  • El diseñador del juego (game designer) serás tú, pero puedes elegir cooperar en el diseño con otra persona si lo crees conveniente.
  • Uno o varios programadores.
  • Arte visual. Los dibujos conceptuales y los gráficos finales del juego son tareas diferentes, pueden crearse por personas distintas, o la misma.
  • Interfaz de usuario.
  • Diseño de niveles.
  • Efectos de sonido, música.

Durante el desarrollo, mantendremos una tabla para recordar quién tiene asignada cada tarea, y quién está trabajando en cada escena o componente del juego en cada momento. Es importante delimitar correctamente las escenas y elementos asignados a cada desarrollador para evitar colisiones en las modificaciones.

Los proyectos ya empezados tienen prioridad, para que se completen satisfactoriamente. Por ese motivo, cuando hay varios proyectos en curso, podría ser difícil que encuentres perfiles disponibles para empezar uno nuevo. En ese caso, una opción sería guardar la idea durante un tiempo y volver a exponerla en un momento más favorable.

Crear un GDD (Game Design Document)

Hemos hablado de los GDD en otras ocasiones, los documentos de diseño del juego (Game Design Document). Sirven de referencia durante el desarrollo, permiten mantener al equipo enfocado en una línea de trabajo concreta, anticiparse a las dificultades y tomar decisiones.

Como es la parte más extensa, continuaremos con una guía para crear el GDD próximamente, en otro artículo. Hasta entonces, este juego servirá como ejemplo:

Charge Kid es un videojuego creado con Godot Engine, ha sido publicado en PC y consola. Lo más interesante para nosotros es que el desarrollo es open source, su código fuente completo está disponible. Y además, los autores han sido especialmente generosos por compartir el documento de diseño del juego.

Juegos en red y servidores

Hemos estado evaluando la posibilidad de poner nuestros propios servidores de juegos en red. En los tiempos que corren, con el aforo limitado a 6 personas, los servidores parecen una buena opción; podríamos unirnos con quienes no pueden acudir presencialmente, abrirse a invitados externos, y seguir jugando a distancia en caso de que la mutación del virus cause el apocalipsis zombi.

Voy a empezar con una lista de algunos videojuegos candidatos. Continuaré con la lista en otros artículos para evitar que sea demasiado largo, si tienes alguna sugerencia, no dudes en escribir un comentario.

SuperTuxKart

SuperTuxKart es software libre y se desarrolla en abierto. Es un sencillo videojuego de mini coches con trampas, claramente inspirado por la serie de videojuegos Mario Kart. Tiene una sección de documentación para interesados en colaborar que es un ejemplo excepcional de que escribir documentación voluntariamente no es imposible.

En alguna ocasión anterior hemos disfrutado de sesiones de juego de Super Tux Kart en red local, hace aproximadamente un año, poco después de que añadieran esa opción al juego. Existe la posibilidad de poner un servidor, pero parece que el modo de red que incluye Super Tux Kart tiene algunas carencias que habría que estudiar en detalle.

Assetto Corsa

Nos gusta dar prioridad al software libre siempre que sea posible, pero hay ocasiones en las que no disponemos de una alternativa aceptable. El caso de los simuladores de conducción deportiva resulta especialmente interesante… simular el comportamiento físico de un vehículo de competición para que sea creíble no es una tarea sencilla. En alguna ocasión hemos tratado este asunto, lógicamente a un nivel ampliamente simplificado, para que tenga cabida dentro de nuestras posibilidades.

Será el servidor que probemos primero, por preferencia personal de algunas personas que nos juntamos habitualmente (o dicho de otra forma: porque nos apetece). Nos hemos decidido por Assetto Corsa, aunque también podríamos ampliarlo en un futuro a otros simuladores, como puede ser rFactor.

En el servidor, nos vamos a apoyar en Assetto server manager, un programa software libre con un interfaz de administración amigable, que facilita la creación de servidores dedicados, permite mantener puntuaciones, organizar campeonatos, y una larga lista de ventajas adicionales.

En la foto, cortesía de Miguel, vemos su simulador con triple pantalla, una estructura personalizada a medida y volante modificado. Las tres pantallas no son imprescindibles, pero mejoran la visión lateral, ayudan a evitar colisiones y mantener un juego limpio. Y además mola más (que en realidad es el motivo principal, pero parecemos más buenos si se menciona lo del juego limpio primero).

Los simuladores requieren de hardware específico y espacio para disfrutar correctamente de la experiencia. Eso puede suponer un inconveniente para uso en remoto porque no todo el mundo puede mantener el despliegue en su casa particular.

La mayoría del grupo, como nos gustan este tipo de juegos desde hace años, hemos ido mejorando nuestro material con el tiempo y lo tenemos disponible, pero si no es el caso y te falta algún componente necesario (por ejemplo, un volante de calidad aceptable), te animamos igualmente a unirte a las carreras. De momento no pretendemos llevar un campeonato formal, sino divertirnos, y eso lo puedes hacer con independencia del resultado, y además puedes echarle la culpa al volante 😉

AssaultCube

AssaultCube es un videojuego de código abierto en primera persona que recuerda a Counter Strike, basado en el motor Cube. AssaultCube está hecho para jugar en línea, aunque tiene un modo offline en el que se puede jugar con bots. AssaultCube es gratuito, ya que su motor es código abierto, aunque sus texturas y otras partes del juego no lo son. Es un juego muy rápido que funciona en casi cualquier ordenador y sistema operativo, necesita pocos recursos.

Laberinto de neón

Seguimos con los prototipos de videojuegos con mecánica de rotación de escenario. Para diferenciarlo de otros proyectos que se vayan haciendo en paralelo, lo clasificaremos en su propia categoría con el nombre «Proyecto Newton».

El siguiente video es un ejemplo de rotación del escenario en modo relativo, con rotación completa del escenario, que fue explicado con más detalle en la entrada anterior.

Me recuerda ligeramente a las fases especiales del primer juego de Sonic para Mega Drive. En ellas había que encontrar y recoger la esmeralda del caos en un escenario con rotación continua. En el caso de Sonic el control es para el protagonista, mientras que la rotación del escenario es automática y sólo se puede influir en ella tocando ciertos objetos repartidos por el escenario. En este otro ejemplo, el jugador controla la rotación y los objetos se mueven indirectamente por efecto de la gravedad, pero también se podría probar a añadir controles de saltos o impulsos.

El código fuente de este ejemplo está disponible en un repositorio de github.

De momento el objetivo es mostrar ideas, no es necesario que estén desarrolladas en detalle. Si alguna de las ideas nos gusta especialmente, la podríamos elegir para desarrollo y hacer un prototipo jugable.

La idea puede estar basada en el concepto inicial (un objeto que cae hasta la salida evitando las trampas), o puede ser una idea nueva o cualquier prueba experimental que se te pase por la cabeza y que pueda encajar con la mecánica de la gravedad. ¿Qué ideas se te ocurren?

Rotación del escenario

Vamos a continuar con la propuesta de juegos basados en la rotación de escenario para Godot. Aquí está publicado el código que se encarga de la rotación, para usarlo como plantilla y que podamos centrarnos en elaborar las ideas de mini juegos.

Por supuesto, si prefieres programar su propio código de rotación, no es necesario utilizar este ejemplo, pero recomiendo leer los siguientes párrafos igualmente, para ahorrar tiempo.

Aunque el código para el control por rotación del escenario no es muy extenso, tampoco es tan trivial como puede parecer. En una primera impresión podríamos pensar en rotar los nodos que corresponden al escenario del juego. Si el mapa tiene desplazamiento, habría que hacerlo tomando como centro de rotación la posición del actor o de la cámara, en lugar de utilizar un punto de rotación fijo.

Esta manera tiene muchas complicaciones, en especial con las áreas de colisión de los StaticBody y la física de los RigidBody. Si lo intentas hacer así, lo más probable es que acabes desesperado intentando comprender qué está pasando con la física y por qué los RigidBody se comportan erróneamente con las paredes.

Cuando nos encontramos con una situación como ésta, conviene detenerse a pensar si hay una manera alternativa más simple y directa de obtener el resultado. Y normalmente esa manera alternativa existe. En este caso, la solución más efectiva fue rotar la cámara y el vector de gravedad. Sencillo, rápido, y funciona perfectamente sin efectos secundarios.

Empezamos por configurar la cámara

El nodo de la cámara no debe ser un nodo hijo del nodo RigidBody. Eso sería lo más habitual, dejar que la cámara copie automáticamente la posición del nodo que será el foco de atención, pero en este caso debe pertenecer a la escena del mapa o del juego global porque necesitamos manipular manualmente sus propiedades.

Como la cámara no copiará la posición del nodo del jugador, debemos hacerlo manualmente desde otro lugar, algún nodo situado más arriba en la jerarquía.

func _process(_delta):
	$Camera.position = $Character.position

El script asociado a la cámara es el que procesa las acciones del jugador. Esta es otra particularidad de la mecánica de juego, el movimiento es indirecto: no controlamos al objeto o personaje, sino la inclinación del escenario, y resulta más conveniente que sea procesado en la cámara o en otro lugar global. Los ejemplos que muestro a continuación están pensados para funcionar dentro del script de la cámara.

func _process(delta):
	var wanted_rotation := 0.0
	if Input.is_action_pressed("go_left"):
		wanted_rotation = PI / 3
	if Input.is_action_pressed("go_right"):
		wanted_rotation = -PI / 3

	absolute_rotation(wanted_rotation, delta)
	rotate_gravity_vector()

Las funciones absolute_rotation y rotate_gravity_vector se puede ver a continuación. Voy a mostrar dos funciones para hacer la rotación de la cámara, puedes elegir la que prefieras, según el tipo de juego o preferencias de control.

Es conveniente recordar que el nodo Camera2D tiene una propiedad Rotating, que debe activarse para que los cambios de rotación tengan efecto.

Control absoluto de posición de rotación

func absolute_rotation(target: float, delta: float):
	var v1 := Vector2.DOWN.rotated(rotation)
	var v2 := Vector2.DOWN.rotated(target)
	var wanted_angle := v1.angle_to(v2)

	var angle := wanted_angle * rotation_speed_factor * delta
	var min_angle := rotation_speed_min * delta

	if abs(angle) < min_angle:
		angle = sign(angle) * min_angle
		if abs(angle) > abs(wanted_angle):
			angle = wanted_angle

	rotation += angle

La función absolute_rotation recibe un ángulo en radianes en la variable target. Este ángulo indica la posición final a la que el usuario quiere rotar el escenario, donde 0 significa la posición «normal» sin rotación. Con un joystick analógico se podría usar la posición del joystick para que fuese copiada por el escenario.

La rotación se hace siempre por el camino más corto entre el ángulo actual y el ángulo destino, y no se hace instantáneamente, sino que aplica interpolación durante un tiempo para suavizar el movimiento. Los parámetros de esta interpolación se pueden controlar mediante dos variables:

var rotation_speed_factor := 2.5
var rotation_speed_min := 1.2

Así se hizo el juego que salió en la fachada de Medialab Prado, el ángulo rotación se tomaba directamente de la posición de las personas en la plaza.

Control relativo de posición de rotación

# Declaramos en el script
var rotation_speed := 0.0

func relative_rotation(force: float, delta: float):
	if abs(force) > 0.001:
		rotation_speed += rotation_accel_factor * force * delta
		rotation_speed = clamp(rotation_speed, -rotation_speed_max, rotation_speed_max)
	else:
		if abs(rotation_speed) < 0.05:
			rotation_speed = 0
		else:
			var deceleration := rotation_speed * rotation_stop_factor * delta
			if sign(rotation_speed) != sign(rotation_speed - deceleration):
				rotation_speed = 0
			else:
				rotation_speed -= deceleration

	rotation += rotation_speed * delta

La función relative_rotation es más indicada para juegos en los que el escenario puede rotar 360 grados, dar vueltas sin fin, y no hay una posición que se pueda considerar como la posición «normal». Recibe un valor que, a partir de la posición actual, indica la fuerza con la que se quiere rotar el escenario y la dirección de rotación (signo positivo o negativo).

Es conveniente que el valor de fuerza se mantenga entre -1 y 1. Para un control digital como puede ser el teclado, el valor de fuerza podría ser -1 hacia un la derecha y 1 hacia la izquierda.

La rotación tiene efecto de aceleración e inercia, lo que proporciona una sensación de control más suave. Para controlar estos parámetros se utilizan tres variables:

var rotation_speed_max := 2.0
var rotation_accel_factor := 6.0
var rotation_stop_factor := 3.5

Ajuste del vector de gravedad

En este punto ya tenemos la rotación de la cámara controlada por el jugador, pero la gravedad sigue funcionando en la misma dirección: si rotamos hacia la izquierda, tendremos la sensación de que las cosas caen hacia la derecha. Para que todo caiga siempre «hacia abajo» (visto desde fuera de la pantalla), hay que rotar también el vector de gravedad.

Hay varias formas de hacerlo. Originalmente se hizo modificando las propiedades de un nodo de tipo Area2D invisible que cubría el área de juego, pero hay otra manera más directa que se aplica globalmente a la física de todo el juego al completo:

func rotate_gravity_vector():
	Physics2DServer.area_set_param(
			get_world_2d().space,
			Physics2DServer.AREA_PARAM_GRAVITY_VECTOR,
			Vector2.DOWN.rotated(rotation))

La función rotate_gravity_vector se ejecuta después de alguna de las dos funciones anteriores, y se encarga de ajustar los parámetros de Physics2DServer para que el vector de gravedad se mantenga en la posición esperada.