Bei Rennspielen kommt es häufig vor, dass Spieler je nach ihrer Leistung in der vorherigen Runde eine andere Startposition haben. Es ermutigt die Spieler, das Rennen schnell zu beenden, auch wenn sie nicht auf dem ersten Platz sind, sodass sie vor den anderen Spielern starten.
Um dies zu erreichen, muss das Spiel wissen, in welcher Runde sich die Spieler gerade befinden, und die Reihenfolge der Teilnehmer muss über alle Runden hinweg bestehen bleiben – aber nicht über alle Spielsitzungen hinweg. Eine schwache Map-Variable für Sitzungen in Verse setzt ihre Daten jede Runde zurück, so dass diese Rundeninformationen mit jedem Spieler unter Verwendung einer schwachen Map-Variable für Spieler gespeichert und nach Spielende zurückgesetzt werden müssen.
Derzeit kann ein Projekt nur bis zu zwei schwache Map-Variable für Spieler haben. Wenn dein Projekt bereits über eine schwache Map-Variable für Spieler verfügt, ist es eine gute Idee, die zweite Variable die Rundeninformationen aufzeichnen zu lassen, um die Daten, die immer bestehen bleiben sollen, von den Daten zu unterscheiden, die du zurücksetzt, wenn das Spiel endet oder ein Spieler die Sitzung verlässt.
Außerdem ist es wichtig zu wissen, in welcher Runde sich die Spieler gerade befinden, um rundenspezifische Logik anzuwenden und die Rundeninformationen auf die letzte Runde zurückzusetzen. Da derzeit keine API zum Abrufen der aktuellen Runde vorhanden ist, müssen diese Informationen auch in den dauerhaften Daten jedes Spielers aufgezeichnet werden.
Zusammenfassend benötigst du eine schwache Map-Variable für Spieler, die mindestens die folgenden Informationen enthält:
Reihenfolge des Zieleinlaufs
Letzte abgeschlossene Runde
Die folgenden Abschnitte zeigen dir, wie du diese Daten- und Rundenlogik einrichtest. Den vollständigen Code findest du am Ende dieser Seite.
Aufzeichnen die letzten abgeschlossenen Runde
Folge diesen Schritten, um die dauerhaften Daten für jeden Spieler einzurichten und die letzte abgeschlossene Runde aufzuzeichnen.
Erstelle eine „persistable“-Klasse mit dem Namen
player_circuit_info, um Spieler-Infos über Runden hinweg zu speichern. Diese Klasse sollte über die Felder verfügen, um die letzte Reihenfolge des Zieleinlaufs des Spielers,LastRoundFinishOrder, und seine letzte abgeschlossene Runde,LastCompletedRound, darzustellen. Diese Felder werden mit-1initialisiert, um ungültige Werte zu repräsentieren, damit du weißt, wann diese Felder tatsächlich hilfreiche Informationen enthalten.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 = -1Erstelle eine schwache Map-Variable für Spieler mit der Klasse
player_circuit_info, um die Rundeninformationen bei den Spielern dauerhaft zu halten.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{}Als Best Practice für die Arbeit mit dauerhaften Daten erstellst du einen Constructor für die dauerhafte Klasse, um die Informationen für jeden Spieler einfach aktualisieren zu können. Weitere Informationen findest du unter Using constructors for partial updates.
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.LastCompletedRoundNachdem du nun Strukturen für diese Daten definiert hast, fügst du eine Funktion hinzu, um die Endreihenfolge eines Spielers aufzuzeichnen und seine dauerhaften Daten zu aktualisieren. Diese Funktion verwendet den im vorherigen Schritt erstellten Constructor, um die Daten für die einzige Information teilweise zu aktualisieren, die dich interessiert: die Reihenfolge im Ziel.
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{}Erstelle eine weitere Funktion, um nur die letzte abgeschlossene Runde für den Spieler zu aktualisieren.
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 := CompletedRoundDa du nun die letzte abgeschlossene Runde für den Spieler aufzeichnen kannst, erstelle eine Funktion, um die letzte abgeschlossene Runde für das Spiel zu berechnen, indem du prüfst, welche Spieler die zuletzt aufgezeichnete Runde haben. Du musst alle Spieler prüfen, um die Spieler zu berücksichtigen, die möglicherweise der laufenden Sitzung beigetreten sind. Die letzte Variable für die abgeschlossene Runde wird mit
–1initialisiert, um ungültige Daten darzustellen. Wenn irgendwelche Spieler einen Wert größer als–1haben, dann ist bereits eine Runde beendet.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:Erstelle ein Verse-Gerät, um zu testen, ob die Reihenfolge der Runden- und Spielerergebnisse wie erwartet funktioniert. Stelle sicher, dass dein Projekt für mehrere Runden eingerichtet ist, indem du die Eigenschaft „Runden insgesamt“ in den Insel-Einstellungen festlegst.
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:
Beim Verlassen des Spielers Rundeninfo zurücksetzen
Die dauerhaften Daten für die Rundeninformationen eines Spielers sollten zurückgesetzt werden, wenn ein Spieler während des Spiels das Spiel verlässt. Du kannst das PlayerRemovedEvent des Spielbereichs abonnieren, um zu erfahren, wenn sie das Spiel verlassen.
Folge diesen Schritten, um die Rundeninformationen zurückzusetzen, wenn ein Spieler das Speil verlässt:
Erstelle eine Funktion, um die dauerhaften Daten des Spielers für Rundeninformationen zurückzusetzen. Das bedeutet, die Werte auf
–1festzulegen oder was auch immer du zur Darstellung der ungültigen Daten für diese Felder verwendest. Diese Implementierung legt fest, welche Daten zurückgesetzt werden sollen, für den Fall, dass du der dauerhaften Klasse später andere Informationen hinzufügst, die hier nicht zurückgesetzt werden sollen.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 := -1Erstelle eine Funktion mit dem Namen
OnPlayerRemoved, um die Rundeninformationen zurückzusetzen, wenn ein Spieler das Spiel verlässt.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")Richte die Funktion
OnPlayerRemovedein, die aufgerufen werden soll, wenn ein Spieler das Spiel verlässt, indem du das EventGetPlayspace().PlayerRemovedEvent()abonnierst.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) + 1Teste, um zu überprüfen, ob das Verlassen des Spiels durch einen Spieler dessen Informationen zurücksetzt.
Zurücksetzen der Rundeninfo bei Spielende
Die Funktion OnBegin für ein Verse-Gerät wird beim Start jeder Runde ausgeführt. Dies ist ein guter Zeitpunkt, um festzustellen, ob ein Spieler über unerwartete dauerhafte Daten verfügt, z. B. ob die Anzahl seiner letzten abgeschlossenen Runde mit der Gesamtzahl der Runden übereinstimmt. Wenn dies zutrifft, musst du die Daten des Spielers zurücksetzen. Es gibt derzeit keine API, die die Gesamtzahl der Runden in einem Spiel ermitteln kann. Stattdessen musst du dem Verse-Gerät eine bearbeitbare Eigenschaft für die Gesamtzahl der Runden im Verse-Code hinzufügen und sicherstellen, dass sie mit der Eigenschaft „Runden insgesamt“ in Inseleinstellungen übereinstimmt.
Folge diesen Schritten, um die Rundeninfos zurückzusetzen, wenn das Spiel beendet wurde:
Füge eine bearbeitbare Eigenschaft zu deinem Verse-Gerät für die Gesamtzahl der Runden im Spiel hinzu. TotalRounds sollte nicht kleiner als oder gleich
0sein, also beschränke die Werte der Eigenschaft auf größer als oder gleich1.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)Aktualisiere die Funktion für
GetLastCompletedRound, um die dauerhaften Daten für die letzte abgeschlossene Runde des Spielers zurückzusetzen, wenn sie größer als die erwartete Anzahl der Runden im Spiel sind.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.Aktualisiere den Aufruf von
GetLastCompletedRound, um die Runden insgesamt als Argument zu übergeben und die Spieler-Runden-Info zurückzusetzen, wenn sie sich in einem unerwarteten Zustand befinden.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)Teste, um zu verifizieren, dass die Spielerrundeninformationen auch dann zurückgesetzt werden, wenn alle Runden gespielt und abgeschlossen wurden.
Hinzufügen von Logik basierend auf der aktuellen Runde
Nun kannst du diese Informationen verwenden, um eine benutzerdefinierte Logik zu erstellen, die davon abhängt, in welcher Runde sich die Spieler befinden. Beispielsweise könntest du eine lokale Rangliste für die erste Runde eines Spiels anzeigen.
Der Aufruf von GetLastCompletedRound() jedes Mal, wenn du wissen möchtest, welche Runde gespielt wird, ist nicht ideal. Stattdessen kannst du dies einmal pro Runde machen und die Rundeninfos in einer schwachen Map-Variable für Sitzungen aufzeichnen. Dadurch kann der gesamte Verse-Code im Projekt jederzeit auf diesen Wert zugreifen, ohne dass er jedes mal neu berechnet werden muss.
Dies ist ein großartiges Beispiel, um die Unterschiede und die Gründe für die Verwendung von schwachen Map-Variablen für Sitzungen und schwache Map-Variablen für Spieler in deinem Code zu verdeutlichen:
Schwache Map-Variablen für Sitzungen sind nützlich für Einzelspieler und das Speichern von Daten für die aktuelle Runde, die du nicht jedes Mal neu berechnen möchtest.
Die schwachen Spieler-Map-Variablen sind für Informationen gedacht, die über mehrere Runden und Spielsitzungen hinweg erhalten bleiben müssen, aber mit einzelnen Spielern assoziiert sein müssen.
Folge diesen Schritten, um eine schwache Map-Variable für Sitzungen zum Speichern der aktuellen Runde einzurichten.
Erstelle eine Klasse zum Speichern der Rundeninformationen. Du brauchst zumindest ein Feld für
CurrentRound, kannst aber auch andere Rundeninformationen aufnehmen, die du in deinem Verse-Code speichern möchtest, wie z. B. die Startpositionen und die Fahrzeuge der Spieler. InitialisiereCurrentRoundauf–1, um ungültige Daten darzustellen.Verseround_info := class: CurrentRound:int = -1Erstelle eine schwache Map-Variable für Sitzungen unter Verwendung der Klasse
round_info, um die Rundeninformationen mit der Sitzung zu speichern.Verse# Maps the current session to its associated round info. var RoundInfo:weak_map(session, round_info) = map{}Füge eine Getter-Funktion zum Abrufen der aktuellen Runde aus der schwachen Map-Variable für Sitzungen hinzu.
VerseGetRound<public>()<decides><transacts>:int= RoundInfo[GetSession()].CurrentRoundFüge eine Funktion hinzu, um die aktuelle Runde zu holen und sie in der schwache Map-Variable für Sitzungen zu speichern.
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 + 1Aktualisiere dein Verse-Gerät, um die neue Funktion
RecordCurrentRound zuverwenden undGetRoundaufzurufen, wenn du wissen musst, in welcher Runde du dich befindest.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)
Da diese Infos nun in einer schwachen Map-Variable für Sitzungen gespeichert sind, kannst die auf einfache Weise benutzerdefinierte Logik for Runden hinzufügen. Beispielsweise kannst du prüfen, ob es sich um die erste Runde handelt, und eine Lobby und einen Bereich zur Anzeige der Rangliste für die Spieler einrichten.
# Returns true if this is the first round of the game.
IsFirstRound<public>(RoundToCheck:int)<decides><transacts>:void=
RoundToCheck <= 0Du bist am Zug
Sieh dir Speedway Race with Verse Persistence Template an, um zu erfahren, wie du diesen Code in einem Rennspiel verwendest, um die Startreihenfolge der Spieler zu bestimmen.
Versuche nach dem Prüfen der Vorlage Folgendes:
Füge zusätzliche Rundeninformationen hinzu, z. B. welches Fahrzeug dem Spieler zugewiesen ist.
Teleportiere Spieler zu Beginn jeder Runde in verschiedene Bereiche der Karte.
Welche anderen Spiele fallen dir ein, die eine rundenbezogene Logik verwenden?
Vollständiger 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{}