En este último paso del tutorial puzzle de luces combinadas, encontrarás la secuencia de comandos completa del puzzle e ideas para cambiar el ejemplo
Secuencia de comandos completa
El siguiente código es la secuencia de comandos completa de un puzzle reutilizable que requiere que un jugador encuentre la combinación correcta de luces conmutando sus estados 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 juego.
puzzle_light := class(tag){}
log_tagged_lights_puzzle := class(log_channel){}
<#
Puedes usar una instancia de esta clase para suscribir al
InteractedWithEvent de un button_device y tener acceso a propiedades por evento (en este caso, Indices y PuzzleDevice) en el controlador de eventos.
Imagínalo como cada botón con su estado personal y controlador de eventos.
Como la clase no tiene el especificador <concrete>, no es necesario especificar valores predeterminados para el campo.
#>
button_event_handler := class():
# Posiciones que se usan para acceder a las luces que controla este botón.
Indices : []int
# tagged_lights_puzzle que crearon este button_event_handler para poder llamar funciones a él.
PuzzleDevice : tagged_lights_puzzle
OnButtonPressed(InPlayer : agent) : void =
# Decir a PuzzleDevice que conmute las luces en las posiciones que controla este botón.
PuzzleDevice.ToggleLights(Indices)
tagged_lights_puzzle := class<concrete>(creative_device):
Logger : log = log{Channel := log_tagged_lights_puzzle}
# Botones que usa el jugador para interactuar con el puzzle
@editable
Buttons : []button_device = array{}
<#
El número de elementos de ButtonsToLights debe coincidir con el número de elementos en Buttons.
Cada matriz de indices de indice cero describe cuáles luces conmuta el botón en Buttons[x] (donde X es el índice del botón).
Por ejemplo, Buttons[2] conmuta la primera y segunda luz, según se especifica mediante array{0,1}.
#>
ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
<#
La matriz lógica LightsState se usa para hacer el seguimiento del estado encendido/apagado de todas las luces.
También se usa para definir 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 tag.
#>
@editable
var LightsState : []logic = array{false, false, false, false}
<#
El puzzle se resuelve 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 habilita cuando se resuelve el puzzle.
ItemSpawner : item_spawner_device = item_spawner_device{}
# Contiene las luces encontradas mediante las etiquetas de juego.
var Lights : []customizable_light_device = array{}
# Contiene los controladores de InteractedWithEvent de cada botón para poder desuscribir del evento cuando se resuelva el puzzle.
var ButtonSubscriptions : []cancelable = array{}
OnBegin<override>()<suspends> : void =
SetupPuzzleLights()
<#
Para cada botón y su índice, si tiene valores LightsIndices válidos,
crea un nuevo button_event_handler con los Indices que usa y una referencia a este tagged_light_puzzle (Self).
Usa la función OnButtonPressed del controlador para suscribir a InteractedWithEvent del botón.
Guarda estas suscripciones en la matriz ButtonSubscriptions para cancelar la suscripción más adelante para que el jugador no pueda interactuar con el puzzle después de resolverlo.
#>
set ButtonSubscriptions = for:
ButtonIndex -> Button : Buttons
LightIndices := ButtonsToLights[ButtonIndex]
do:
Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
SetupPuzzleLights() : void =
# Ten presente que no se garantiza un orden específico para los dispositivos cuando se recuperan mediante las etiquetas de juego.
TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})
<#
Para cada actor con la etiqueta puzzle_light, comprueba si es un customizable_light_device al intentar proyectarlo a dicho tipo.
Si lo es, obtiene su LightState incial para TurnOn() (encender) o TurnOff() (apagar) el 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 índice {ActorIndex} con State:{if (ShouldLightBeOn?) entonces "Encender" else "Apagar"}")
LightDevice.TurnOn() else LightDevice.TurnOff()
LightDevice # La última declaración de una expresión es su valor de resultado. Las expresiones `for` devuelven todos los valores en una matriz.
ToggleLights(LightIndices : []int) : void =
<#
Para cada índice, obtiene el LightState correspondiente y referencia al dispositivo Light.
Si ambos son válidos, determina IsLightOn a partir del LightState actual (si está encendida->apagada ; si está apagada->encendida) y
define LighsState[Index] correspondiente a 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 apagamos o encendemos (TurnOff() o TurnOn()) la luz antes de volver al valor negado.
#>
Logger.Print("Cambiando estado de luz en {LightIndex} {if (IsLightOn?) entonces "Apagar" else "Encender"}")
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
if (set LightsState[LightIndex] = NewLightState): # La indexación de arrays puede fallar, por lo que hay que envolverla en un contexto de fallo.
Logger.Print("Se actualizó el estado de la luz en {LightIndex}")
# LightsState puede haber cambiado por lo que comprueba si se ha resuelto el puzzle.
if (IsPuzzleSolved[]):
Logger.Print("¡Puzzle resuelto!")
ItemSpawner.Enable()
# Desuscribir/cancelar todos los controladores de eventos para que el jugador ya no pueda conmutar las luces.
for (ButtonSubscription : ButtonSubscriptions):
ButtonSubscription.Cancel()
<#
Una función con el efecto <decides> solo puede usarse en un contexto de fracaso como `if (IsPuzzleSolved[])`.
Observa la llamada a función con [] en lugar de () para llamadas a funciones falibles.
#>
IsPuzzleSolved()<decides><transacts> : void =
<#
Itera cada LightsState y comprueba si coincide con SolvedLightsState.
La primera vez que el bloque interno `for` fracasa, la función fracasa y
el fracaso escapa al llamador.
De lo contrario, la función tiene éxito.
#>
for:
LightIndex -> IsLightOn : LightsState
IsLightOnInSolution := SolvedLightsState[LightIndex]
do:
IsLightOn = IsLightOnInSolution
Por tu cuenta
Al completar este tutorial, has aprendido cómo crear un puzzle reutilizable con Verse que requiere que el jugador encuentre la combinación correcta de luces al conmutar su estado con botones.
Con lo que has aprendido, intenta lo siguiente:
- Crear más combinaciones y usarlas para controlar la luces con un orden visual establecido.
- Usar distintas condiciones iniciales y soluciones y añadir más botones y luces para crear más puzzles con la misma configuración.
- Controlar múltiples tipos de dispositivo con múltiples tipos de dispositivo interactivos con los que puede jugar el usuario.