In quest'ultimo passaggio del tutorial Puzzle delle luci con tag, troverai lo script completo per il puzzle e le idee per modificare ulteriormente l'esempio.
Script completo
Il codice seguente è lo script completo per un puzzle riutilizzabile, che richiede al giocatore di trovare la giusta combinazione di luci, alternandone lo stato con i pulsanti.
using { /Fortnite.com/Devices }
using { /Verse.org/Native }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation/Tags }
using { /Verse.org/Simulation }
# Deriva dalla classe `tag` del modulo Verse.org/Simulation/Tags per creare un nuovo tag di gameplay.
puzzle_light := class(tag){}
log_tagged_lights_puzzle := class(log_channel){}
<#
È possibile utilizzare un'istanza di questa classe per sottoscrivere a
InteractedWithEvent di un button_device e avere accesso alle proprietà per evento (in questo caso, Indices e PuzzleDevice) nel tuo gestore eventi.
Considera ogni pulsante come dotato del suo stato e il suo gestore eventi personale.
Poiché la classe non include lo specificatore <concrete>, non è necessario specificare i valori predefiniti per i campi.
#>
button_event_handler := class():
# Posizioni utilizzate per accedere alle luci controllate da questo pulsante.
Indices : []int
# tagged_lights_puzzle che ha creato questo button_event_handler, in modo da poter chiamare le funzioni su di esso.
PuzzleDevice : tagged_lights_puzzle
OnButtonPressed(InPlayer : agent) : void =
# Indica al PuzzleDevice di attivare/disattivare le luci nelle posizioni controllate da questo pulsante.
PuzzleDevice.ToggleLights(Indices)
tagged_lights_puzzle := class<concrete>(creative_device):
Logger : log = log{Channel := log_tagged_lights_puzzle}
# Pulsanti che il giocatore utilizza per interagire con il puzzle.
@editable
Buttons : []button_device = array{}
<#
Il numero di elementi in ButtonsToLights deve corrispondere al numero di elementi in Buttons.
Ciascun array di indici con indice zero descrive quali luci vengono attivate/disattivate dal pulsante Buttons[x], dove 'x' è l'indice del pulsante.
Ad esempio, Buttons[2] attiva/disattiva la prima e la seconda luce, come specificato dall'array{0,1}.
#>
ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
<#
L'array logico LightsState viene utilizzato per tenere traccia dello stato di accensione e spegnimento di tutte le luci.
Viene utilizzato anche per impostare lo stato iniziale delle luci, quindi il suo numero di elementi
deve corrispondere al numero di luci con il tag puzzle_light.
#>
@editable
var LightsState : []logic = array{false, false, false, false}
<#
Il puzzle è risolto quando LightsState corrisponde a SolvedLightsState.
Ciò significa che SolvedLightsState deve avere lo stesso numero di elementi di LightsState.
#>
@editable
SolvedLightsState : []logic = array{true, true, true, true}
@editable
# Questo ItemSpawner viene abilitato quando il puzzle viene risolto.
ItemSpawner:item_spawner_device = item_spawner_device{}
# Mantiene le luci trovate tramite i tag gameplay.
var Lights : []customizable_light_device = array{}
# Mantiene i gestori di InteractedWithEvent di ciascun pulsante, per consentire l'annullamento della sottoscrizione dall'evento, una volta risolto il puzzle.
var ButtonSubscriptions : []cancelable = array{}
OnBegin<override>()<suspends> : void =
SetupPuzzleLights()
<#
Per ogni pulsante e il suo indice, se ha valori validi di LightsIndices,
crea un nuovo button_event_handler con gli Indices che utilizza e un riferimento a questo tagged_light_puzzle (Self).
Utilizza la funzione OnButtonPressed del gestore per sottoscrivere all'InteractedWithEvent del Pulsante.
Salva queste iscrizioni nell'array ButtonSubscriptions per annullarle in seguito, in modo che il giocatore non possa interagire con il puzzle, una volta risolto.
#>
set ButtonSubscriptions = for:
ButtonIndex -> Button : Buttons
LightIndices := ButtonsToLights[ButtonIndex]
do:
Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
SetupPuzzleLights() : void =
# Tieni presente che i dispositivi non sono garantiti in un ordine specifico, quando vengono recuperati tramite i tag di gameplay.
TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})
<#
Per ogni attore con il tag puzzle_light, verifica se si tratta di un dispositivo customizable_light_device, provando a effettuarne il cast in quel tipo.
Se lo è, ottieni il suo LightState iniziale per eseguire TurnOn() o TurnOff() del LightDevice.
Salva tutti i customizable_light_device con tag nell'array Lights.
#>
set Lights = for:
ActorIndex -> TaggedActor : TaggedActors
LightDevice := customizable_light_device[TaggedActor]
ShouldLightBeOn := LightsState[ActorIndex]
do:
Logger.Print("Aggiunta delle luci all'indice {ActorIndex} con stato:{if (ShouldLightBeOn?) then "On" else "Off"}")
if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff()
LightDevice # L'ultima istruzione di un'espressione è il suo valore risultato. Le espressioni `for` restituiscono tutti i valori di un array.
ToggleLights(LightIndices : []int) : void =
<#
Per ogni Index, ottieni il corrispondente LightState e il riferimento al dispositivo Light.
Se sono entrambi validi, determina IsLightOn dal LightState corrente (if on->off ; if off->on) e
imposta il corrispondente LighsState[Index] su IsLightOn.
#>
for:
LightIndex : LightIndices
IsLightOn := LightsState[LightIndex]
Light := Lights[LightIndex]
do:
<#
Per negare un valore booleano in Verse: if (MyLogic?) {false} else {true}
In questo caso, eseguiamo anche TurnOff() o TurnOn() di Light prima di restituire il valore negativo.
#>
Logger.Print("Impostazione delle luci all'indice {LightIndex} {if (IsLightOn?) then "Off" else "On"}")
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
falso
else:
Light.TurnOn()
vero
if (set LightsState[LightIndex] = NewLightState): # L'indicizzazione dell'array potrebbe non riuscire, quindi ne deve essere eseguito il wrapping in un contesto di fallimento.
Logger.Print("Stato per le luci a {LightIndex} aggiornato")
# LightsState potrebbe essere cambiato, quindi controlla se il puzzle è stato risolto.
if (IsPuzzleSolved[]):
Logger.Print("Puzzle risolto!")
ItemSpawner.Enable()
# Annulla la sottoscrizione/annulla tutti i gestori eventi in modo che il giocatore non possa più attivare/disattivare le luci.
for (ButtonSubscription : ButtonSubscriptions):
ButtonSubscription.Cancel()
<#
Una funzione con l'effetto <decides> può essere utilizzata solo in un contesto di fallimento come `if (IsPuzzleSolved[])`.
Nota che la chiamata di funzione presenta [] invece di () per le chiamate di funzione che possono fallire.
#>
IsPuzzleSolved()<decides><transacts> : void =
<#
Esegui l'iterazione di ogni LightsState e verifica se corrisponde al suo SolvedLightsState.
La prima volta che il blocco interno `for` non riesce, la funzione fallisce e
l'errore si ripercuote sul chiamante.
Altrimenti, la funzione riesce.
#>
for:
LightIndex -> IsLightOn : LightsState
IsLightOnInSolution := SolvedLightsState[LightIndex]
do:
IsLightOn = IsLightOnInSolution
In autonomia
Al termine di questo tutorial, hai imparato a creare un puzzle riutilizzabile utilizzando Verse, che richiede al giocatore di trovare la giusta combinazione di luci, alternandone lo stato con i pulsanti.
Utilizzando quanto appreso, prova a fare quanto segue:
- Crea più tag e utilizzali per controllare le luci con un ordine visivo deterministico.
- Utilizza condizioni iniziali e soluzioni diverse e aggiungi altri pulsanti e luci per creare più puzzle con la stessa configurazione.
- Controlla più tipi di dispositivi con più tipi di dispositivi utilizzabili dall'utente.