Wykonując ten krok w samouczku Próba czasu: Pogoń za pizzą (Time Trial: Pizza Pursuit), nauczysz się zarządzać wynikiem, gdy gracz odbiera i dostarcza przedmioty, a także aktualizować UI w celu wyświetlenia wyników. Aby dowiedzieć się więcej o tworzeniu UI w grze w Verse, patrz Tworzenie UI w grze.
Menedżer wyników będzie śledzić i wyświetlać następujące wartości:
- Łączna liczba punktów (Total Points): Oznacza całkowitą liczbę punktów zdobytych przez gracza w grze.
- Liczba punktów oczekujących (Pending Points): Oznacza punkty, jakie gracz zgromadził za bieżący zbiór odebranych przedmiotów.
- Poziom odbioru (Pickup Level): Oznacza bieżący poziom odbioru.

Tworzenie UI
Aby utworzyć UI menedżera wyników w Verse, wykonaj następujące instrukcje:
- Utwórz nowy pusty plik Verse i nadaj mu nazwę score_manager.verse.
- Utwórz nową klasę o nazwie
score_manager
i dodaj do niej następujące pola:- Opcjonalny element
agent
o nazwieMaybePlayer
do przechowywania odwołania do gracza.MaybePlayer<internal> : ?agent = false
- Opcjonalny element
player_ui
o nazwieMaybePlayerUI
do przechowywania odwołania do UI gracza.MaybePlayerUI<internal> : ?player_ui = false
- Element
score_manager_device
do przechowywania odwołania dla urządzenia menedżera wyniku, wokół którego utworzono tę klasę. (Pamiętaj, że nie jest to odwołanie edytowalne, ponieważ musi być połączone z urządzeniemgame_coordinator_device
utworzonym w Verse.ScoreManagerDevice<internal> : score_manager_device = score_manager_device{}
- Zmienna z liczbą całkowitą o nazwie
TotalGameScore
reprezentująca wszystkie punkty, jakie gracz zdobył w trakcie całej gry.var TotalGameScore<private> : int = 0
- Zmienna z liczbą całkowitą o nazwie
PendingScore
reprezentująca punkty, jakie gracz ma w tej chwili zgromadzone za dany zestaw odebranych przedmiotów.var PendingScore<private> : int = 0
- Zmienna z liczbą całkowitą o nazwie
PickupLevel
reprezentująca bieżący poziom odbioru.var PickupLevel<private> : int = 0
- Opcjonalny element
- Utworzona definicja klasy score_manager powinna wyglądać następująco:
score_manager := class: MaybePlayer<internal> : ?agent = false MaybePlayerUI<internal> : ?player_ui = false ScoreManagerDevice<internal> : score_manager_device = score_manager_device{} var TotalGameScore<private> : int = 0 var PendingScore<private> : int = 0 var PickupLevel<private> : int = 0
- Utwórz UI przy pierwszym utworzeniu klasy. Można to zrobić, dodając wyrażenie
block
do definicji klasy, które będzie wykonywane przy każdym utworzeniu instancji klasy. Dodaj następujące zmienne w celu wygenerowania UI:- Zmienna
canvas
o nazwieCanvas
zawierająca specyfikatorinternal
do przechowywania niestandardowego widżetu kanwy.var Canvas<internal> : canvas = canvas{}
- Zmienna
text_block
o nazwieTotalGameScoreWidget
zawierająca specyfikatorinternal
do przechowywania widżetu tekstu w celu wyświetlenia wszystkich punktów zdobytych przez gracza w trakcie całej gry reprezentowanych przez zmiennąTotalGameScore
. Ustaw domyślny kolor tekstu bloku tekstu na biały.TotalGameScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White}
- Zmienna
text_block
o nazwiePendingScoreWidget
zawierająca specyfikatorinternal
do przechowywania widżetu tekstu w celu wyświetlenia punktów, jakie gracz ma obecnie zgromadzone za dany zbiór odebranych przedmiotów, reprezentowanych przez zmiennąPendingScore
. Ustaw domyślny kolor tekstu bloku tekstu na biały.PendingScoreWidget<internal> : text_block = text_block{}
- Zmienna
text_block
o nazwiePickupLevelWidget
zawierająca specyfikatorinternal
do przechowywania widżetu tekstu w celu wyświetlenia bieżącego poziomu odbioru reprezentowanego przez zmiennąPickupLevel
. Ustaw domyślny kolor tekstu bloku tekstu na biały.PickupLevelWidget<internal> : text_block = text_block{}
- Funkcja zwracająca wynik
message
o nazwieTotalGameScoreText
tworząca możliwy do zlokalizowania tekst, który może być wyświetlany w UI dla ogólnej liczby punktów zdobytych przez gracza w grze.TotalGameScoreText<localizes>(CurrentTotalGameScore : int) : message = "Łączna liczba punktów: {CurrentTotalGameScore}"
- Funkcja zwracająca wynik
message
o nazwiePendingScoreText
, która tworzy możliwy do zlokalizowania tekst, który może być wyświetlany w UI dla liczby punktów, jakie gracz ma obecnie zgromadzone za dany zbiór odebranych przedmiotów.PendingScoreText<localizes>(CurrentPendingScore : int) : message = "Punkty oczekujące: {CurrentPendingScore}"
- Funkcja zwracająca wynik
message
o nazwiePickupLevelText
, która tworzy możliwy do zlokalizowania tekst, który może być wyświetlany w UI dla bieżącego poziomu odbioru.PickupLevelText<localizes>(CurrentPickupLevel : int) : message = "Poziom odbioru {CurrentPickupLevel}"
- Dodaj wyrażenie
block
, które tworzy widżet kanwy i umieszcza tekst rozmieszczony pionowo z lewej strony ekranu.<# W czasie życia menedżera wyników nie będziemy ponownie tworzyć kanwy, dlatego należy utworzyć ją raz przy każdym tworzeniu obiektu tego typu. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := true Widget := stack_box: Orientation := orientation.Vertical Slots := array: stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := TotalGameScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PendingScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PickupLevelWidget
- Zmienna
- Otrzymany kod menedżera wyników
score_manager
powinien wyglądać następująco:using { /UnrealEngine.com/Temporary/UI } using { /Fortnite.com/UI } using { /Verse.org/Colors } score_manager := class: var Canvas<internal> : canvas = canvas{} TotalGameScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PendingScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PickupLevelWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} MaybePlayer<internal> : ?agent = false MaybePlayerUI<internal> : ?player_ui = false ScoreManagerDevice<internal> : score_manager_device = score_manager_device{} PickupLevelText<private><localizes>(InLevel : int) : message = "Poziom odbioru: {InLevel}" PendingScoreText<private><localizes>(InPoints : int) : message = "Punkty oczekujące: {InPoints}" TotalGameScoreText<private><localizes>(InPoints : int) : message = "Łączna liczba punktów: {InPoints}" var TotalGameScore<private> : int = 0 var PendingScore<private> : int = 0 var PickupLevel<private> : int = 0 <# W czasie życia menedżera wyników nie będziemy ponownie tworzyć kanwy, dlatego należy utworzyć ją raz przy każdym tworzeniu obiektu tego typu. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := true Widget := stack_box: Orientation := orientation.Vertical Slots := array: stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := TotalGameScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PendingScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PickupLevelWidget
- Utwórz funkcję o nazwie
UpdateUI()
zawierającą specyfikatorprivate
, która będzie aktualizować tekst w UI o najnowsze wartości wyniku i bieżący poziom odbioru.UpdateUI<private>() : void = if (PlayerUI := MaybePlayerUI?): PickupLevelWidget.SetText(PickupLevelText(PickupLevel)) PendingScoreWidget.SetText(PendingScoreText(PendingScore)) PendingScoreWidget.SetText(TotalGameScoreText(TotalGameScore))
- Utwórz funkcję o nazwie
AddScoreManagerToUI()
, która będzie aktualizować UI gracza o niestandardowy UI menedżera wyników.AddScoreManagerToUI<public>() : void = if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas) UpdateUI()
- Utwórz następujące funkcje dla każdej wartości wyświetlanej w UI, aby pętla gry mogła aktualizować wartości:
- Funkcja o nazwie
AddPendingScoreToTotalScore()
zawierająca specyfikatorpublic
. Funkcja powinna dodawać wynik oczekujący do łącznego wyniku gry, a następnie resetować wartość wyniku oczekującego do0
. Za pomocą wyrażeniadefer
można opóźnić resetowanie wynikuPendingScore
i zaktualizować UI po aktualizacji wynikuTotalGameScore
. Pozwala to uniknąć przytrzymywania wartości wynikuPendingScore
przez zmienną tymczasową przed jego zresetowaniem.<# Dodaje wartość wyniku PendingScore do wartości wyniku TotalGameScore i resetuje wartość PendingScore do 0.#> AddPendingScoreToTotalScore<public>() : void = defer: set PendingScore = 0 UpdateUI() set TotalGameScore += PendingScore
- Funkcja o nazwie
UpdatePendingScore()
zawierająca specyfikatorpublic
i parametr liczby całkowitej o nazwiePoints
, który funkcja doda do aktualnego oczekującego wyniku.<# Dodaje konkretną liczbę punktów do punktów oczekujących. #> UpdatePendingScore<public>(Points : int) : void = set PendingScore += Points UpdateUI()
- Funkcja o nazwie
UpdatePickupLevel
zawierająca specyfikatorpublic
oraz parametr liczby całkowitej o nazwieLevel
oznaczający nową wartość bieżącego poziomu odbioru.UpdatePickupLevel<public>(Level : int) : void = set PickupLevel = Level UpdateUI()
- Utwórz funkcję o nazwie
AwardScore()
zawierającą specyfikatorpublic
. Funkcja ta przyznaje graczowi wynik za pomocą urządzenia menedżera wyniku oraz aktywuje to urządzenie.<# Przyznaje graczowi wynik za pomocą urządzenia menedżera wyniku, aktywując to urządzenie. #> AwardScore<public>() : void = ScoreManagerDevice.SetScoreAward(TotalGameScore) if (AwardedPlayer := MaybePlayer?): ScoreManagerDevice.Activate(AwardedPlayer)
- Funkcja o nazwie
- Utworzona klasa
score_manager
powinna wyglądać następująco:score_manager := class: <# W czasie życia menedżera wyników nie będziemy ponownie tworzyć kanwy, dlatego należy utworzyć ją raz przy każdym tworzeniu obiektu tego typu. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := true Widget := stack_box: Orientation := orientation.Vertical Slots := array: stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := TotalGameScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PendingScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PickupLevelWidget AddScoreManagerToUI<public>() : void = if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas) UpdateUI() <# Dodaje wartość PendingPickupPoints do wartości TotalPickupPoints i resetuje wartość PendingPickupPoints do 0. Zwraca łączną liczbę dodanych punktów za odbiór. #> AddPendingScoreToTotalScore<public>() : void = set TotalGameScore += PendingScore defer: set PendingScore = 0 UpdateUI() <# Dodaje konkretną liczbę punktów do punktów oczekujących. #> UpdatePendingScore<public>(Points : int) : void = set PendingScore += Points UpdateUI() UpdatePickupLevel<public>(Level : int) : void= set PickupLevel = Level UpdateUI() <# Przyznaje graczowi wynik za pomocą urządzenia menedżera wyniku, aktywując to urządzenie. #> AwardScore<public>() : void = ScoreManagerDevice.SetScoreAward(TotalGameScore) if (AwardedPlayer := MaybePlayer?): ScoreManagerDevice.Activate(AwardedPlayer) MaybePlayer<internal> : ?agent = false MaybePlayerUI<internal> : ?player_ui = false ScoreManagerDevice<internal> : score_manager_device = score_manager_device{} var Canvas<internal> : canvas = canvas{} TotalGameScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PendingScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PickupLevelWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PickupLevelText<private><localizes>(InLevel : int) : message = "Poziom odbioru: {InLevel}" PendingScoreText<private><localizes>(InPoints : int) : message = "Punkty oczekujące: {InPoints}" TotalGameScoreText<private><localizes>(InPoints : int) : message = "Łączna liczba punktów: {InPoints}" var TotalGameScore<private> : int = 0 var PendingScore<private> : int = 0 var PickupLevel<private> : int = 0 UpdateUI<private>() : void = if (PlayerUI := MaybePlayerUI?): PickupLevelWidget.SetText(PickupLevelText(PickupLevel)) PendingScoreWidget.SetText(PendingScoreText(PendingScore)) PendingScoreWidget.SetText(TotalGameScoreText(TotalGameScore))
- Masz już utworzoną klasę
score_manager
. Teraz utwórz konstruktor dla klasy, aby inicjować zmienne gracza z poziomu gry. Pamiętaj, że w celu uzyskania odwołania do UI gracza musisz przeprowadzić rzutowanie typu odwołania do gracza zagent
naplayer
.MakeScoreManager<constructor><public>(InPlayer : agent, InScoreManagerDevice : score_manager_device) := score_manager: MaybePlayer := option{InPlayer} MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]}
- Plik score_manager.verse powinien teraz wyglądać następująco:
using { /UnrealEngine.com/Temporary/SpatialMath } using { /UnrealEngine.com/Temporary/UI } using { /Fortnite.com/Devices } using { /Fortnite.com/UI } using { /Verse.org/Colors } using { /Verse.org/Simulation } MakeScoreManager<constructor><public>(InPlayer : agent, InScoreManagerDevice : score_manager_device) := score_manager: MaybePlayer := option{InPlayer} MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]} score_manager := class: <# W czasie życia menedżera wyników nie będziemy ponownie tworzyć kanwy, dlatego należy utworzyć ją raz przy każdym tworzeniu obiektu tego typu. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := true Widget := stack_box: Orientation := orientation.Vertical Slots := array: stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := TotalGameScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PendingScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PickupLevelWidget AddScoreManagerToUI<public>() : void = if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas) UpdateUI() <# Dodaje wartość PendingPickupPoints do wartości TotalPickupPoints i resetuje wartość PendingPickupPoints do 0. Zwraca łączną liczbę dodanych punktów za odbiór. #> AddPendingScoreToTotalScore<public>() : int = set TotalGameScore += PendingScore defer: set PendingScore = 0 UpdateUI() return PendingScore <# Dodaje konkretną liczbę punktów do punktów oczekujących. #> UpdatePendingScore<public>(Points : int) : void = set PendingScore += Points UpdateUI() UpdatePickupLevel<public>(Level : int) : void= set PickupLevel = Level UpdateUI() <# Przyznaje graczowi wynik za pomocą urządzenia menedżera wyniku, aktywując to urządzenie. #> AwardScore<public>() : void = ScoreManagerDevice.SetScoreAward(TotalGameScore) if (AwardedPlayer := MaybePlayer?): ScoreManagerDevice.Activate(AwardedPlayer) MaybePlayer<internal> : ?agent = false MaybePlayerUI<internal> : ?player_ui = false ScoreManagerDevice<internal> : score_manager_device = score_manager_device{} var Canvas<internal> : canvas = canvas{} TotalGameScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PendingScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PickupLevelWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PickupLevelText<localizes><internal>(CurrentPickupLevel : int) : message = "Poziom odbioru: {CurrentPickupLevel}" PendingScoreText<localizes><internal>(CurrentPendingScore : int) : message = "Punkty oczekujące: {CurrentPendingScore}" TotalGameScoreText<localizes><internal>(CurrentTotalGameScore : int) : message = "Łączna liczba punktów: {CurrentTotalGameScore}" var TotalGameScore<private> : int = 0 var PendingScore<private>:int = 0 var PickupLevel<private>:int = 0 UpdateUI<private>() : void = if (PlayerUI := MaybePlayerUI?): PickupLevelWidget.SetText(PickupLevelText(PickupLevel)) PendingScoreWidget.SetText(PendingScoreText(PendingScore)) PendingScoreWidget.SetText(TotalGameScoreText(TotalGameScore))
Aktualizacja wyniku i UI w pętli gry
Aby utworzyć, a następnie aktualizować UI w trakcie gry w pliku game_coordinator_device.verse, wykonaj następujące instrukcje:
- Dodaj następujące właściwości do klasy
game_coordinator_device
:- Zmienna
score_manager
o nazwieScoreManager
zawierająca specyfikatorprivate
. Ta instancja zarządza wynikiem gracza i UI.var ScoreManager<private> : score_manager = score_manager{}
- Edytowalna wartość
score_manager_device
, dla której można ustawić urządzenie menedżera wyniku w konkretnym poziomie. Urządzenie to będzie urządzeniem używanym przez klasęscore_manager
.@editable ScoreManagerDevice<public> : score_manager_device = score_manager_device{}
- Edytowalna tablica liczb całkowitych o nazwie
PointsForPickupLevel
zawierająca specyfikatorpublic
, aby zdefiniować punkty, jakie gracz może otrzymać za każdy poziom odbioru.@editable # Mapuje liczbę punktów, jaka przysługuje za odbiór, w zależności od poziomu odbioru. PointsForPickupLevel<public> : []int = array{1, 2, 3}
- Zmienna
-
W funkcji
StartGame
zainicjuj zmienną menedżera wyników, wywołując konstruktorMakeScoreManager()
z odwołaniem do gracza i urządzeniem menedżera wyniku, a następnie wygeneruj UI widoczne dla gracza.StartGame<private>()<suspends> : void = Logger.Print("Próba rozpoczęcia gry...") <# Konstruujemy nowy licznik czasu odliczania countdown_timer, który po uruchomieniu zacznie odliczać w dół od wartości InitialCountdownTime. Utwórz również nową klasę score_manager, która będzie śledzić wynik gracza oraz poziom odbioru. Klasy countdown_timer i score_manager wymagają gracza, któremu można wyświetlić UI. Na tym etapie powinniśmy już mieć prawidłowego gracza: tego, który wszedł do pojazdu, aktywując rozpoczęcie gry. #> if (ValidPlayer := MaybePlayer?): Logger.Print("Prawidłowy gracz, rozpoczynanie gry...") set ScoreManager = MakeScoreManager(ValidPlayer, ScoreManagerDevice) ScoreManager.AddScoreManagerToUI() set CountdownTimer = MakeCountdownTimer(InitialCountdownTime, ValidPlayer) CountdownTimer.StartCountdown() # Czekamy na zakończenie odliczania. # Jednocześnie uruchamiamy również pętlę gry dotyczącą odbioru i dostarczania, która stanowi główną rozgrywkę. race: HandleCountdownEnd(ValidPlayer) PickupDeliveryLoop() else: Logger.Print("Nie znaleziono prawidłowego gracza. Przerwanie uruchamiania gry", ?Level := log_level.Error)
- W funkcji
PickupDeliveryLoop()
pętli gry aktualizuj UI po każdej zmianie poziomu odbioru i zakończeniu przez gracza odbioru lub dostarczenia:PickupDeliveryLoop<private>()<suspends> : void = PickupZonesTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} MaxPickupLevel := PickupZonesTags.Length - 1 FirstPickupZoneCompletedEvent := event(){} loop: var PickupLevel : int = 0 var IsFirstPickup : logic = true <# Za każdym razem, gdy pętla zostanie ponownie uruchomiona, UI poziomu odbioru powinien być resetowany przez ScoreManager. Początkowym poziomem odbioru w UI jest 1 (nie 0). Jeśli odliczanie rozpocznie się od 0, niektórzy gracze będą zdezorientowani. Indeksujemy od 0, zatem PickupLevel=0 oznacza poziom 1 w UI. #> ScoreManager.UpdatePickupLevel(PickupLevel + 1) race: loop: if (PickupZone:base_zone = PickupZones[PickupLevel].SelectNext[]): PickupZone.ActivateZone() <# To jedyne wyrażenie defer, jakiego potrzebujemy dla dowolnej aktywowanej strefy PickupZone. Spowoduje ono dezaktywowanie pierwszej strefy PickupZone na końcu każdej pętli zewnętrznej lub dezaktywację dowolnej późniejszej strefy PickupZone. Dzieje się tak dlatego, że wyrażenie jest poddawane ocenie na końcu, gdy zmienna PickupZone została już powiązana z nowszą strefą. #> defer: PickupZone.DeactivateZone() PickupZone.ZoneCompletedEvent.Await() Logger.Print("Odebrano", ?Level:=log_level.Normal) # Po pierwszym odbiorze możemy włączyć strefę dostarczania. if (IsFirstPickup?): set IsFirstPickup = false FirstPickupZoneCompletedEvent.Signal() if (PickupPoints := PointsForPickupLevel[PickupLevel]): ScoreManager.UpdatePendingScore(PickupPoints) # Zaktualizuj poziom odbioru i wartość ScoreManager. if (PickupLevel < MaxPickupLevel): set PickupLevel += 1 ScoreManager.UpdatePickupLevel(PickupLevel + 1) else: Logger.Print("Nie znaleziono następnej strefy PickupZone do wybrania", ?Level := log_level.Error) return # Błąd pętli PickupDeliveryLoop block: FirstPickupZoneCompletedEvent.Await() if (DeliveryZone := DeliveryZoneSelector.SelectNext[]): DeliveryZone.ActivateZone() # Opóźniamy dezaktywację strefy, aby anulowanie pętli PickupDeliveryLoop kończyło się także dezaktywacją każdej aktywnej strefy dostarczania. defer: Logger.Print("Dezaktywacja strefy dostarczania.", ?Level := log_level.Normal) DeliveryZone.DeactivateZone() DeliveryZone.ZoneCompletedEvent.Await() Logger.Print("Dostarczono", ?Level := log_level.Normal) ScoreManager.AddPendingScoreToTotalScore() else: Logger.Print("Nie znaleziono następnej strefy DeliveryZone do wybrania.", ?Level := log_level.Error) return # Błąd pętli PickupDeliveryLoop
- Teraz, gdy odliczanie dobiegło końca, przyznaj graczowi wynik. W funkcji
HandleCountdownEnd()
wywołaj funkcjęScoreManager.AwardScore()
.HandleCountdownEnd<private>(InPlayer : player)<suspends>:void= TotalTime := CountdownTimer.CountdownEndedEvent.Await() ScoreManager.AwardScore() EndGame.Activate(InPlayer)
- Otrzymany plik game_coordinator_device.verse powinien wyglądać następująco:
using { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /Fortnite.com/Vehicles } using { /Fortnite.com/Characters } using { /Fortnite.com/Playspaces } using { /Verse.org/Random } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } using { /EpicGames.com/Temporary/Curves } using { /Verse.org/Simulation/Tags } # Tagi stref gry pickup_zone_tag<public> := class(tag): pickup_zone_level_1_tag<public> := class(pickup_zone_tag): pickup_zone_level_2_tag<public> := class(pickup_zone_tag): pickup_zone_level_3_tag<public> := class(pickup_zone_tag): delivery_zone_tag<public> := class(tag): log_pizza_pursuit<internal> := class(log_channel){} game_coordinator_device<public> := class(creative_device): # Od jakiej wartości licznik czasu odliczania rozpocznie odliczanie w dół. @editable InitialCountdownTime<public> : float = 30.0 @editable EndGame<public> : end_game_device = end_game_device{} @editable ScoreManagerDevice<public> : score_manager_device = score_manager_device{} @editable # Mapuje liczbę punktów, jaka przysługuje za odbiór, w zależności od poziomu odbioru. PointsForPickupLevel<public> : []int = array{1, 2, 3} OnBegin<override>()<suspends> : void = FindPlayer() SetupZones() StartGame() Logger<private> : log = log{Channel := log_pizza_pursuit} var MaybePlayer<private> : ?agent = false var CountdownTimer<private> : countdown_timer = countdown_timer{} var ScoreManager<private> : score_manager = score_manager{} DeliveryZoneSelector<private> : tagged_zone_selector = tagged_zone_selector{} var PickupZoneSelectors<private> : []tagged_zone_selector = array{} FindPlayer<private>() : void = # Gra odbywa się w trybie jednego gracza, dlatego pierwszy gracz (0) # powinien być jedynym dostępnym graczem. Playspace := Self.GetPlayspace() if (FirstPlayer := Playspace.GetPlayers()[0]): set MaybePlayer = option{FirstPlayer} Logger.Print("Znaleziono gracza") else: # Zarejestruj błąd, jeśli nie można znaleźć gracza. # To nie powinno się zdarzyć, ponieważ co najmniej jeden gracz jest zawsze obecny. Logger.Print("Nie znaleziono prawidłowego gracza", ?Level := log_level.Error) SetupZones<private>() : void = # Istnieje tylko jeden typ strefy dostarczania, ponieważ nie skaluje się ona według poziomu trudności. DeliveryZoneSelector.InitZones(delivery_zone_tag{}) # Strefy (reprezentowane przez urządzenia) wybiera się za pomocą tagów rozgrywki w oparciu o poziom ich trudności. # Zastosowanie tablicy ułatwia modyfikowanie poziomów trudności: można dodać więcej # poziomów, zwiększać lub zmniejszać poziom ich szczegółowości bądź zmieniać ich kolejność bez ingerencji w kod. # Utwórz jeden element tagged_zone_selector dla każdego tagu poziomu trudności, aby wszystkie urządzenia oznaczone tym samym tagiem (tj. o takim samym poziomie trudności) # trafiły do tej samej puli wyboru. LevelTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} set PickupZoneSelectors = for (ZoneTag : LevelTags): NewZone := tagged_zone_selector{} NewZone.InitZones(ZoneTag) NewZone StartGame<private>()<suspends> : void = Logger.Print("Próba rozpoczęcia gry...") <# Konstruujemy nowy licznik czasu odliczania countdown_timer, który po uruchomieniu zacznie odliczać w dół od wartości InitialCountdownTime. Utwórz również nową klasę score_manager, która będzie śledzić wynik gracza oraz poziom odbioru. Klasy countdown_timer i score_manager wymagają gracza, któremu można wyświetlić UI. Na tym etapie powinniśmy już mieć prawidłowego gracza: tego, który wszedł do pojazdu, aktywując rozpoczęcie gry. #> if (ValidPlayer := MaybePlayer?): Logger.Print("Prawidłowy gracz, rozpoczynanie gry...") set ScoreManager = MakeScoreManager(ValidPlayer, ScoreManagerDevice) ScoreManager.AddScoreManagerToUI() set CountdownTimer = MakeCountdownTimer(InitialCountdownTime, ValidPlayer) CountdownTimer.StartCountdown() # Czekamy na zakończenie odliczania. # Jednocześnie uruchamiamy również pętlę gry dotyczącą odbioru i dostarczania, która stanowi główną rozgrywkę. race: HandleCountdownEnd(ValidPlayer) PickupDeliveryLoop() else: Logger.Print("Nie znaleziono prawidłowego gracza. Przerwanie uruchamiania gry", ?Level := log_level.Error) HandleCountdownEnd<private>(InPlayer : agent)<suspends> : void = CountdownTimer.CountdownEndedEvent.Await() ScoreManager.AwardScore() EndGame.Activate(InPlayer) PickupDeliveryLoop<private>()<suspends> : void = PickupZonesTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} MaxPickupLevel := PickupZonesTags.Length - 1 FirstPickupZoneCompletedEvent := event(){} loop: var PickupLevel : int = 0 var IsFirstPickup : logic = true <# Za każdym razem, gdy pętla zostanie ponownie uruchomiona, UI poziomu odbioru powinien być resetowany przez ScoreManager. Początkowym poziomem odbioru w UI jest 1 (nie 0). Jeśli odliczanie rozpocznie się od 0, niektórzy gracze będą zdezorientowani. Indeksujemy od 0, zatem PickupLevel=0 oznacza poziom 1 w UI. #> ScoreManager.UpdatePickupLevel(PickupLevel + 1) race: loop: if (PickupZone:base_zone = PickupZoneSelectors[PickupLevel].SelectNext[]): PickupZone.ActivateZone() <# To jedyne wyrażenie defer, jakiego potrzebujemy dla dowolnej aktywowanej strefy PickupZone. Spowoduje ono dezaktywowanie pierwszej strefy PickupZone na końcu każdej pętli zewnętrznej lub dezaktywację dowolnej późniejszej strefy PickupZone. Dzieje się tak dlatego, że wyrażenie jest poddawane ocenie na końcu, gdy zmienna PickupZone została już powiązana z nowszą strefą. #> defer: PickupZone.DeactivateZone() PickupZone.ZoneCompletedEvent.Await() Logger.Print("Odebrano", ?Level := log_level.Normal) # Po pierwszym odbiorze możemy włączyć strefę dostarczania. if (IsFirstPickup?): set IsFirstPickup = false FirstPickupZoneCompletedEvent.Signal() if (PickupPoints := PointsForPickupLevel[PickupLevel]): ScoreManager.UpdatePendingScore(PickupPoints) # Zaktualizuj poziom odbioru i wartość ScoreManager. if (PickupLevel < MaxPickupLevel): set PickupLevel += 1 ScoreManager.UpdatePickupLevel(PickupLevel + 1) else: Logger.Print("Nie znaleziono następnej strefy PickupZone do wybrania", ?Level := log_level.Error) return # Błąd pętli PickupDeliveryLoop block: FirstPickupZoneCompletedEvent.Await() if (DeliveryZone := DeliveryZoneSelector.SelectNext[]): DeliveryZone.ActivateZone() # Opóźniamy dezaktywację strefy, aby anulowanie pętli PickupDeliveryLoop kończyło się także dezaktywacją każdej aktywnej strefy dostarczania. defer: Logger.Print("Dezaktywacja strefy dostarczania.", ?Level := log_level.Normal) DeliveryZone.DeactivateZone() DeliveryZone.ZoneCompletedEvent.Await() Logger.Print("Dostarczono", ?Level := log_level.Normal) ScoreManager.AddPendingScoreToTotalScore() else: Logger.Print("Nie znaleziono następnej strefy DeliveryZone do wybrania.", ?Level := log_level.Error) return # Błąd pętli PickupDeliveryLoop