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 grze Próba czasu: Pogoń za pizzą istnieją dwa rodzaje stref: strefy odbioru, które wykorzystują urządzenie generatora przedmiotów, oraz strefy dostarczania, które korzystają z urządzenia strefy punktowanej. 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 Visual Studio Code.
W pliku Verse utwórz nową klasę o nazwie
base_zonezawierającą specyfikatorpublici dodaj:Stała
creative_object_interfaceo nazwieActivatorDeviceze specyfikatorempublicdo przechowywania urządzenia używanego w strefie.Zdarzenie o nazwie
ZoneCompletedEventzawierają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 zwrotnejvoidoraz specyfikatorempublic, aby włączyć urządzenie używane w strefie.Funkcja o nazwie
DeactivateZone()z typem informacji zwrotnejvoidoraz specyfikatorempublic, aby wyłączyć urządzenie używane w strefie.Versebase_zone<public> := class: ActivatorDevice<public> : creative_object_interface ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){} ActivateZone<public>() : void = Print("Zone activated") DeactivateZone<public>() : void = Print("Zone deactivated")Ustawienie dla klasy i jej elementów członkowskich specyfikatora
publicoznacza, że są one powszechnie dostępne z poziomu innego kodu. Aby uzyskać więcej informacji, patrz: Specyfikatory i atrybuty.
W funkcji
ActivateZone()sprawdź, czy urządzenieActivatorDevicejest strefą punktowaną czy generatorem przedmiotów, rzutując typActivatorDevicena 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().Versebase_zone<public> := class: ActivatorDevice<public> : creative_object_interface ActivateZone<public>() : void = Print("Zone activated") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Enable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Enable()Utwórz funkcję o nazwie
WaitForZoneCompleted()zawierającą specyfikatorprivateoraz 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 zdarzenieZoneCompletedEventi nie musi uwzględniać rodzaju zdarzenia wykorzystywanego przez podstawowe urządzenie.VerseWaitForZoneCompleted<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żeniespawnwywołujące funkcjęWaitForZoneCompleted()z uwzględnieniem zdarzenia urządzenia odpowiedniego dla gracza wchodzącego w interakcję z urządzeniem:AgentEntersEventw przypadku strefy punktowanej iItemPickedUpEventw przypadku generatora przedmiotów.WaitForZoneCompletedoczekuje 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.ItemPickedUpEventspełniają ten warunek, dlatego można ich użyć jako parametru. Oto kolejny przykład abstrakcji.VerseActivateZone<public>() : void = Print("Zone activated") 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
ZoneDeactivatedEventzawierają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().VerseZoneDeactivatedEvent<protected> : event() = event(){} DeactivateZone<public>() : void = Print("Zone deactivated") if (CaptureArea := capture_area_device[ActivatorDevice]): CaptureArea.Disable() else if (ItemSpawner := item_spawner_device[ActivatorDevice]): ItemSpawner.Disable() ZoneDeactivatedEvent.Signal()Zaktualizuj
WaitForZoneCompleted()o wyrażenierace. Dzięki temu funkcja będzie czekać, aż gracz ukończy strefę lub strefa zostanie zdezaktywowana. Po zastosowaniu wyrażeniaracewywołanie funkcji asynchronicznejZoneDeactivatedEvent.Await()oraz wyrażenieblockze zdarzeniem urządzenia i sygnałemZoneCompletedEventsą uruchamiane równocześnie, jednak wyrażenie, które nie zostanie zakończone jako pierwsze, będzie anulowane.VerseWaitForZoneCompleted<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.VerseMakeBaseZone<constructor><public>(InActivatorDevice : creative_object_interface) := base_zone: ActivatorDevice := InActivatorDevicePoniżej przedstawiono kompletny kod klasy
base_zone.Verse# A zone is an area of the map (represented by a device) that can be Activated/Deactivated and that provides events to signal when the zone has been "Completed" (can't be completed anymore until next activation). # Zone "Completed" depends on the device type (ActivatorDevice) for the zone. # Suggested usage: 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()
Wyszukiwanie stref w czasie wykonywania programu 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_selectorw 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ą specyfikatorpublicoraz parametrtag, aby wyszukać wszystkie strefy powiązane z tym tagiem rozgrywki i zapisać je w pamięci podręcznej.VerseInitZones<public>(ZoneTag : tag) : void = # On creation of a zone selector, find all available zones and cache them so we don't consume time searching for tagged devices every time the next zone is selected. ZoneDevices := GetCreativeObjectsWithTag(ZoneTag) set Zones = for (ZoneDevice : ZoneDevices): MakeBaseZone(ZoneDevice)Dodaj metodę o nazwie
SelectNext()zawierającą specyfikatorydecidesitransacts, 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).VerseSelectNext<public>()<transacts><decides> : base_zone = Zones[GetRandomInt(0, Zones.Length - 1)]Kompletny kod pliku pickup_delivery_zone.verse powinien wyglądać następująco:
Verseusing { /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 } <# A zone is an area of the map (represented by a device) that can be Activated/Deactivated and that provides events to signal when the zone has been "Completed" (can't be completed anymore until next activation). Zone "Completed" depends on the device type (ActivatorDevice) for the zone.
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_devicestałą 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_selectordla 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 liczbytagów pickup_zone_tagwPickupZoneLevelTags.Taka konfiguracja pozwala zwiększyć liczbę poziomów odbioru przy minimalnych zmianach w kodzie: wystarczy zaktualizować
PickupZoneLevelTagso dodatkowe poziomy wyprowadzone z tagupickup_zone_tag, a następnie oznaczyć urządzenia tagami w edytorze.Versegame_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
privateoraz typ informacji zwrotnejvoid.Zainicjuj wybieracz strefy dostarczania za pomocą tagu
delivery_zone_tag.Utwórz tagi poziomu strefy odbioru i zainicjuj wybieracze strefy odbioru.
VerseOnBegin<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{}
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.VerseOnBegin<override>()<suspends> : void = SetupZones() var PickupLevel : int = 0 loop: if (PickupZone : base_zone = PickupZoneSelectors[PickupLevel].SelectNext[]): PickupZone.ActivateZone() PickupZone.ZoneCompletedEvent.Await() PickupZone.DeactivateZone()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.