W tym ostatnim kroku samouczka na temat łamigłówki z otagowanymi światłami znajdziesz kompletny skrypt łamigłówki, a także pomysły na dalszą modyfikację przykładu.
Kompletny skrypt
Poniższy kod przedstawia kompletny skrypt łamigłówki wielokrotnego użycia, która wymaga od gracza znalezienia właściwej kombinacji świateł przez przełączanie ich stanu za pomocą przycisków.
using { /Fortnite.com/Devices }
using { /Verse.org/Native }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation/Tags }
using { /Verse.org/Simulation }
# Wyprowadź z klasy `tag` w module Verse.org/Simulation/Tags, aby utworzyć nowy tag rozgrywki.
puzzle_light := class(tag){}
log_tagged_lights_puzzle := class(log_channel){}
<#
Instancję tej klasy możesz wykorzystać do zasubskrybowania zdarzenia
InteractedWithEvent urządzenia button_device i uzyskania dostępu do właściwości poszczególnych zdarzeń (w tym przypadku Indices i PuzzleDevice) w swojej procedurze obsługi zdarzeń.
Potraktuj to jak przydzielenie każdemu przyciskowi własnego stanu oraz procedury obsługi zdarzeń.
Klasa nie zawiera specyfikatora <concrete>, dlatego definiowanie wartości domyślnych pól nie jest wymagane.
#>
button_event_handler := class():
# Pozycje używane w celu uzyskania dostępu do świateł kontrolowanych tym przyciskiem.
Indices : []int
# Urządzenie tagged_lights_puzzle, które utworzyło tę procedurę obsługi zdarzeń button_event_handler, aby można było wywoływać w nim funkcje.
PuzzleDevice : tagged_lights_puzzle
OnButtonPressed(InPlayer : agent) : void =
# Przekaż do PuzzleDevice instrukcję przełączenia świateł w pozycjach kontrolowanych tym przyciskiem.
PuzzleDevice.ToggleLights(Indices)
tagged_lights_puzzle := class<concrete>(creative_device):
Logger : log = log{Channel := log_tagged_lights_puzzle}
# Przyciski używane przez gracza do interakcji z łamigłówką.
@editable
Buttons : []button_device = array{}
<#
Liczba elementów tablicy ButtonsToLights powinna odpowiadać liczbie elementów tablicy Buttons.
Każda tablica indeksów zaindeksowana pod numerem zerowym opisuje, które światła przełącza przycisk w pozycji Buttons[x] (gdzie 'x' oznacza indeks przycisku).
Na przykład Buttons[2] przełącza pierwsze i drugie światło zgodnie z tablicą array{0,1}.
#>
ButtonsToLights : [][]int = array{array{0, 3}, array{0, 1, 2}, array{0, 1}, array{1}}
<#
Tablica wartości logicznych LightsState służy do monitorowania stanów włączenia i wyłączenia wszystkich świateł.
Posłuży ona również do ustalenia stanu początkowego świateł, tak aby liczba zawartych w nich elementów
była zgodna z liczbą świateł oznaczonych tagiem puzzle_light.
#>
@editable
var LightsState : []logic = array{false, false, false, false}
<#
Łamigłówka jest rozwiązana, gdy stan LightsState odpowiada stanowi SolvedLightsState.
Oznacza to, że tablica SolvedLightsState powinna zawierać tyle samo elementów, co tablica LightsState.
#>
@editable
SolvedLightsState : []logic = array{true, true, true, true}
@editable
# To urządzenie ItemSpawner zostaje włączone po rozwiązaniu łamigłówki.
ItemSpawner : item_spawner_device = item_spawner_device{}
# Zawiera światła wyszukane za pośrednictwem tagów rozgrywki.
var Lights : []customizable_light_device = array{}
# Zawiera procedury obsługi InteractedWithEvent każdego przycisku, aby umożliwić anulowanie subskrypcji zdarzenia po rozwiązaniu łamigłówki.
var ButtonSubscriptions : []cancelable = array{}
OnBegin<override>()<suspends> : void =
SetupPuzzleLights()
<#
Dla każdego przycisku i jego indeksu, jeśli zawiera prawidłowe wartości LightsIndices,
utwórz nowy obiekt button_event_handler zawierający właściwość Indices z używanymi indeksami oraz odwołanie do tej klasy tagged_light_puzzle (Self).
Użyj funkcji OnButtonPressed procedury obsługi w subskrypcji zdarzenia InteractedWithEvent przycisku.
Zapisz te subskrypcje w tablicy ButtonSubscriptions, aby anulować subskrypcję później i uniemożliwić graczowi interakcję z łamigłówką po jej rozwiązaniu.
#>
set ButtonSubscriptions = for:
ButtonIndex -> Button : Buttons
LightIndices := ButtonsToLights[ButtonIndex]
do:
Button.InteractedWithEvent.Subscribe(button_event_handler{Indices := LightIndices, PuzzleDevice := Self}.OnButtonPressed)
SetupPuzzleLights() : void =
# Pamiętaj, że przy pobieraniu za pomocą tagów rozgrywki konkretna kolejność urządzeń nie jest gwarantowana.
TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})
<#
Dla każdego aktora oznaczonego tagiem puzzle_light, sprawdź, czy jest on obiektem typu customizable_light_device, wykonując próbę rzutowania go do tego typu.
Jeśli tak, pobierz jego stan LightState, aby włączyć za pomocą funkcji TurnOn() lub wyłączyć za pomocą funkcji TurnOff() urządzenie LightDevice.
Zapisz wszystkie obiekty customizable_light_device oznaczone tagiem w tablicy świateł Lights.
#>
set Lights = for:
ActorIndex -> TaggedActor : TaggedActors
LightDevice := customizable_light_device[TaggedActor]
ShouldLightBeOn := LightsState[ActorIndex]
do:
Logger.Print("Dodawanie światła pod indeksem {ActorIndex} ze stanem:{if (ShouldLightBeOn?) then "On" else "Off"}")
if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff()
LightDevice # Ostatnia instrukcja wyrażenia jest jego wartością wynikową. Wyrażenia `for` zwracają wszystkie wartości w tablicy.
ToggleLights(LightIndices : []int) : void =
<#
Dla każdego indeksu pobierz odpowiedni stan światła LightState oraz odwołanie do urządzenia światła.
Jeśli obydwie wartości są prawidłowe, ustal wartość logiczną IsLightOn na podstawie bieżącego stanu LightState (jeśli włączone, zostanie wyłączone, a jeśli wyłączone, zostanie włączone)
i ustaw odpowiedni stan LightState[Index] na IsLightOn.
#>
for:
LightIndex : LightIndices
IsLightOn := LightsState[LightIndex]
Light := Lights[LightIndex]
do:
<#
Negowanie wartości logicznej w Verse: if (MyLogic?) {false} else {true}
W tym przykładzie wykonujemy również funkcję wyłączenia TurnOff() lub włączenia TurnOn() światła przed zwróceniem zanegowanej wartości.
#>
Logger.Print("Światło pod indeksem {LightIndex} {if (IsLightOn?) then "– wyłączanie" else "– włączanie"}")
NewLightState :=
if (IsLightOn?):
Light.TurnOff()
false
else:
Light.TurnOn()
true
if (set LightsState[LightIndex] = NewLightState): # Indeksowanie tablicy może zakończyć się niepowodzeniem, dlatego należy je osadzić w kontekście niepowodzenia.
Logger.Print("Zaktualizowano stan światła pod indeksem {LightIndex}")
# Stan LightsState mógł ulec zmianie, więc sprawdź, czy łamigłówka została rozwiązana.
if (IsPuzzleSolved[]):
Logger.Print("Łamigłówka została rozwiązana!")
ItemSpawner.Enable()
# Cofnij subskrypcję wszystkich procedur obsługi zdarzeń lub je anuluj, aby gracz nie mógł więcej przełączać świateł.
for (ButtonSubscription : ButtonSubscriptions):
ButtonSubscription.Cancel()
<#
Funkcji z efektem <decides> można używać tylko w kontekście niepowodzenia, takim jak `if (IsPuzzleSolved[])`.
Zwróć uwagę, że w przypadku wywołań funkcji zawodnych stosujemy nawias kwadratowy [] zamiast zwykłego ().
#>
IsPuzzleSolved()<decides><transacts> : void =
<#
Wykonaj iterację po każdej tablicy LightsState i sprawdź, czy zawarte w niej elementy są zgodne z elementami w tablicy SolvedLightsState.
Pierwsze niepowodzenie składowego bloku wyrażenia `for` oznacza niepowodzenie funkcji,
które jest eskalowane do wywoływacza.
W innym przypadku funkcja się powiedzie.
#>
for:
LightIndex -> IsLightOn : LightsState
IsLightOnInSolution := SolvedLightsState[LightIndex]
do:
IsLightOn = IsLightOnInSolution
We własnym zakresie
Kończąc ten samouczek, wiesz już, jak utworzyć łamigłówkę wielokrotnego użycia, która wymaga od gracza znalezienia właściwej kombinacji świateł przez przełączanie ich stanu za pomocą przycisków.
Wykorzystując zdobytą wiedzę, spróbuj wykonać następujące czynności:
- Utwórz więcej tagów i wykorzystaj je do sterowania światłami w ściśle określonym porządku wizualnym.
- Wykorzystaj różne warunki początkowe i rozwiązania, aby dodać więcej przycisków i świateł, tworząc więcej łamigłówek z wykorzystaniem podobnej konfiguracji.
- Steruj wieloma typami urządzeń za pomocą wielu rodzajów urządzeń, z którymi użytkownik może wchodzić w interakcje.