W grach wyścigowych powszechnie stosuje się rozróżnienie pozycji startowych w zależności od wyniku uzyskanego w poprzedniej rundzie. W ten sposób zachęca się graczy do szybkiego ukończenia wyścigu, nawet jeśli nie zajmują pierwszej lokaty, aby mogli wystartować przed innymi graczami.
Aby uzyskać taki rezultat, gra musi wiedzieć, w której rundzie akurat są gracze, a kolejność ukończenia wyścigu musi być wartością trwałą zachowywaną przez wszystkie rundy, ale nie przez wszystkie sesje gry. Zmienna mapy słabości sesji w Verse resetuje swoje dane w każdej rundzie, więc informacje o rundach muszą być przechowywane dla każdego gracza, przy użyciu zmiennej mapy słabości gracza, i resetowane po zakończeniu gry.
Obecnie projekt może zawierać tylko maksymalnie dwie zmienne słabej mapy gracza. Jeśli projekt zawiera już zmienną słabej mapy gracza, dobrze jest skonfigurować drugą taką zmienną do rejestrowania danych rundy w celu rozróżnienia danych, które powinny być trwale przechowywane od danych resetowanych po zakończeniu gry lub w momencie opuszczenia sesji przez gracza.
Ważne jest również, aby wiedzieć, w której rundzie akurat znajduje się gracz, aby zastosować logikę właściwą dla rundy i zresetować dane rundy przy ostatniej rundzie. Obecnie nie ma interfejsu API do pobierania bieżącej rundy, więc informację tę także trzeba rejestrować w danych z możliwością persystencji dla każdego gracza.
Podsumowując, będziesz potrzebować zmiennej słabej mapy gracza zawierającej przynajmniej następujące informacje:
Zakończ zamówienie
Ostatnia ukończona runda
W poniższych sekcjach zaprezentowano, w jaki sposób skonfigurować te dane oraz logikę rund. Na końcu strony znajdziesz kompletny kod.
Zarejestruj ostatnią ukończoną rundę
Wykonaj poniższą procedurę, aby skonfigurować dane z możliwością persystencji dla każdego gracza oraz rejestrować ostatnią ukończoną rundę.
Utwórz klasę możliwą do utrwalenia o nazwie
player_circuit_info, aby przechowywać informacje o graczu z różnych rund. Klasa powinna zawierać pola oznaczające ostatnią pozycję gracza na mecie –LastRoundFinishOrder, oraz ostatnią ukończoną rundę gracza –LastCompletedRound. Pola te są inicjowane z wartością-1reprezentującą wartości nieprawidłowe, dzięki czemu będziesz wiedzieć, gdy pola te będą zawierały faktycznie użyteczne informacje.Verse# Tracks the number of and in what order a player finished the previous round. player_circuit_info<public> := class<final><persistable>: Version:int = 0 LastRoundFinishOrder:int = -1 LastCompletedRound<public>:int = -1Utwórz zmienną słabej mapy gracza przy użyciu klasy
player_circuit_info, aby utrwalić dane rund przy graczach.Verse# A persistable map that maps each player to # what order they finished the previous round. var CircuitInfo<public>:weak_map(player, player_circuit_info) = map{}Dobrą praktyką podczas pracy z danymi możliwymi do persystencji jest utworzenie konstruktora, aby umożliwić klasie z możliwością persystencji łatwe aktualizowanie danych dla każdego gracza. Aby dowiedzieć się więcej, patrz Używanie konstruktorów dla częściowych aktualizacji.
Verse# Creates a new player_circuit_info from the given older player_circuit_info. MakePlayerCircuitInfo<constructor>(OldPlayerCircuitInfo:player_circuit_info)<transacts> := player_circuit_info: Version := OldPlayerCircuitInfo.Version LastRoundFinishOrder := OldPlayerCircuitInfo.LastRoundFinishOrder LastCompletedRound := OldPlayerCircuitInfo.LastCompletedRoundTeraz, gdy masz już zdefiniowane struktury dla tych danych, dodaj funkcję, aby rejestrować pozycję gracza na mecie i aktualizować jego trwałe dane. Funkcja ta wykorzystuje konstruktora utworzonego w poprzednim kroku do częściowego aktualizowania danych dla jedynej istotnej informacji: pozycji na mecie.
Verse# Creates a new player_circuit_info for the given player with the order they finished the round in. RecordPlayerFinishOrder<public>(Agent:agent, FinishOrder:int)<decides><transacts>:void= Player := player[Agent] Player.IsActive[] PlayerCircuitInfo:player_circuit_info = if: Info := CircuitInfo[Player] then: Info else: player_circuit_info{}Utwórz kolejną funkcję, aby zaktualizować jedynie ostatnią ukończoną rundę dla gracza.
Verse# Updates a player's player_circuit_info with their last completed round. UpdateRound<public>(Agent:agent, CompletedRound:int)<decides><transacts>:void= Player := player[Agent] Player.IsActive[] PlayerCircuitInfo := CircuitInfo[Player] set CircuitInfo[Player] = player_circuit_info: MakePlayerCircuitInfo<constructor>(PlayerCircuitInfo) LastCompletedRound := CompletedRoundTeraz możesz zarejestrować ostatnią ukończoną rundę dla danego gracza. Teraz utwórz funkcję do obliczania ostatniej ukończonej rundy gry, sprawdzając, którzy gracze mają ostatnio zarejestrowaną rundę. Konieczne jest sprawdzenie wszystkich graczy, aby uwzględnić tych, którzy mogli dołączyć do sesji w trakcie jej trwania. Zmienna ostatniej ukończonej rundy inicjowana jest z wartością
-1reprezentującą nieprawidłowe dane. Jeśli którykolwiek gracz ma wartość większą niż-1, runda już się zakończyła.Verse# Returns the highest last completed round among all players. GetLastCompletedRound<public>(Players:[]player, TotalRounds:int)<transacts>:int= var LastCompletedRound:int = -1 for: Player : Players Player.IsActive[] PlayerCircuitInfo := CircuitInfo[Player] do: # Update LastCompletedRound if this player has the highest last completed round. else if:Utwórz urządzenie Verse, aby przetestować, czy runda i pozycja na mecie działają zgodnie z oczekiwaniami. Upewnij się, że projekt jest skonfigurowany do obsługi wielu rund, ustawiając właściwość Łączna liczba rund w Ustawieniach wyspy.
Verse# A Verse-authored creative device that can be placed in a level test_round_info_device := class(creative_device): # Runs when the device is started in a running game OnBegin<override>()<suspends>:void= Players := GetPlayspace().GetPlayers() CurrentRound := GetLastCompletedRound(Players) + 1 Print("Current round is {CurrentRound}") for:
Resetuj informacje o rundzie po wyjściu gracza
Trwałe dane z informacjami o rundzie gracza powinny być resetowane, gdy gracz opuści grę. Możesz zasubskrybować zdarzenie PlayerRemovedEvent przestrzeni gry, aby wiedzieć, kiedy gracz ją opuści.
Aby resetować informacje o rundzie, gdy gracz opuści grę, wykonaj następującą procedurę:
Utwórz funkcję, aby zresetować trwałe dane gracza z informacjami o rundzie. To oznacza ustawienie wartości na
-1lub inną wybraną przez ciebie wartość, która będzie reprezentować dane nieprawidłowe dla tych pól. Ta implementacja wybiera, które dane powinny być resetowane, na wypadek późniejszego dodania do klasy trwałej innych informacji, które nie powinny być resetowane w tym miejscu.Verse# Resets a player's player_circuit_info. ResetCircuitInfo<public>(Agent:agent)<decides><transacts>:void= Player := player[Agent] Player.IsActive[] PlayerCircuitInfo := CircuitInfo[Player] set CircuitInfo[Player] = player_circuit_info: MakePlayerCircuitInfo<constructor>(PlayerCircuitInfo) LastRoundFinishOrder := -1 LastCompletedRound := -1Utwórz funkcję o nazwie
OnPlayerRemoved, aby zresetować informacje o rundzie, gdy gracz opuści grę.Verse# When a player is removed from the race, reset their circuit to prevent their # stats from showing up on billboards and player references. OnPlayerRemoved(Player:player):void= # Reset circuit info when player leaves the game. if: ResetCircuitInfo[Player] else: Print("Unable to reset circuit info for player")Skonfiguruj funkcję
OnPlayerRemoved, która będzie wywoływana, gdy gracz opuści grę, poprzez subskrypcję zdarzeniaGetPlayspace().PlayerRemovedEvent().Verse# A Verse-authored creative device that can be placed in a level test_round_info_device := class(creative_device): # Runs when the device is started in a running game OnBegin<override>()<suspends>:void= GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved) Players := GetPlayspace().GetPlayers() CurrentRound := GetLastCompletedRound(Players) + 1Wykonaj test, aby sprawdzić, czy opuszczenie gry przez gracza spowoduje zresetowanie jego danych.
Resetuj informacje o rundzie po zakończeniu gry
Funkcja OnBegin urządzenia Verse jest uruchamiana na początku każdej rundy. Jest to dobry moment, aby ustalić, czy gracz posiada nieoczekiwane dane trwałe, takie jak informacja o tym, czy numer ostatniej ukończonej przez gracza rundy odpowiada łącznej liczbie rund. Jeśli tak jest, trzeba zresetować dane gracza. Obecnie nie ma interfejsu API pozwalającego pobrać łączną liczbę rund w grze. Zamiast tego musisz dodać edytowalną właściwość do urządzenia Verse dla łącznej liczby rund w kodzie Verse, i upewnić się, że jest ona zgodna z właściwością Suma rund w Ustawieniach wyspy.
Aby resetować informacje o rundzie po zakończeniu gry, wykonaj następującą procedurę:
Dodaj do urządzenia Verse edytowalną właściwość łącznej liczby rund w grze. Wartość TotalRounds nie może być równa
0ani mniejsza, więc ogranicz wartości właściwości do wartości równych1lub większych.Verse# A Verse-authored creative device that can be placed in a level test_round_info_device := class(creative_device): # The total number of rounds in the race. @editable TotalRounds:type {Rounds:int where 1 <= Rounds} = 3 # Runs when the device is started in a running game OnBegin<override>()<suspends>:void= GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)Zaktualizuj funkcję dla
GetLastCompletedRound, aby zresetować trwałe dane gracza dla ostatniej ukończonej rundy, jeśli wartość przekracza oczekiwaną liczbę rund w grze.Verse# Returns the highest last completed round among all players. GetLastCompletedRound<public>(Players:[]player, TotalRounds:int)<transacts>:int= var LastCompletedRound:int = -1 for: Player : Players Player.IsActive[] PlayerCircuitInfo := CircuitInfo[Player] do: # If player's recorded info is greater than the total rounds for whatever reason, # then need to reset the player's circuit info because they shouldn't have more than what's allowed.Zaktualizuj wywołanie do
GetLastCompletedRound, aby przekazać łączną liczbę rund jako argument w celu zresetowania informacji o rundzie gracza, jeśli przybiorą one nieoczekiwany stan.Verse# A Verse-authored creative device that can be placed in a level test_round_info_device := class(creative_device): # The total number of rounds in the race. @editable TotalRounds:type {Rounds:int where 1 <= Rounds} = 3 # Runs when the device is started in a running game OnBegin<override>()<suspends>:void= GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)Wykonaj test, aby sprawdzić, czy informacje o rundzie gracza zostaną zresetowane po rozegraniu i ukończeniu wszystkich rund.
Dodawanie logiki w oparciu o bieżącą rundę
Teraz możesz wykorzystać te informacje, aby wdrożyć logikę niestandardową w zależności od tego, w której rundzie są gracze. Możesz na przykład wyświetlić ranking lokalny po pierwszej rundzie gry.
Wywoływanie funkcji GetLastCompletedRound() za każdym razem, gdy musisz się dowiedzieć, w której rundzie jest gracz, nie jest idealnym rozwiązaniem. Zamiast tego możesz to robić tylko raz na rundę i zapisać informacje o rundzie w słabej zmiennej mapy sesji, dzięki czemu cały kod Verse w projekcie może uzyskać dostęp do tej wartości w dowolnym momencie bez konieczności jej ponownego obliczania za każdym razem.
Jest to świetny przykład do pokazania różnic i uzasadnienia użycia zmiennej słabej mapy sesji i zmiennej słabej mapy gracza w kodzie:
Zmienne słabej mapy sesji są przydatne dla singletonów i przechowywania danych dla bieżącej rundy, których nie chcemy za każdym razem ponownie obliczać.
Zmienne mapy słabości gracza zostały zaprojektowane tak, aby przyjmować informacje, które muszą się utrzymywać w wielu rundach i sesjach gry, ale muszą być powiązane z poszczególnymi graczami.
Aby skonfigurować zmienną słabej mapy gracza do przechowywania informacji o bieżącej rundzie, wykonaj poniższą procedurę.
Utwórz klasę do przechowywania informacji o rundzie. Potrzebujesz przynajmniej pola dla
CurrentRound,ale możesz dołączyć inne informacje o rundzie, którą chcesz zapisać, w całym kodzie Verse, takie jak pozycje początkowe i pojazdy graczy. ZainicjujCurrentRoundna-1, aby reprezentować nieprawidłowe dane.Verseround_info := class: CurrentRound:int = -1Utwórz zmienną słabej mapy sesji, używając klasy
round_infodo przechowywania informacji na temat rundy wraz z sesją.Verse# Maps the current session to its associated round info. var RoundInfo:weak_map(session, round_info) = map{}Dodaj funkcję getter do pobierania bieżącej rundy ze zmiennej słabej mapy sesji.
VerseGetRound<public>()<decides><transacts>:int= RoundInfo[GetSession()].CurrentRoundDodaj funkcję, aby pobrać bieżącą rundę i zapisać ją w zmiennej słabej mapy sesji.
VerseRecordCurrentRound<public>(Players:[]player, TotalRounds:int):void= var CurrentRoundInfo:round_info = if: Info := RoundInfo[GetSession()] then: Info else: LastCompletedRound := GetLastCompletedRound(Players, TotalRounds) round_info: CurrentRound := LastCompletedRound + 1Zaktualizuj urządzenie Verse, aby użyć funkcji
RecordCurrentRoundi wywołaćGetRound, gdy zachodzi konieczność ustalenia bieżącej rundy gracza.Verse# A Verse-authored creative device that can be placed in a level test_round_info_device := class(creative_device): # The total number of rounds in the race. @editable TotalRounds:type {Rounds:int where 1 <= Rounds} = 3 # Runs when the device is started in a running game OnBegin<override>()<suspends>:void= GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
Teraz, gdy informacja ta jest zapisana w zmiennej słabej mapy sesji, można z łatwością dodać dla rund logikę niestandardową. Można na przykład sprawdzić, czy jest to pierwsze runda i skonfigurować dla graczy lobby oraz obszar wyświetlania rankingu.
# Returns true if this is the first round of the game.
IsFirstRound<public>(RoundToCheck:int)<decides><transacts>:void=
RoundToCheck <= 0Praca własna
Informacje na temat zastosowania tego kodu w grze wyścigowej do ustalania kolejności wyjściowej graczy zawiera artykuł Speedway Race z persystencją Verse.
Po zapoznaniu się z szablonem, wypróbuj następujące rozwiązania:
Dodaj dodatkowe informacje o rundzie, na przykład o pojeździe przypisanym do gracza.
Teleportuj graczy do różnych obszarów mapy na początku każdej rundy.
Czy przychodzą ci do głowy jakieś inne gry wykorzystujące logikę właściwą dla rund?
Complete Code
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
# A persistable map that maps each player to
# what order they finished the previous round.
var CircuitInfo<public>:weak_map(player, player_circuit_info) = map{}
# Maps the current session to its associated round info.
var RoundInfo:weak_map(session, round_info) = map{}