In diesem letzten Schritt des Tagged Lights Puzzle Tutorials findest du das vollständige Script für das Rätsel und Ideen, um das Beispiel weiter zu verändern.
Vollständiges Script
Der folgende Code ist das komplette Script für ein wiederverwendbares Rätsel, bei dem der Spieler die richtige Kombination an Lichtern finden muss, indem er deren Zustand mit Schaltflächen umschaltet.
using { /Fortnite.com/Devices }
using { /Verse.org/Native }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation/Tags }
using { /Verse.org/Simulation }
# Leite die `tag`-Klasse im Verse.org/Simulation/Tags-Modul ab, um ein neues Gameplay-Tag zu erstellen.
puzzle_light := class(tag){}
log_tagged_lights_puzzle := class(log_channel){}
<#
Du kannst eine Instanz dieser Klasse verwenden, um die
InteractedWithEvent eines button_device zu abonnieren und hast Zugriff auf die Event-bezogenen Eigenschaften (in diesem Fall Indizes und PuzzleDevice) in deinem Event-Handler.
Du kannst dir das so vorstellen, dass jede Schaltfläche ihren eigenen Status und Event-Handler hat.
Da die Klasse nicht über den <concrete>-Spezifikator verfügt, ist die Angabe von Standardwerten für Felder nicht erforderlich.
#>
button_event_handler := class():
# Positionen, die zum Zugreifen auf die Lichter dienen, die von dieser Schaltfläche gesteuert werden.
Indices : []int
# tagged_lights_puzzle, das diesen button_event_handler erstellt hat, damit du Funktionen darauf aufrufen kannst
PuzzleDevice : tagged_lights_puzzle
OnButtonPressed(InPlayer : agent) : void =
# Teile dem PuzzleDevice mit, dass es die Lichter an den Positionen ein-/ausschalten soll, welche von dieser Schaltfläche gesteuert werden.
PuzzleDevice.ToggleLights(Indices)
tagged_lights_puzzle := class<concrete>(creative_device):
Logger : log = log{Channel := log_tagged_lights_puzzle}
# Schaltflächen, mit denen der Spieler mit dem Rätsel interagiert.
@editable
Buttons : []button_device = array{}
<#
Die Anzahl der Elemente in ButtonsToLights sollte mit der Anzahl der Elemente in Buttons übereinstimmen.
Jedes Array von Indizes mit Null-Index beschreibt, welche Lichter die Schaltfläche unter Buttons[x] schaltet (wobei 'x' der Tastenindex ist).
Zum Beispiel schaltet Buttons[2] das erste und zweite Licht um, wie durch array{0,1} angegeben.
#>
ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
<#
Das Logik-Matrix LightsState wird verwendet, um den Ein-Aus-Zustand aller Lichter zu verfolgen.
Er wird auch verwendet, um den Anfangszustand der Lichter festzulegen.
Die Anzahl der Elemente muss also mit der Anzahl der Lichter übereinstimmen, die mit dem Tag „puzzle_light“ gekennzeichnet sind.
#>
@editable
var LightsState : []logic = array{false, false, false, false}
<#
Das Rätsel ist gelöst, wenn LightsState mit SolvedLightsState übereinstimmt.
Dies bedeutet, dass SolvedLightsState genauso viele Elemente haben sollte wie LightsState.
#>
@editable
SolvedLightsState : []logic = array{true, true, true, true}
@editable
# Dieser ItemSpawner wird aktiviert, wenn das Rätsel gelöst ist.
ItemSpawner : item_spawner_device = item_spawner_device{}
# Enthält die über Gameplay-Tags gefundenen Lichter.
var Lights : []customizable_light_device = array{}
# Enthält die Handler für das InteractedWithEvent jeder Schaltfläche, um das Abmelden vom Event zu ermöglichen, sobald das Rätsel gelöst ist.
var ButtonSubscriptions : []cancelable = array{}
OnBegin<override>()<suspends> : void =
SetupPuzzleLights()
<#
Erstelle für jede Schaltfläche und ihren Index, wenn sie gültige LightsIndices-Werte hat,
einen neuen button_event_handler mit den von ihm verwendeten Indizes und einem Verweis auf dieses tagged_light_puzzle (Self).
Über die Funktion OnButtonPressed des Handlers kannst du das InteractedWithEvent der Schaltfläche abonnieren.
Speichere diese Abonnements im ButtonSubscriptions-Array, um das Abonnement später zu löschen, damit der Spieler nicht mit dem Rätsel interagieren kann, wenn es einmal gelöst ist.
#>
set ButtonSubscriptions = for:
ButtonIndex -> Schaltfläche : Schaltflächen
LightIndices := ButtonsToLights[ButtonIndex]
do:
Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
SetupPuzzleLights() : void =
# Denke daran, dass den Geräten keine bestimmte Reihenfolge garantiert wird, wenn sie über Gameplay-Tags abgerufen werden.
TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})
<#
Überprüfe für jeden Actor mit dem puzzle_light-Tag, ob er ein customizable_light_device ist, indem du versuchst, ihn auf diesen Typ zu casten.
Ist dies der Fall, rufe seinen Anfangs-LightState ab, um TurnOn() oder TurnOff() auf das LightDevice anzuwenden.
Speichere alle gekennzeichneten customizable_light_device im Lights-Array.
#>
set Lights = for:
ActorIndex -> TaggedActor : TaggedActors
LightDevice := customizable_light_device[TaggedActor]
ShouldLightBeOn := LightsState[ActorIndex]
do:
Logger.Print("Adding Light at index {ActorIndex} with State:{if (ShouldLightBeOn?) then "On" else "Off"}")
if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff()
LightDevice # Die letzte Anweisung eines Ausdrucks ist sein Ergebniswert. `For` Ausdrücke geben alle Werte in einem Array zurück.
ToggleLights(LightIndices : []int) : void =
<#
Rufe für jeden Index den entsprechenden LightState und den Verweis auf das Lichtgerät ab.
Wenn beide gültig sind, bestimme IsLightOn aus dem aktuellen LightState (if on->off ; if off->on) und
setze den entsprechenden LightState[Index] auf IsLightOn.
#>
for:
LightIndex : LightIndices
IsLightOn := LightsState[LightIndex]
Light := Lights[LightIndex]
do:
<#
Um einen booleschen Wert in Verse zu negieren: if (MyLogic?) {false} else {true}
Hier verwenden wir auch TurnOff() oder TurnOn(), bevor wir den negierten Wert zurückgeben.
#>
Logger.Print("Turning light at index {LightIndex} {if (IsLightOn?) then "Off" else "On"}")
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
if (set LightsState[LightIndex] = NewLightState): # Die Array-Indizierung kann fehlschlagen, daher muss sie in einen Fehlerkontext eingeschlossen werden.
Logger.Print("Zustand von Licht bei {LightIndex} aktualisiert")
# LightsState könnte sich geändert haben, also überprüfe, ob das Rätsel gelöst ist.
if (IsPuzzleSolved[]):
Logger.Print("Rätsel gelöst!")
ItemSpawner.Enable()
# Melde alle Event-Handler ab, damit der Spieler keine Lichter mehr umschalten kann.
für (ButtonSubscription : ButtonSubscriptions):
ButtonSubscription.Cancel()
<#
Eine Funktion mit dem <decides>-Effekt kann nur in einem Fehlerkontext wie `if (IsPuzzleSolved[])` verwendet werden.
Beachte den Funktionsaufruf mit [] anstatt mit () für fehlbare Funktionsaufrufe.
#>
IsPuzzleSolved()<decides><transacts> : void =
<#
Iteriere jeden LightsState und prüfe, ob er mit seinem SolvedLightsState übereinstimmt.
Wenn der innere Block `for` zum ersten Mal fehlschlägt, schlägt die Funktion fehl und
der Fehler wird dem Caller angezeigt.
Andernfalls ist die Funktion erfolgreich.
#>
for:
LightIndex -> IsLightOn : LightsState
IsLightOnInSolution := SolvedLightsState[LightIndex]
do:
IsLightOn = IsLightOnInSolution
Auf eigene Faust
Nach Abschluss dieses Tutorials hast du gelernt, wie man mit Verse ein wiederverwendbares Puzzle erstellt, bei dem der Spieler die richtige Kombination von Lichtern finden muss, indem er ihren Zustand mit Schaltflächen umschaltet.
Probiere mit dem, was du gelernt hast, Folgendes aus:
- Erstelle weitere Tags und verwende sie, um die Lichter mit einer deterministischen visuellen Reihenfolge zu steuern.
- Verwende verschiedene Ausgangsbedingungen und Lösungen und füge weitere Knöpfe und Lichter hinzu, um mehr Rätsel mit demselben Aufbau zu erstellen.
- Steuere mehrere Gerätetypen mit mehreren Typen von Geräten, mit denen der Benutzer interagieren kann.