En este último paso del tutorial Puzle de luces marcadas, encontrarás la secuencia de comandos completa para el puzle e ideas para seguir modificando el ejemplo.
Secuencia de comandos completa
El siguiente código es la secuencia de comandos completa de un puzle reutilizable que requiere que el jugador encuentre la combinación correcta de luces alternando su estado con botones.
using { /Fortnite.com/Devices }
using { /Verse.org/Native }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation/Tags }
using { /Verse.org/Simulation }
# Deriva de la clase `tag` en el módulo Verse.org/Simulation/Tags para crear una nueva etiqueta de jugabilidad.
puzzle_light := class(tag){}
log_tagged_lights_puzzle := class(log_channel){}
<#
Puedes utilizar una instancia de esta clase para suscribirte a la función
InteractedWithEvent de un button_device y tener acceso a las propiedades por evento (en este caso, Indices y PuzzleDevice) en tu controlador de eventos.
Piensa que cada botón tiene su propio estado y controlador de eventos.
Como la clase no tiene <concrete> especificador, no es necesario especificar valores por defecto para campo.
#>
button_event_handler := class():
# Posiciones utilizadas para acceder a las luces que controla este botón.
Indices : []int
# tagged_lights_puzzle que creó este button_event_handler para que podamos llamar a funciones en él.
PuzzleDevice : tagged_lights_puzzle
OnButtonPressed(InPlayer : agent) : void =
# Dile a PuzzleDevice que conmute las luces en las posiciones que controla este botón.
PuzzleDevice.ToggleLights(Indices)
tagged_lights_puzzle := clase<concrete>(modo Creativo):
Logger : log = log{Channel := log_tagged_lights_puzzle}
# Botones que el jugador utiliza para interactuar con el puzle.
@editable
Buttons : []button_device = array{}
<#
El número de elementos de ButtonsToLights debe coincidir con el número de elementos de Buttons.
Cada matriz de índices con índice cero describe qué luces activa el botón en Buttons[x] (donde "x" es el índice del botón).
Por ejemplo, Buttons[2] activa la primera y la segunda luz, según lo especificado por array {0,1}.
#>
ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
<#
La matriz lógica LightsState se utiliza para controlar el estado de encendido y apagado de todas las luces.
También se utiliza para establecer el estado inicial de las luces, por lo que su número de elementos
debe coincidir con el número de luces etiquetadas con la etiqueta puzzle_light.
#>
@editable
var LightsState : []logic = array{false, false, false, false}
<#
El puzle está resuelto cuando LightsState coincide con SolvedLightsState.
Esto significa que SolvedLightsState debe tener tantos elementos como LightsState.
#>
@editable
SolvedLightsState : []logic = array{true, true, true, true}
@editable
# Este ItemSpawner se activa cuando se resuelve el puzle.
ItemSpawner : item_spawner_device = item_spawner_device{}
# Guarda las luces encontradas mediante etiquetas de jugabilidad.
var Lights : []customizable_light_device = array{}
# Contiene los controladores del evento InteractedWithEvent de cada botón para permitir darse de baja del evento una vez resuelto el puzle.
var ButtonSubscriptions : []cancelable = array{}
OnBegin<override>()<suspends> : void =
SetupPuzzleLights()
<#
Para cada botón y su índice, si tiene valores válidos de LightsIndices,
crea un nuevo button_event_handler con los índices que utiliza y una referencia a este tagged_light_puzzle (Self).
Utiliza la función OnButtonPressed del controlador para suscribirte al evento InteractedWithEvent del botón.
Guarda estas suscripciones en la matriz ButtonSubscriptions para cancelar la suscripción más adelante, de modo que el jugador no pueda interactuar con el puzle una vez resuelto.
#>
set ButtonSubscriptions = for:
ButtonIndex -> Button : Buttons
LightIndices := ButtonsToLights[ButtonIndex]
do:
Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
SetupPuzzleLights() : void =
# Ten en cuenta que no se garantiza ningún orden específico de los dispositivos cuando se recuperan mediante etiquetas de juego.
TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})
<#
Para cada actor con la etiqueta puzzle_light, comprueba si es un dispositivo customizable_light_device intentando hacerle casting a ese tipo.
Si lo es, obtiene su LightState inicial para encender [TurnOn()] o apagar [TurnOff()] LightDevice.
Guarda todos los dispositivos customizable_light_device etiquetados en la matriz Lights.
#>
set Lights = for:
ActorIndex -> TaggedActor : TaggedActors
LightDevice := customizable_light_device[TaggedActor]
ShouldLightBeOn := LightsState[ActorIndex]
do:
Logger.Print("Añadiendo luz en el índice {ActorIndex} con estado:{if (ShouldLightBeOn?) then "On" else "Off"}")
if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff()
LightDevice # La última sentencia de una expresión es su valor resultante. Las expresiones `for` devuelven todos los valores de una matriz.
ToggleLights(LightIndices : []int) : void =
<#
Para cada Index, obtén el LightState correspondiente y la referencia al dispositivo Light.
Si ambos son válidos, determina IsLightOn a partir del LightState actual (si encendido->apagado; si apagado->encendido) y
establece el correspondiente LighsState[Index] en IsLightOn.
#>
for:
LightIndex : LightIndices
IsLightOn := LightsState[LightIndex]
Light := Lights[LightIndex]
do:
<#
Para negar un valor booleano en Verse: if (MyLogic?) {false} else {true}
Aquí también aplicamos TurnOff() o TurnOn() a Light antes de devolver el valor negado.
#>
Logger.Print("Encendiendo luz en el índice {LightIndex} {if (IsLightOn?) then "Apagado" else "Encendido"}")
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
if (set LightsState[LightIndex] = NewLightState): # Matriz indexing may error, so it must be wrapped in a contexto de fallo.
Logger.Print("Se ha actualizado el estado de la luz en {LightIndex}")
# Puede que LightsState haya cambiado, así que comprueba si el puzle está resuelto.
if (IsPuzzleSolved[]):
Logger.Print("¡Puzle resuelto!")
ItemSpawner.Enable()
# Anula/cancela todos los controladores de eventos para que el jugador ya no pueda alternar las luces.
para (ButtonSubscription : ButtonSubscriptions):
ButtonSubscription.Cancel()
<#
Una función con el <decides> efecto solo puede utilizarse en un contexto de fallo como `if (IsPuzzleSolved[])`.
Fíjate en la llamada de función con [] en lugar de () para las llamadas de función falibles.
#>
IsPuzzleSolved()<decides><transacts> : void =
<#
Itera cada LightsState y comprueba si coincide con su SolvedLightsState.
La primera vez que falla el bloque interno `for`, falla la función y
el fallo llega hasta el autor de la llamada.
En caso contrario, la función tiene éxito.
#>
for:
LightIndex -> IsLightOn : LightsState
IsLightOnInSolution := SolvedLightsState[LightIndex]
do:
IsLightOn = IsLightOnInSolution
Por tu cuenta
En este tutorial te hemos enseñado a crear un puzle reutilizable en Verse que requiere que el jugador encuentre la combinación correcta de luces alternando su estado con botones.
Con lo que has aprendido, intenta lo siguiente:
- Crea más etiquetas y utilízalas para controlar las luces con un orden visual determinista.
- Utiliza diferentes condiciones iniciales y soluciones y añade más botones y luces para crear más puzles con la misma configuración.
- Controla varios tipos de dispositivos con otros distintos con los que se pueda interactuar.