Antes de sumergirte en la escritura del código Verse para el Puzzle de luces etiquetadas, es una buena idea pensar en la mejor manera de lograr lo que deseas hacer. Esta sección te muestra cómo abordar la creación de la mecánica del puzzle. Al final de este paso, tendrás un pseudocódigo que representa el algoritmo para crear el puzzle. En el siguiente paso, aprenderás cómo implementar dicho algoritmo en Verse y UEFN.
Identificar objetivos, requisitos y restricciones
El primer paso es identificar tus objetivos, requisitos y restricciones. Los requisitos surgen al desarmar los objetivos más amplios en otros más pequeños.
| Objetivos |
|
| Requisitos |
|
| Restricciones |
|
Dividir el problema
Ahora que comprendes lo que deseas y con qué estás trabajando, divide el problema en problemas más pequeños que serán más fáciles de razonar. Formular preguntas puede ayudar a descomponer un problema más grande:
- ¿Cómo puede el jugador interactuar con el puzzle?
- ¿Cómo se usan las etiquetas de juego para encontrar las luces?
- ¿Cómo se definen las condiciones iniciales y las soluciones que pueden modificarse en el editor?
- ¿Cómo hacer coincidir el estado de juego almacenado en una estructura de Verse con los gráficos en el juego?
- ¿Cómo puede la interacción del jugador actualizar un conjunto de luces específico?
- ¿Cómo se deshabilita la interacción del jugador después de resolver el puzzle?
A continuación, identifica las dependencias potenciales entre estos problemas más pequeños. En este caso, parece que los problemas son independientes, pero vale la pena considerar:
- Las preguntas 1, 5 y 6 están ligeramente relacionadas.
- En el caso de las preguntas 1 y 6, la manera en la que el jugador interactúa con el puzzle no puede determinar cómo se deshabilita la interacción después de resolver el puzzle.
- En el caso de las preguntas 1 y 5, una única interacción conmuta múltiples luces a la vez. Esto informará la estructura de datos a usar para el mapeo de interacción a luces.
- La pregunta 2 es una consideración de diseño importante. La manera en la que funciona la API etiquetas de juego podría influir en la manera en la que las luces se controlan en el código. Esto tiene ramificaciones para las preguntas 4 y 5 porque necesitarás cambiar el estado de luces en el juego y debes encontrar una manera común de hacerlo.
- Las preguntas 3 y 4 probablemente convergen en una única solución para la estructura de datos subyacente para los estados inicial, actual y solución.
Piensa potenciales soluciones
Ahora que el problema está dividido en problemas más pequeños, concéntrate en responder las preguntas relacionadas con dichos problemas:
1. ¿Cómo puede el jugador interactuar con el puzzle?
Hay múltiples soluciones a este pregunta. Por lo general, puedes usar cualquier dispositivo con el que el jugador pueda interactuar y que Verse pueda usar para detectar la interacción. El conjunto de herramientas de Creative tiene muchos dispositivos que reúnen estos requisitos, como, por ejemplo Dispositivos activadores, * Dispositivos Botón pero también Dispositivos de cuadro que cambia de color y Dispositivos activadores de percepción.
En el siguiente ejemplo, se usará el dispositivo Botón y su InteractedWithEvent, que se despacha cada vez que el jugador interactúa con el botón siempre que este esté habilitado. Si deseas obtener más información sobre los eventos, consulta Codificación de interacciones entre dispositivos.
2. ¿Cómo se usan las etiquetas de juego para encontrar las luces?
Con Etiquetas del juego, puedes recuperar grupos de actores a los que se le asigna una etiqueta personalizada que defines en el código de Verse.
Puedes usar la función GetCreativeObjectsWithTag() para obtener una matriz de todos los actores a los que se les asignó tu etiqueta personalizada. El resultado de la función es una matriz de todos los objetos que implementan creative_object_interface. El customizable_light_device es la representación de Verse de un dispositivo de Iluminación personalizable, y es una clase que implementa creative_object_interface.
No se garantiza el orden en la lista de dispositivos que devuelve GetCreativeObjectsWithTag(), y la llamada a la función puede demorar en devolver todos los dispositivos, en especial si hay muchos en el nivel; entonces, es una buena idea almacenar las luces para acceder con mayor rapidez en el futuro. Esto se llama guardar en caché y, a menudo, puede mejorar el rendimiento. Como las luces son una colección del mismo tipo, puedes usar un matriz para guardarlas juntas.
Esto significa que puedes:
- Crear una nueva etiqueta denominada
puzzle_light. - Marcar todas las luces para el puzzle con la etiqueta
puzzle_light. - Llamar a
GetCreativeObjectsWithTag(puzzle_light)para obtener todos los actores con la etiquetapuzzle_light. - Determinar cuáles resultados de la llamada a función son
customizable_light_device. - Guardar la lista de objetos
customizable_light_deviceen una matriz para poder acceder a ella más adelante.
3. ¿Cómo se definen las condiciones iniciales y las soluciones que pueden modificarse en el editor?
Una luz solo tiene dos estados: encendida y apagada. Puedes usar el tipo logic en Verse para representar el estado encendido/apagado de una luz, ya que los valores del tipo logic solo puede ser true o false. Como hay múltiples luces, puedes usar también aquí una matriz para almacenar todos los valores lógicos y que la matriz posicione, o indexe, un estado de luz que coincida con el índice con el que está asociada la luz.
Esta matriz de valores lógicos puede usarse para definir el estado inicial de las luces del puzzle y también contener el estado actual de las luces durante el juego. Puedes exponer esta matriz al editor con el atributo @editable. Al comienzo del juego, las luces pueden estar encendidas o apagadas para que coincidan visualmente con el estado almacenado en la matriz.
La solución del puzzle debería hacer coincidir el tipo usado para almacenar el estado actual de las luces de manera que puedas comprobar si se resuelve el puzzle al comparar los dos. Esto significa que tendrás dos matrices logic editables, una que representa el estado actual de las luces y la otra que representa la solución del puzzle. Es decir, que puedes cambiar el estado inicial de las luces del puzzle y su solución desde el editor de manera de poder reutilizar el puzzle con distintas configuraciones.
4. ¿Cómo hacer coincidir el estado de juego almacenado en una estructura de Verse con los gráficos en el juego?
Puedes encender y apagar un customizable_light_device en el juego mediante las funciones TurnOn() y TurnOff(). De manera que, siempre que actualices el estado actual de las luces según se representan en la matriz lógica, también deberías llamar a TurnOn() y TurnOff() para que coincida la gráfica del juego con su estado.
5. ¿Cómo puede la interacción del jugador actualizar un conjunto de luces específico?
De la primera pregunta, ya has determinado que el jugador interactuará con el puzzle mediante el dispositivo de botón. Puedes suscribir un controlador de eventos a InteractedWithEvent del botón que cambiará las luces cuando el jugador interactúe con dicho dispositivo. Debido a que el jugador puede usar múltiples botones, puedes, de nuevo, usar una matriz para agruparlos.
Ahora debes identificar cómo mapear cada evento de botón separado al conjunto de luces que debería conmutar.
Como el orden de las luces en la matriz customizable_light_device será el mismo orden que el de la matriz de lógica que representa el estado de las luces, puedes crear un mapeo entre un botón y los índices de las luces que va a afectar. Este mapeo puede representarse con una matriz, donde el orden de los elementos coincida con el orden de los botones y los elementos son matrices de índices.
Puedes hacer editable la matriz para poder cambiar el mapeo de botones a luces en el editor y reutilizar el puzzle sin cambiar el código mismo.
6. ¿Cómo se deshabilita la interacción del jugador después de resolver el puzzle?
Ya sabes que el jugador interactúa con el puzzle mediante el dispositivo de botón, que se detecta con InteractedWithEvent.
Cuando el puzzle esté resuelto, ¿cómo puede el dispositivo del puzzle dejar de recibir entradas del jugador de manera que él no puede ya cambiar el puzzle?
Hay, por lo menos, tres maneras de hacerlo:
- Deshabilitar los botones en el juego cuando se resuelve el puzzle.
- Añadir un campo
logicatagged_lights_puzzleque se modifique cuando se resuelva el puzzle. Cada vez que se actualice el estado del juego, este campologicdebe comprobarse primero para asegurarse de que no se ha resuelto el puzzle. - Desuscribir
InteractedWithEventde los botones cuando se resuelva el puzzle para que ya no se llame a los controladores de eventos.
La tercera opción es la mejor porque es una solución simple y eficiente. No necesitas crear nuevos campos para comprobar la ejecución condicional del código. El concepto de desuscribir de un evento de dispositivo también puede reutilizarse en otras situaciones. En general, es una buena práctica suscribir a un evento cuando desees que se te notifique acerca de él y desuscribir cuando ya no lo necesitas. Los detalles de la implementación de la desuscripción se explican más adelante en este tutorial.
Combinar las soluciones y planificar con pseudocódigo
Ahora que tienes las soluciones a los problemas más pequeños, combínalas para resolver el problema original. Formaliza el algoritmo para construir la solución mediante pseudocódigo.
¿Qué ocurre cuando comienza el juego? Se configuran las luces. Suscribes a InteractedWithEvent de los botones, encuentras todos los dispositivos con la etiqueta puzzle_light y los guardas en caché. También enciendes y apagas las luces del juego con base en el LightState inicial.
OnBegin:
El resultado de GetCreativeObjectsWithTag(puzzle_light) se almacena en la variable FoundDevices
para cada dispositivo en FoundDevices:
Si el dispositivo es un dispositivo de luz personalizable:
Guardar la luz
si ShouldLightBeOn?:
Encender la luz
else:
Apagar la luz
para cada botón:
Suscribir a InteractedWithEvent de botón con el controlador OnButtonInteractedWith
Una versión en pseudocódigo de OnButtonInteractedWith sería así, donde InteractedButtonIndex equivale al índice de la matriz button_device que coincide con el botón con el que el jugador está interactuando. Verás cómo recibir esta información dentro del controlador de eventos más adelante en el tutorial.
OnButtonInteractedWith:
Obtiene las luces asociadas con el botón con el que se está interactuando con la matriz ButtonsToLights y las guarda en Lights
# Conmuta las luces
por cada Light en Lights:
si IsLightOn?:
Definir el estado de juego de la luz en apagado
Apagar la luz
else:
Definir el estado de juego de la luz en encendido
Encender la luz
si IsPuzzleSolved():
Habilitar el generador de elementos
para cada botón:
Desuscribir de InteractedWithEvent de botón
El pseudocódigo de IsPuzzleSolved comprueba si el estado actual de las luces coincide con la solución. Si el estado actual no coincide con la solución, la comprobación fracasa y el bloque if IsPuzzleSolved del pseudocódigo anterior no se ejecuta. Si el estado actual coincide con la solución, la comprobación tiene éxito y se ejecuta el bloque if IsPuzzleSolved.
IsPuzzleSolved:
para cada Light:
si IsLightOn no es igual a IsLightOnInSolution
fracasa y vuelve
tener éxito
¡Has desarrollado un algoritmo!
Próximo paso
En el próximo paso de este tutorial, trasladarás este algoritmo al lenguaje de programación Verse y realizarás una prueba de juego de tu proyecto para ver los pasos en acción.