Strefa jest obszarem mapy (reprezentowanym przez urządzenie), gdzie gracz może odbierać lub dostarczać przedmioty. Wykonując ten krok w samouczku Próba czasu: Pogoń za pizzą (Time Trial: Pizza Pursuit), nauczysz się tworzyć takie strefy odbioru i dostarczania, a także aktywować/dezaktywować je dla gracza.
Tworzenie klasy stref za pomocą abstrakcji
Abstrakcja to zasada programowania, zgodnie z którą niepotrzebne szczegóły są ukrywane przed użytkownikiem, który nie musi rozumieć ukrytych zawiłości. Abstrakcja opisuje, czym coś jest, bez konieczności zaznajamiania się ze sposobem działania takiej rzeczy. Możesz na przykład wrzucić pieniądze do automatu vendingowego i wyciągnąć z niego przekąskę, nie rozumiejąc, jak działa jego mechanizm.
W Próba czasu: Pogoń za pizzą (Time Trial: Pizza Pursuit) występują dwa rodzaje stref: strefy odbioru, w których używany jest generator przedmiotów, oraz strefy dostarczania wykorzystujące strefę punktowaną. Strefy te będą zachowywały się w taki sam sposób pomimo zastosowania różnych urządzeń – co oznacza, że będzie można je aktywować i dezaktywować – dlatego możesz utworzyć klasę, aby wyabstrahować to zachowanie do ogólnego obiektu strefy, który będzie obsługiwał interakcje z konkretnymi urządzeniami.
Wyabstrahowanie tego zachowania do klasy oznacza stworzenie jednego miejsca, w którym będzie można zmienić rodzaj używanych urządzeń. Taka implementacja oznacza również możliwość zmiany specyfiki klasy bez konieczności modyfikowania innego kodu, ponieważ każdy kod wykorzystujący tę klasę będzie znał wyłącznie funkcje aktywacji/dezaktywacji.
Aby utworzyć klasę stref, wykonaj następujące instrukcje:
- Utwórz nowy pusty plik Verse o nazwie pickup_delivery_zone.verse i otwórz go w narzędziu Visual Studio Code.
- W pliku Verse utwórz nową klasę o nazwie
base_zone
zawierającą specyfikatorpublic
i dodaj następujące elementy:- Stała
creative_object_interface
o nazwieActivatorDevice
zawierająca specyfikatorpublic
do przechowywania urządzenia użytego w strefie. - Zdarzenie o nazwie
ZoneCompletedEvent
zawierające specyfikatorpublic
, aby zasygnalizować, kiedy gracz wejdzie w interakcję z tą strefą, na przykład przez odebranie lub dostarczenie przedmiotów. - Funkcja o nazwie
ActivateZone()
z typem informacji zwrotnejvoid
oraz specyfikatorempublic
, aby włączyć urządzenie używane w strefie. - Funkcja o nazwie
DeactivateZone()
z typem informacji zwrotnejvoid
oraz specyfikatorempublic
, aby wyłączyć urządzenie używane w strefie.
base_zone<public> := class: ActivatorDevice<public> : creative_object_interface ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){} ActivateZone<public>() : void = Print("Strefa aktywowana") DeactivateZone<public>() : void = Print("Strefa dezaktywowana")
Ustawienie dla klasy i jej elementów członkowskich specyfikatora
public
oznacza, że są one powszechnie dostępne z poziomu innego kodu. Aby dowiedzieć się więcej na ten temat, patrz Specyfikatory i atrybuty. - Stała
- W funkcji
ActivateZone()
sprawdź, czy urządzenieActivatorDevice
jest strefą punktowaną czy generatorem przedmiotów, rzutując typActivatorDevice
na inne typy i wywołując funkcjęEnable()
na konwertowanym urządzeniu. Wykonaj tę samą operację w odwołaniu do funkcjiDeactivateZone()
, pomijając jednak wywołanie funkcjiDisable()
.base_zone<public> := class: ActivatorDevice<public> : creative_object_interface ActivateZone<public>() : void = Print("Strefa aktywowana") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable() DeactivateZone<public>() : void = Print("Strefa dezaktywowana") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Disable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Disable()
- Utwórz funkcję o nazwie
WaitForZoneCompleted()
zawierającą specyfikatorprivate
oraz specyfikatorsuspends
. Funkcja ta będzie sygnalizować zdarzenieZoneCompletedEvent
, gdy wystąpi zdarzenie specyficzne dla urządzenia. Taka konfiguracja oznacza, że inny kod musi po prostu poczekać na zdarzenieZoneCompletedEvent
i nie musi uwzględniać rodzaju zdarzenia wykorzystywanego przez podstawowe urządzenie.WaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void = if (DeviceEvent := ZoneDeviceCompletionEventOpt?): DeviceEvent.Await() ZoneCompletedEvent.Signal(Self)
Funkcja ta musi zawierać efekt
suspends
, aby móc wywołać funkcjęDeviceEvent.Await()
. - Zaktualizuj funkcję
ActivateZone()
o wyrażeniespawn
wywołujące funkcjęWaitForZoneCompleted()
z uwzględnieniem zdarzenia urządzenia odpowiedniego dla gracza wchodzącego w interakcję z urządzeniem:AgentEntersEvent
w przypadku strefy punktowanej iItemPickedUpEvent
w przypadku generatora przedmiotów.WaitForZoneCompleted
oczekuje parametruoption
(wskazywanego symbolem?
) typuawaitable(agent)
, można więc przekazać dowolny typ, który implementuje interfejsawaitable
, a jego typ parametryczny jest równy typowiagent
. Zarówno zdarzenieCaptureArea.AgentEntersEvent
, jak i zdarzenieItemSpawner.ItemPickedUpEvent
spełniają ten warunek, dlatego można ich użyć jako parametru. Oto kolejny przykład abstrakcji.ActivateZone<public>() : void = Print("Strefa aktywowana") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() spawn { WaitForZoneCompleted(option{CaptureArea.AgentEntersEvent})} else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable() spawn { WaitForZoneCompleted(option{ItemSpawner.ItemPickedUpEvent}) }
- Dodaj kolejne zdarzenie o nazwie
ZoneDeactivatedEvent
zawierające specyfikatorprotected
. Zdarzenie to jest konieczne do zakończenia funkcjiWaitForZoneCompleted()
, jeśli strefa zostanie dezaktywowana, zanim gracz ją ukończy. Zasygnalizuj to zdarzenie w funkcjiDeactivateZone()
.ZoneDeactivatedEvent<protected> : event() = event(){} DeactivateZone<public>() : void = Print("Strefa dezaktywowana") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Disable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Disable() ZoneDeactivatedEvent.Signal()
- Zaktualizuj funkcję
WaitForZoneCompleted()
o wyrażenierace
. Dzięki temu funkcja będzie czekać, aż gracz ukończy strefę lub strefa zostanie zdezaktywowana. Po zastosowaniu wyrażeniarace
wywołanie funkcji asynchronicznejZoneDeactivatedEvent.Await()
oraz wyrażenieblock
ze zdarzeniem urządzenia i sygnałemZoneCompletedEvent
są uruchamiane równocześnie, jednak wyrażenie, które nie zostanie zakończone jako pierwsze, będzie anulowane.WaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void = if (DeviceEvent := ZoneDeviceCompletionEventOpt?): race: block: DeviceEvent.Await() ZoneCompletedEvent.Signal(Self) ZoneDeactivatedEvent.Await()
- Na końcu utwórz konstruktor dla klasy
base_zone
, który będzie inicjował poleActivatorDevice
.MakeBaseZone<constructor><public>(InActivatorDevice : creative_object_interface) := base_zone: ActivatorDevice := InActivatorDevice
- Poniżej przedstawiono kompletny kod klasy
base_zone
.<# Strefa jest obszarem mapy (reprezentowanym przez urządzenie), który można aktywować/dezaktywować oraz który jest źródłem zdarzeń sygnalizowanych, gdy strefa zostanie "ukończona" (nie można ukończyć jej po raz kolejny do czasu ponownej aktywacji). "Ukończona" strefa zależy od typu urządzenia (ActivatorDevice) użytego w tej strefie. Sugerowane użycie: ActivateZone() -> ZoneCompletedEvent.Await() -> DeactivateZone() #> base_zone<public> := class: ActivatorDevice<public> : creative_object_interface ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){} GetTransform<public>() : transform = ActivatorDevice.GetTransform() <# Aktywuje strefę. W tym miejscu włącz urządzenia i wszelkie wskaźniki wizualne strefy. #> ActivateZone<public>() : void = # Strefa bazowa może obsługiwać strefy zdefiniowane jako generatory przedmiotów lub strefy punktowane. # Wypróbuj każdy typ i wykonaj do niego rzutowanie, aby sprawdzić, z czym mamy do czynienia. if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() spawn { WaitForZoneCompleted(option{CaptureArea.AgentEntersEvent}) } else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable() spawn { WaitForZoneCompleted(option{ItemSpawner.ItemPickedUpEvent}) } <# Dezaktywuje strefę. W tym miejscu wyłącz urządzenia i wszelkie wskaźniki wizualne strefy. #> DeactivateZone<public>() : void = if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Disable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Disable() ZoneDeactivatedEvent.Signal() <# Zdarzenie to jest konieczne do zakończenia koprocedury WaitForZoneCompleted, jeśli strefa zostanie dezaktywowana bez ukończenia. #> ZoneDeactivatedEvent<protected> : event() = event(){} WaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void = if (DeviceEvent := ZoneDeviceCompletionEventOpt?): race: block: DeviceEvent.Await() ZoneCompletedEvent.Signal(Self) ZoneDeactivatedEvent.Await() MakeBaseZone<constructor><public>(InActivatorDevice : creative_object_interface) := base_zone: ActivatorDevice := InActivatorDevice
Wyszukiwanie stref w środowisku uruchomieniowym za pomocą tagów rozgrywki
Znasz już sposób tworzenia oraz aktywowania/dezaktywowania stref. Poznajmy więc sposób zainicjowania wszystkich stref w poziomie oznaczonych tagami oraz wybierania kolejnej strefy do aktywowania.
Ten przykład ilustruje, jak należy to zrobić, posługując się klasą odpowiedzialną za tworzenie stref oraz wybieranie kolejnej strefy do aktywowania.
Aby utworzyć klasę do tworzenia i wyboru stref, wykonaj następujące instrukcje:
- Utwórz nową klasę o nazwie
tagged_zone_selector
w pliku pickup_delivery_zone.verse. Dodaj tablicę zmiennych, w której zostaną zapisane wszystkie strefy poziomu.tagged_zone_selector<public> := class: var Zones<protected> : []base_zone = array{}
- Dodaj metodę o nazwie
InitZones()
zawierającą specyfikatorpublic
oraz parametrtag
, aby wyszukać wszystkie strefy powiązane z tym tagiem rozgrywki i zapisać je w pamięci podręcznej.InitZones<public>(ZoneTag : tag) : void = <# Przy tworzeniu wybieracza stref znajdź wszystkie dostępne strefy i zapisz je w pamięci podręcznej, abyśmy nie tracili czasu na wyszukiwanie otagowanych urządzeń za każdym razem, gdy wybierana jest kolejna strefa. #> ZoneDevices := GetCreativeObjectsWithTag(ZoneTag) set Zones = for (ZoneDevice : ZoneDevices): MakeBaseZone(ZoneDevice)
- Dodaj metodę o nazwie
SelectNext()
zawierającą specyfikatorydecides
itransacts
, aby metoda albo znalazła kolejną strefę, albo zawiodła. Wybierz strefę pod losowym indeksem w tablicy, stosując dla indeksu zapisGetRandomInt(0, Zones.Length - 1)
.SelectNext<public>()<transacts><decides> : base_zone = Zones[GetRandomInt(0, Zones.Length - 1)]
- Kompletny kod pliku pickup_delivery_zone.verse powinien wyglądać następująco:
using { /Verse.org/Simulation } using { /Verse.org/Random } using { /Verse.org/Concurrency } using { /Verse.org/Simulation/Tags } using { /UnrealEngine.com/Temporary/SpatialMath } using { /Fortnite.com/Devices } <# Strefa jest obszarem mapy (reprezentowanym przez urządzenie), który można aktywować/dezaktywować oraz który jest źródłem zdarzeń sygnalizowanych, gdy strefa zostanie "ukończona" (nie można ukończyć jej po raz kolejny do czasu ponownej aktywacji). "Ukończona" strefa zależy od typu urządzenia (ActivatorDevice) użytego w tej strefie. Sugerowane użycie: ActivateZone() -> ZoneCompletedEvent.Await() -> DeactivateZone() #> base_zone<public> := class: ActivatorDevice<public> : creative_object_interface ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){} GetTransform<public>() : transform = ActivatorDevice.GetTransform() <# Aktywuje strefę. W tym miejscu włącz urządzenia i wszelkie wskaźniki wizualne strefy. #> ActivateZone<public>() : void = # Strefa bazowa może obsługiwać strefy zdefiniowane jako generatory przedmiotów lub strefy punktowane. # Wypróbuj każdy typ i wykonaj do niego rzutowanie, aby sprawdzić, z czym mamy do czynienia. if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() spawn { WaitForZoneCompleted(option{CaptureArea.AgentEntersEvent}) } else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable() spawn { WaitForZoneCompleted(option{ItemSpawner.ItemPickedUpEvent}) } <# Dezaktywuje strefę. W tym miejscu wyłącz urządzenia i wszelkie wskaźniki wizualne strefy. #> DeactivateZone<public>() : void = if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Disable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Disable() ZoneDeactivatedEvent.Signal() <# Zdarzenie to jest konieczne do zakończenia koprocedury WaitForZoneCompleted, jeśli strefa zostanie dezaktywowana bez ukończenia. #> ZoneDeactivatedEvent<protected> : event() = event(){} WaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void = if (DeviceEvent := ZoneDeviceCompletionEventOpt?): race: block: DeviceEvent.Await() ZoneCompletedEvent.Signal(Self) ZoneDeactivatedEvent.Await() MakeBaseZone<constructor><public>(InActivatorDevice : creative_object_interface) := base_zone: ActivatorDevice := InActivatorDevice # tagged_zone_selector tworzy strefy w oparciu o aktywatory oznaczone tagiem przekazanym do InitZones. tagged_zone_selector<public> := class: var Zones<protected> : []base_zone = array{} InitZones<public>(ZoneTag : tag) : void = <# Przy tworzeniu wybieracza stref znajdź wszystkie dostępne strefy i zapisz je w pamięci podręcznej, abyśmy nie tracili czasu na wyszukiwanie otagowanych urządzeń za każdym razem, gdy wybierana jest kolejna strefa. #> ZoneDevices := GetCreativeObjectsWithTag(ZoneTag) set Zones = for (ZoneDevice : ZoneDevices): MakeBaseZone(ZoneDevice) SelectNext<public>()<transacts><decides> : base_zone = Zones[GetRandomInt(0, Zones.Length - 1)]
Testowanie stref odbioru i dostarczania
Masz już utworzone dwie klasy. Teraz dobrym pomysłem będzie przetestowanie kodu i upewnienie się, że mechanizm wyboru stref działa w oczekiwany sposób.
Aby zaktualizować swój plik game_coordinator_device.verse, wykonaj następujące instrukcje:
- Dodaj do urządzenia
game_coordinator_device
stałą dla wybieracza strefy dostarczania oraz tablicę zmiennych dla selektorów strefy odbioru. Gra będzie później podwyższać poziom odbioru po każdym odebraniu pizzy, dlatego potrzebny będzie jedentagged_zone_selector
dla każdego poziomu odbioru, jaki chcesz uzyskać w grze, stąd tablicaPickupZoneSelectors
. W każdym wybieraczu strefy znajdują się strefy odbioru konkretnego poziomu. Musi być zmienną, ponieważ jego konfiguracja zależy od liczby tagówpickup_zone_tag
wPickupZoneLevelTags
. - Taka konfiguracja pozwala zwiększyć liczbę poziomów odbioru przy minimalnych zmianach w kodzie: wystarczy zaktualizować
PickupZoneLevelTags
o dodatkowe poziomy wyprowadzone z tagupickup_zone_tag
, a następnie oznaczyć urządzenia tagami w edytorze.game_coordinator_device<public> := class<concrete>(creative_device): DeliveryZoneSelector<private> : tagged_zone_selector = tagged_zone_selector{} var PickupZoneSelectors<private> : []tagged_zone_selector = array{}
- Dodaj metodę o nazwie
SetupZones()
i wywołaj ją w metodzieOnBegin()
:- Ustaw dla metody specyfikator
private
oraz typ informacji zwrotnejvoid
. - Zainicjuj wybieracz strefy dostarczania za pomocą tagu
delivery_zone_tag
. - Utwórz tagi poziomu strefy odbioru i zainicjuj wybieracze strefy odbioru.
OnBegin<override>()<suspends> : void = SetupZones() SetupZones<private>() : void = DeliveryZoneSelector.InitZones(delivery_zone_tag{}) PickupZoneLevelTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} set PickupZoneSelectors = for(PickupZoneTag : PickupZoneLevelTags): PickupZone := tagged_zone_selector{} PickupZone.InitZones(PickupZoneTag) PickupZone
- Ustaw dla metody specyfikator
- Utwórz w metodzie
OnBegin()
pętlę, która będzie wybierać kolejną strefę odbioru, aktywować ją i czekać, aż gracz ukończy strefę, po czym ją dezaktywuje.OnBegin<override>()<suspends> : void = SetupZones() var PickupLevel : int = 0 loop: if (PickupZone : base_zone = PickupZoneSelectors[PickupLevel].SelectNext[]): PickupZone.ActivateZone() PickupZone.ZoneCompletedEvent.Await() PickupZone.DeactivateZone() else: Print("Nie znaleziono następnej strefy PickupZone do wybrania") return if (DeliveryZone := DeliveryZoneSelector.SelectNext[]): DeliveryZone.ActivateZone() DeliveryZone.ZoneCompletedEvent.Await() DeliveryZone.DeactivateZone() else: Print("Nie znaleziono następnej strefy DeliveryZone do wybrania") return
- Zapisz swoje pliki Verse, skompiluj kod i przeprowadź test gry na swoim poziomie.
W trakcie przeprowadzania testu gry na twoim poziomie jeden z generatorów przedmiotów zostanie aktywowany na początku gry. Po odebraniu przedmiotu generator przedmiotów zostanie dezaktywowany i nastąpi aktywacja strefy punktowanej. Cykl ten będzie kontynuowany, dopóki ręcznie nie zakończysz gry.