Przed przystąpieniem do pisania samego kodu łamigłówki z otagowanymi światłami dobrze jest przemyśleć, jaki sposób będzie najlepszy do osiągnięcia zamierzonego rezultatu. W tej sekcji przedstawiono sposób podejścia do tworzenia mechaniki łamigłówki. Na końcu tego kroku będziesz dysponować pseudokodem reprezentującym algorytm będący podstawą do utworzenia tej łamigłówki. W kolejnym kroku zobaczysz, jak zaimplementować ten algorytm w Verse i UEFN.
Identyfikacja celów, wymagań i ograniczeń
Pierwszym krokiem jest zidentyfikowanie celów, wymagań i ograniczeń. Wymagania często definiuje się przez podział szerszych celów na mniejsze części.
Cele |
|
Wymagania |
|
Ograniczenia |
|
Podział problemu
Teraz wiesz już, jaki rezultat chcesz uzyskać i nad czym pracujesz. Podziel więc problem na mniejsze problemy, które łatwiej będzie przeanalizować. Przy podziale większego problemu na części składowe pomaga zadawanie pytań:
- W jaki sposób gracz może wejść w interakcję z łamigłówką?
- Jak wykorzystać tagi rozgrywki do znalezienia świateł?
- Jak zdefiniować stany wstępne i rozwiązania, które można modyfikować w edytorze?
- Jak uzgodnić stan gry zapisany w strukturze Verse z elementami wizualnymi w grze?
- W jaki sposób interakcja gracza może aktualizować określony zestaw świateł?
- Jak wyłączyć możliwość interakcji gracza po rozwiązaniu łamigłówki?
Następnie zidentyfikuj potencjalne zależności między tymi mniejszymi problemami. W tym przypadku wydaje się, że problemy są od siebie niezależne, jednak warto rozważyć następujące kwestie:
- Pytania 1, 5 i 6 są luźno połączone.
- W przypadku pytań 1 i 6 sposób interakcji gracza z łamigłówką nie może określać sposobu wyłączenia możliwości interakcji po rozwiązaniu łamigłówki.
- W przypadku pytań 1 i 5 jedna interakcja powoduje równoczesne przełączenie wielu świateł. Dla struktury danych będzie to informacja o konieczności zastosowania mapowania interakcji do świateł.
- Pytanie 2 jest istotne pod względem projektowym. Sposób działania interfejsu API tagów rozgrywki może wpływać na sposób kontrolowania świateł w kodzie. Ma to konsekwencje dla pytań 4 i 5, ponieważ konieczna będzie zmiana stanu świateł w grze, dlatego należy znaleźć wspólny sposób na wykonanie tej czynności.
- Pytania 3 i 4 powinny prawdopodobnie prowadzić do jednego rozwiązania podstawowej struktury danych dla stanu początkowego, bieżącego oraz po rozwiązaniu.
Opracowanie potencjalnych rozwiązań
Po podzieleniu problemu na mniejsze problemy skoncentruj się na udzieleniu odpowiedzi na pytania dotyczące tych mniejszych problemów:
1. W jaki sposób gracz może wejść w interakcję z łamigłówką?
Tę kwestię można rozwiązać na wiele sposobów. Zasadniczo można wykorzystać dowolne urządzenie, z którym gracz może wejść w interakcję i które Verse może wykorzystać do wykrycia interakcji. Zestaw narzędzi trybu kreatywnego zawiera wiele urządzeń spełniających te wymagania, takich jak urządzenia aktywatora, Urządzenia przycisku, a także pola zmieniające kolor czy aktywatory percepcyjne.
W tym przykładzie wykorzystano urządzenie przycisku oraz powiązane z nim zdarzenie InteractedWithEvent
, które jest wysyłane po każdej interakcji gracza z przyciskiem, dopóki przycisk ten jest włączony. Aby dowiedzieć się więcej, patrz Kodowanie interakcji z urządzeniem.
2. Jak wykorzystać tagi rozgrywki do znalezienia świateł?
Wykorzystując tagi rozgrywki, można pobierać grupy aktorów z przypisanym tagiem niestandardowym zdefiniowanym w kodzie Verse.
Za pomocą funkcji GetCreativeObjectsWithTag()
można pobrać dowolną tablicę wszystkich aktorów, do których przypisano niestandardowy tag. Wynikiem funkcji jest tablica wszystkich obiektów z implementacją creative_object_interface
. customizable_light_device
jest w Verse reprezentacją urządzenia konfigurowalnego światła oraz klasą zawierającą implementację creative_object_interface
.
Kolejność na liście urządzeń zwracanej przez funkcję GetCreativeObjectsWithTag()
nie jest gwarantowana, a wywołanie funkcji w celu pobrania wszystkich urządzeń może zająć sporo czasu, zwłaszcza jeśli na danym poziomie jest ich wiele, dlatego dobrym rozwiązaniem jest zapisanie świateł, aby zapewnić do nich szybki dostęp na późniejszych etapach. Taka czynność nazywana jest zapisywaniem w pamięci podręcznej i często pozwala zwiększyć wydajność. Światła są zbiorem elementów tego samego typu, dlatego do zapisania ich razem można użyć tablicy.
To oznacza, że możesz wykonać następującą procedurę:
- Utwórz nowy tag o nazwie
puzzle_light
. - Oznacz wszystkie światła do łamigłówki tagiem
puzzle_light
. - Wywołaj funkcję
GetCreativeObjectsWithTag(puzzle_light)
, aby pobrać wszystkich aktorów oznaczonych tagiempuzzle_light
. - Ustal, które z wyników wywołania funkcji są obiektem
customizable_light_device
. - Zapisz listę obiektów
customizable_light_device
w tablicy, aby móc skorzystać z nich później.
3. Jak zdefiniować stany wstępne i rozwiązania, które można modyfikować w edytorze?
Światło ma jedynie dwa stany: włączone lub wyłączone. Za pomocą typu logic
w Verse możesz utworzyć reprezentacje stanu włączenia i wyłączenia światła, ponieważ typ logic
przyjmuje tylko jedną z dwóch wartości: true
lub false
. Świateł jest wiele, dlatego w tym przypadku także można użyć tablicy do zapisania wszystkich wartości logic
, a następnie uzgodnić pozycję (lub indeks) w tablicy dla stanu światła z indeksem światła, z którym jest skojarzona.
Za pomocą tablicy wartości logic
można zdefiniować stan początkowy świateł łamigłówki, a także zawrzeć bieżący stan świateł w trakcie gry. Tablicę tę można uwidocznić w edytorze za pomocą atrybutu @editable
. Następnie na początku gry można włączać lub wyłączać światła, aby dostosować je wizualnie do stanu zapisanego w tablicy.
Rozwiązanie łamigłówki powinno być zgodne z typem używanym do przechowywania bieżącego stanu świateł, aby można było sprawdzić, czy łamigłówka została rozwiązana, przez porównanie tych dwóch wartości. To oznacza, że będziesz mieć dwie edytowalne tablice logic
, z których jedna będzie reprezentować stan bieżący świateł, a druga rozwiązanie łamigłówki. To umożliwia zmianę stanu początkowego świateł łamigłówki oraz jej rozwiązania z poziomu edytora, co pozwala na wielokrotne wykorzystanie łamigłówki z zastosowaniem różnych konfiguracji.
4. Jak uzgodnić stan gry zapisany w strukturze Verse z elementami wizualnymi w grze?
Obiekt customizable_light_device
można włączać i wyłączać w trakcie gry za pomocą funkcji TurnOn()
i TurnOff()
. Zatem przy każdej aktualizacji bieżącego stanu świateł reprezentowanego przez tablicę wartości logicznych trzeba również wywołać funkcje TurnOn()
i TurnOff()
, aby uzgodnić efekty wizualne w trakcie gry z jej stanem.
5. W jaki sposób interakcja gracza może aktualizować określony zestaw świateł?
Przy pierwszym pytaniu ustaliliśmy już, że gracz będzie wchodził w interakcje z łamigłówką za pomocą urządzenia przycisku. Możesz zasubskrybować dla procedury obsługi zdarzeń InteractedWithEvent
przycisku, które spowoduje zmianę świateł, gdy gracz wejdzie w interakcję z urządzeniem przycisku. Dostępnych jest wiele przycisków, z których gracz może skorzystać, dlatego ponownie można je tutaj zebrać razem za pomocą tablicy.
Następnie musisz określić sposób mapowania każdego zdarzenia przycisku z osobna do zbioru świateł, których przełączanie ma powodować zdarzenie.
Kolejność świateł w tablicy customizable_light_device będzie taka sama, jak kolejność w tablicy wartości logicznych reprezentującej stan świateł, dlatego można utworzyć mapowanie między przyciskiem, a indeksami świateł, na które przycisk ten będzie wpływał. Takie mapowanie można odzwierciedlić w tablicy, w której kolejność elementów jest zgodna z kolejnością przycisków, a elementy są tablicami indeksów.
Tablicę można skonfigurować jako edytowalną, aby móc zmieniać mapowania przycisków na światła w edytorze, a tym samym stosować łamigłówkę wielokrotnie bez konieczności zmiany samego kodu.
6. Jak wyłączyć możliwość interakcji gracza po rozwiązaniu łamigłówki?
Wiesz już, w jaki sposób gracz wchodzi w interakcję z łamigłówką za pomocą urządzenia przycisku, a interakcja ta jest wykrywana za pomocą zdarzenia InteractedWithEvent
.
Jak zatem sprawić, aby po rozwiązaniu łamigłówki jej urządzenie przestało odbierać dane wejściowe od gracza, aby nie mógł on więcej zmieniać łamigłówki?
Można to zrobić na co najmniej trzy sposoby:
- Wyłączając przyciski w grze po rozwiązaniu łamigłówki.
- Dodając pole
logic
do obiektutagged_lights_puzzle
modyfikowanego, gdy łamigłówka zostanie rozwiązana. Po każdym zaktualizowaniu stanu gry takie polelogic
musi być najpierw sprawdzane w celu upewnienia się, czy łamigłówka została już rozwiązana. - Anulując subskrypcję zdarzenia
InteractedWithEvent
przycisków po rozwiązaniu łamigłówki, aby procedury obsługi zdarzeń nie były więcej wywoływane.
Trzecia opcja jest najlepsza, ponieważ jest prostym i efektywnym rozwiązaniem. Nie trzeba tworzyć nowych pól, aby sprawdzić wykonanie kodu warunkowego. Koncepcję anulowania subskrypcji zdarzenia urządzenia można wykorzystać także w innych sytuacjach. Zasadniczo do dobrych praktyk należy subskrybowanie zdarzenia, gdy chce się otrzymywać powiadomienia na jego temat, a następnie anulowanie subskrypcji, gdy powiadomienia przestają być potrzebne. Szczegóły implementacji anulowania subskrypcji objaśniono w dalszej części tego samouczka.
Połączenie rozwiązań i zaplanowanie gry z użyciem pseudokodu
Masz już rozwiązania mniejszych problemów. Teraz połącz je ze sobą w celu rozwiązania pierwotnego problemu. Nadaj algorytmowi formalny kształt, aby utworzyć build rozwiązania za pomocą pseudokodu.
Co się stanie, gdy rozpocznie się gra? Światła są skonfigurowane. Subskrybujesz zdarzenie InteractedWithEvent
przycisków, znajdujesz wszystkie urządzenia oznaczone tagiem puzzle_light
i zapisujesz je w pamięci podręcznej. Ponadto włączasz i wyłączasz światła w trakcie gry w oparciu o wartość początkową LightState.
OnBegin:
Result of GetCreativeObjectsWithTag(puzzle_light) is stored in the variable FoundDevices
for each Device in FoundDevices:
if Device is a Customizable Light Device:
Store the Light
if ShouldLightBeOn?:
Turn on Light
else:
Turn off Light
for each Button:
Subscribe to the Button InteractedWithEvent using the handler OnButtonInteractedWith
Pseudokodowa wersja funkcji OnButtonInteractedWith
może wyglądać tak, jak w poniższym przykładzie, gdzie InteractedButtonIndex
oznacza indeks tablicy button_device
odpowiadający przyciskowi, z którym gracz wszedł w interakcję. W dalszej części tego samouczka dowiesz się, jak odebrać te informacje w procedurze obsługi zdarzeń.
OnButtonInteractedWith:
Get lights associated with the button interacted with using the ButtonsToLights array and store in the variable Lights
# Przełącz światła
for each Light in Lights:
if IsLightOn?:
Set the Light game state to off
Turn off Light
else:
Set the Light game state to on
Turn on Light
if IsPuzzleSolved():
Enable Item Spawner
for each Button:
Unsubscribe from the Button InteractedWithEvent
Pseudokod funkcji IsPuzzleSolved
sprawdza, czy bieżący stan świateł jest zgodny z rozwiązaniem. Jeśli bieżący stan nie jest zgodny z rozwiązaniem, sprawdzanie kończy się niepowodzeniem, a blok if IsPuzzleSolved
z powyższego pseudokodu nie jest uruchamiany. Jeśli bieżący stan jest zgodny z rozwiązaniem, wówczas sprawdzanie kończy się powodzeniem i następuje uruchomienie bloku if IsPuzzleSolved
.
IsPuzzleSolved:
for each Light:
if IsLightOn is not equal to IsLightOnInSolution
fail and return
succeed
Masz już opracowany swój algorytm!
Następny krok
W kolejnym kroku tego samouczka przełożysz ten algorytm na język programowania Verse i przeprowadzisz test gry swojego projektu, aby zobaczyć poszczególne etapy w akcji.