Para cada uno de los niveles se han diseñado diferentes escenarios usando las salas y elementos previamente almacenados como si de piezas físicas encajables se trataran.
Se ha limitado a cuatro niveles diferentes en los cuales se trabajarán elementos concretos de cualquier lenguaje de programación. Aun así, se ha optado por representar el código en lenguaje JavaScript (el mismo que verdaderamente lo implementa en Unity) con algunas licencias pensadas en dar mayor legibilidad y ocupar un menor espacio, haciéndolo más accesible al jugador.
La estructuración en niveles permite introducir progresivamente nuevos elementos o conceptos con los que tiene que tratar el jugador además de mantener, si se desea, los conceptos que ya se han ido viendo anteriormente. De esta forma se crea una curva de dificultad que atiende a razones puramente teóricas y no a un aumento de la exigencia en las mecánicas del videojuego, como ocurre en la mayoría de ellos.
A continuación se exponen los diseños finales de los cuatro niveles que componen el juego con detalle en las modificaciones de código necesarias y los conceptos teóricos trabajados.
NIVEL 1: TUTORIAL Y CONCEPTOS BÁSICOS
El primer nivel del juego sirve como tutorial y toma de contactos con las mecánicas jugables. Los conceptos que se presentan son los cambios en los valores de asignaciones de variables ya establecidas. De esta forma se obliga al jugador a afrontar simples puzles o situaciones que requieren un simple conocimiento de los tipos de variables comunes. De esta forma mediante el diseño, nos aseguramos que capta la idea del juego de cara a niveles más complejos.
En concreto las situaciones a resolver son las siguientes;
El primer código modificable está justo delante de la puerta a la que referencia.
Se debe cambiar el valor booleano de la variable cerrada para avanzar.
A modo de curiosidad y para que el jugador pueda investigar, se puede cambiar el color del láser que impide el paso por los parámetros definidos para la clase Color de Unity.
En un vacío insalvable se encuentra la plataforma que nos permite pasar modificando su tamaño. También incluye la primera referencia a una función la cual puede ser activada simplemente clicando sobre ella sin necesidad de editar nada. En este caso devuelve la plataforma a su tamaño original.
El primer bug del juego se muestra enjaulado y enfrente de la puerta final del nivel que impide el paso hacia la salida si todavía quedan bugs en el escenario.
Simplemente hay que cambiar el valor de la variable.
NIVEL 2: INCORPORA LA CREACIÓN DE VARIABLES Y SENTENCIAS CONDICIONALES
En la segunda fase se introducen las sentencias condicionales. Este tipo de sentencias sirven para redirigir el flujo del código según unas condiciones que se han especificado previamente. De esta forma se consigue que se ejecuten las partes de código deseadas en función de los valores que le llegan como entrada.
En concreto las situaciones a resolver son las siguientes;
El primer bug del nivel plantea una situación muy simple con el tipo de sentencia condicional if- else. Si no se presta atención al orden de los cambios que se realizan, el flujo del código hace que se reinicie el nivel.
Para solucionarlo debemos fijarnos que la vida debe sustituirse por cero o un valor negativo y después cambiar el tipo de variable que se compara en la condición por la especificada en la línea de arriba. Si se hiciese con el orden a la inversa, se ejecutaría la otra parte del código ya que el campo vida tiene más valor que cero.
El segundo bug es único en el juego ya que no se elimina mediante código, sino que debe hacerse caer abriendo la trampilla en la que está situado. Para ejecutar este código se introduce el concepto de captura de excepciones.
La captura de excepciones puede ser considerada un tipo de elemento condicional de programación ya que, debidamente usadas, pueden ser beneficiosas para el flujo del código, aunque su uso se suela limitar a desenmascarar errores de ejecución.
En este caso se intenta abrir uno de los archivos XML con el código de la fase pero lo que se pretende es que falle para poder pasar por el código de la excepción. Para ello simplemente debemos inventarnos un nombre de fichero ya que no lo encontrará y saltará la excepción.
El último código presenta el tipo de condicional restante, la sentencia de tipo “switch”. Es el equivalente a concatenar una serie de if-else que comprueban el valor de la misma variable y en función del resultado obtenido ejecutan una parte de código u otra.
Para poder avanzar hay que poner el primer caso como “vertical”. De esta forma se ejecuta el primer código ya que coincide con la variable declarada. Luego se han de modificar los dos restantes por los valores que se deseen para que ninguno coincida y el flujo pase por el default permitiendo mover la plataforma.
NIVEL 3: INCORPORA SENTENCIAS ITERATIVAS
En la tercera fase se introducen las sentencias iterativas while y for, junto con un puzle de reorganización de código. Las sentencias iterativas son altamente frecuentes y sirven para ejecutar una porción de código múltiples veces.
El primer código hace uso simplemente de asignaciones de variables para poder mover una plataforma según los grados especificados y el eje en que se debe mover.
El segundo bug hace referencia a una secuencia de código. Para destruirlo simplemente hay que ordenar el código correctamente y hacer que la comprobación del condicional se cumpla.
El último código del nivel pone en práctica la el uso de la sentencia while de forma gráfica con la generación de unos pequeños robots que sirven para cumplir la condición de salida y poder abrir la puerta.
NIVEL 4: CONCURRENCIA
El último de los niveles se basa enteramente en la concurrencia y paralelismo de ejecución de código. La concurrencia es la simultaneidad en la ejecución de múltiples tareas o hilos de ejecución creados por un único programa. La programación concurrente está relacionada con la programación paralela, pero enfatiza más la interacción entre tareas. Trata sobre la correcta secuencia de interacciones o comunicaciones entre los procesos y el acceso coordinado de recursos que se comparten por todos los procesos. De esta forma se consigue un uso más eficiente de los recursos físicos disponibles aunque los riesgos y problemáticas de la programación concurrente pueden ser complicados de detectar.
El primer obstáculo del nivel presenta un ejemplo claro de bloque mutuo o deadlock. El bloque mutuo es el bloqueo permanente de un conjunto de procesos o hilos de ejecución en un sistema concurrente, que compiten por recursos del sistema o bien se comunican entre ellos. A diferencia de otros problemas de concurrencia de procesos, no existe una solución general para los interbloqueos.
El ejemplo es claro, el primer hilo de ejecución accede a un recurso y lo bloquea mientras el segundo hace lo propio con el otro. De esta forma se consigue un cuelgue en el sistema. Para poder solucionarlo hay que serializar el flujo de los hilos haciendo que los dos pasen por las mismas funciones uno detrás del otro.
El último código del juego presenta una asignación de tres hilos de ejecución que no presentan problemas de bloqueo entre ellos. El jugador puede asignarlos libremente a las funciones representadas para poder desactivar la barrera y acabar con el enemigo. Primero se puede hacer una asignación basada en “Atacar, Quitar, Atacar” para luego parar los hilos y asignarles la función de “Atacar” a cada uno de ellos. Así se puede comprobar como el rendimiento aumenta eliminando antes al enemigo.
El diseño de niveles en los videojuegos es una parte crucial para ofrecer una experiencia de juego envolvente y desafiante:
- Objetivos Claros:
- Define claramente los objetivos del nivel. Ya sea avanzar en la historia, derrotar a un jefe, resolver rompecabezas o alcanzar un destino, los objetivos deben ser comprensibles para el jugador.
- Flujo y Progresión:
- Diseña el nivel para que haya un flujo natural de principio a fin. La dificultad debe aumentar progresivamente, permitiendo que el jugador mejore sus habilidades a medida que avanza.
- Variedad de Desafíos:
- Introduce una variedad de desafíos en el nivel para evitar la monotonía. Esto puede incluir enemigos diferentes, obstáculos, rompecabezas y cambios en el entorno.
- Feedback Visual y Auditivo:
- Proporciona feedback visual y auditivo para indicar el progreso del jugador y las consecuencias de sus acciones. Esto puede incluir efectos visuales, sonidos específicos y cambios en la música.
- Diseño No Lineal:
- Ofrece oportunidades para que los jugadores exploren y tomen decisiones. El diseño no lineal puede permitir múltiples rutas para alcanzar un objetivo, fomentando la exploración y la rejugabilidad.
- Puntos de Control y Guardado:
- Coloca puntos de control estratégicos para que los jugadores no tengan que repetir grandes secciones en caso de fallo. Asegúrate de que los puntos de guardado sean equitativos y no generen frustración.
- Narrativa Integrada:
- Integra la narrativa del juego en el diseño de niveles. Los eventos y los elementos del entorno deben contar la historia de manera coherente y complementar la experiencia del jugador.
- Estilo Visual Consistente:
- Mantiene un estilo visual consistente a lo largo del nivel y en todo el juego. La coherencia visual contribuye a la inmersión y ayuda a los jugadores a comprender mejor el entorno.
- Pruebas Iterativas:
- Realiza pruebas constantes del nivel con jugadores para obtener feedback. Observa cómo responden y ajusta el diseño según sea necesario. La iteración es clave para un diseño de niveles exitoso.
- Escalada de Dificultad:
- Ajusta la dificultad del nivel de manera que se alinee con la curva de aprendizaje del jugador. Comienza con desafíos más simples y aumenta gradualmente la complejidad.
- Secretos y Recompensas:
- Introduce áreas secretas y recompensas para incentivar la exploración. Esto puede añadir capas adicionales de profundidad al diseño del nivel y motivar a los jugadores a buscar más allá de la ruta principal.
- Accesibilidad:
- Considera la accesibilidad al diseñar niveles. Asegúrate de que haya opciones para diferentes niveles de habilidad y considera características como subtítulos y ajustes de dificultad.
- Optimización de Rendimiento:
- Optimiza el rendimiento del nivel para garantizar una experiencia de juego fluida. Esto incluye la gestión eficiente de recursos gráficos y la optimización del código.