In diesem Abschnitt erfährst du, wie du Teams und Klassen für Spieler festlegen und anpassen kannst.
Verwendete Geräte:
Teameinstellungen und -inventar
Verwende Teameinstellungen und -inventar-Geräte, um Teamnamen und Farben für die Anzeige auf der Anzeigetafel festzulegen.
Platziere ein Gerät für jedes Team in einem Bereich, der von den Spielern nicht gesehen werden kann. Um das Prop-Team einzurichten, konfiguriere die Benutzeroptionen entsprechend der nachstehenden Tabelle.
| Option | Wert | Beschreibung |
|---|---|---|
| Teamname | Props | Setzt eine Textzeichenfolge, die zur Identifizierung des Teams in der Anzeigetafel und den HUD-Elementen verwendet wird. |
| Teamfarbe | Himmelblau | Weist dem ausgewählten Team eine Farbe zu, die in der Anzeigetafel, im HUD und auf bestimmten Geräten verwendet wird. |
| Team | Team-Index: 1 | Bestimmt, für welches Team die Einstellungen auf diesem Gerät gelten. |
Um das Hunter-Team einzurichten, konfiguriere die Benutzeroptionen des anderen Geräts entsprechend der nachstehenden Tabelle.
| Option | Wert | Beschreibung |
|---|---|---|
| Teamname | Hunters | Legt eine Textzeichenfolge fest, die zur Identifizierung des Teams in der Anzeigetafel und den HUD-Elementen verwendet wird. |
| Teamfarbe | Orange | Weist dem ausgewählten Team eine Farbe zu, die in der Anzeigetafel, im HUD und auf bestimmten Geräten verwendet wird. |
| Team | Team-Index: 2 | Bestimmt, für welches Team die Einstellungen auf diesem Gerät gelten. |
Klassengestalter
Verwende einen Klassengestalter, um die soeben erstellten Teams zu ändern.
Platziere zwei Klassengestalter-Geräte, eines für jedes Team, in einem für die Spieler unsichtbaren Bereich. Um das Prop-Team einzurichten, konfiguriere die Benutzeroptionen entsprechend der nachstehenden Tabelle.
| Option | Wert | Beschreibung |
|---|---|---|
| Klassenname | Prop | Bestimmt den Namen dieser Klasse. |
| Klassenbeschreibung | Versteck dich vor den Huntern. Überleben. | Legt die Beschreibung dieser Klasse fest. |
| Klassenkennung | Class-Slot: 1 | Legt die eindeutige Kennung für diese Klasse fest. |
| Max. Kondition | 1 | Bestimmt, wie viel Kondition ein Spieler in Spielen maximal haben kann. Props werden mit einem Treffer eliminiert. |
| Gegenstandsliste | Prop-O-Matic | Legt die Liste der Gegenstände fest, die diese Klasse haben wird. |
| Gewährten Gegenstand ausrüsten | Erster Gegenstand | Legt fest, welcher Gegenstand in der Liste ausgerüstet werden soll. |
Um das Hunter-Team zu personalisieren, konfiguriere die Benutzeroptionen des anderen Geräts entsprechend der nachstehenden Tabelle.
| Option | Wert | Beschreibung |
|---|---|---|
| Klassenname | Hunter | Bestimmt den Namen dieser Klasse. |
| Klassenbeschreibung | Props finden. Eliminiere sie. | Legt die Beschreibung dieser Klasse fest. |
| Klassenkennung | Klassen-Slot: 2 | Legt die eindeutige Kennung für diese Klasse fest. |
| Gegenstandsliste | Blitzpistole | Legt die Liste der Gegenstände fest, die diese Klasse haben wird. |
| Gewährten Gegenstand ausrüsten | Erster Gegenstand | Legt fest, welcher Gegenstand in der Liste ausgerüstet werden soll. |
Klassenauswahl
Kombiniere den Klassengestalter mit der Klassenauswahl, um die von dir erstellten benutzerdefinierten Klassen und Teams zu verwalten.
Zusammen mit Verse bewirken die Einstellungen dieses Geräts, dass Spieler, die sich auf Klassen-Slot 1 befinden, beim Respawn auf Klassen-Slot 2 wechseln.
Platziere zwei Klassenauswahlen, einen für jede Mannschaft, in einem Bereich, den die Spieler nicht sehen können. Um das Prop-Team zu verwalten, verwende die Einstellungen aus der folgenden Tabelle, um die Benutzeroptionen für dieses Gerät zu konfigurieren.
| Option | Wert | Beschreibung |
|---|---|---|
| Klasse zum Wechseln | Klassen-Slot: 1 | Bestimmt, zu welcher Klasse der Spieler wechseln soll. |
| Sichtbar während des Spiels | False | Dieses Gerät ist während des Spiels nicht sichtbar. |
| Zonen-Audio | False | Bestimmt, ob der Klassenwähler Audioeffekte abspielen soll, wenn Spieler die Zone betreten. |
| Team zum Wechseln | Team-Index: 1 | Bestimmt, zu welchem Team der Spieler wechselt. |
| Gegenstände beim Wechsel löschen | True | Legt fest, ob Gegenstände aus dem Inventar des Spielers entfernt werden sollen, wenn der Wechsel durchgeführt wird. |
| Volumen im Spiel sichtbar | False | Bestimmt, ob das Volumen des Geräts im Spiel sichtbar ist. |
| VFX bei Aktivierung anzeigen | False | Bestimmt, ob das Gerät beim Wechsel der Klasse oder des Teams eines Spielers einen VFX-Effekt erzeugen soll. |
Um das Hunter-Team zu verwalten, verwende die Einstellungen aus der folgenden Tabelle, um die Benutzeroptionen für dieses Gerät zu konfigurieren.
| Option | Wert | Beschreibung |
|---|---|---|
| Klasse zum Wechseln | Class-Slot: 2 | Bestimmt, zu welcher Klasse der Spieler wechseln soll. |
| Team zum Wechseln | Team-Index: 2 | Bestimmt, zu welchem Team der Spieler wechselt. |
Teamfunktionalität mit Verse erstellen
Bei diesem Objektjagdspiel gibt es zwei Teams: Hunter und Props. Damit das Spiel funktioniert, müssen beide Teams einige der gleichen Dinge tun können. Zum Beispiel:
-
Hinzufügen von Spielern zu einem Team
-
Entfernen von Spielern aus einem Team
-
Anzeige von Informationen für Spieler über ihr Team
Um diese Funktionalität für beide Teams zu schaffen, ohne Code zu duplizieren, wirst du eine Klasse mit dem <abstract> Bezeichner zuweisen. Klassen mit dem abstract-Bezeichner sind dazu gedacht, eine Teilfunktionalität zu haben, die ihre Subklassen erben und auf der sie aufbauen. Du erstellst zunächst eine abstrakte Klasse namens base_team und gibst ihr die Funktionalität, die sowohl Prop- als auch Hunter-Teams teilen.
Dieses Dokument enthält Verse-Snippets, die zeigen, wie die für dieses Spiel erforderlichen Spielmechaniken ausgeführt werden. Befolge die folgenden Schritte und kopiere das vollständige Script in Schritt 6 dieses Tutorials.
Erstelle eine neue Verse-Datei in deinem Projekt mit dem Namen base_team.verse. Es handelt sich nicht um ein Verse-Gerät, du kannst es also als leere Verse-Datei erstellen.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Colors }
using { /Verse.org/Simulation }
log_team := class(log_channel){}
# Diese Klasse legt die Geräte fest, die für die verschiedenen Teams im Erlebnis benötigt werden.
# Diese Klasse ist abstrakt und kann daher nicht eigenständig verwendet werden. Sie muss von einer anderen Klasse geerbt werden.
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # Wird verwendet, um einen Spieler dem Team zuzuordnen
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # Dient der Vergabe von Punkten an Agents im Team
ScoreManager:score_manager_device = score_manager_device{}
@editable # Wird verwendet, um den Titel der Teamzuweisung anzuzeigen
TeamTitle:hud_message_device = hud_message_device{}
@editable # Dient zur Anzeige der Beschreibung der Teamzuweisung
TeamDescription:hud_message_device = hud_message_device{}
@editable # Wird verwendet, um Events zum Ausscheiden von Teammitgliedern (Objektteam) oder Feinden (Jägerteam) zu abonnieren
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# Dies ist ein Array von Agents im Team.
var TeamAgents<private>:[]agent = array{}
# Dieses Event wird signalisiert, wenn das TeamAgents-Array leer wird (was das Ende der Runde signalisiert).
TeamEmptyEvent:event() = event(){}
# Gibt das aktuelle TeamAgents-Array zurück
# Dies ist erforderlich, weil das TeamAgents-Array privat ist, sodass andere Klassen nicht direkt darauf zugreifen können.
GetAgents()<decides><transacts>:[]agent =
TeamAgents
# Gibt die Größe des TeamAgents-Arrays zurück
# Dies erfordert eine Funktion, weil das TeamAgents-Array privat ist, sodass andere Klassen nicht direkt darauf zugreifen können.
Count()<transacts>:int =
TeamAgents.Length
# Gibt einen Index im TeamAgents-Array eines Agent zurück, andernfalls schlägt es fehl
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# Ordnet den Agent dem Team zu und benachrichtigt den Spieler
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# Fügt einen Agent zu TeamAgents hinzu
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Agent wird zum Team hinzugefügt.")
set TeamAgents += array{AgentToAdd}
# Aktiviert HUD-Nachrichtenübermittler, um dem Spieler zu zeigen, in welchem Team er ist
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# Wenn ein Agent das Spiel verlässt, wird er aus dem Array TeamAgents entfernt und es wird auf das Ende der Runde geprüft
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # Verzögerung von 1 Spieltick, um sicherzustellen, dass der Spieler respawnt ist, bevor fortgefahren wird
RemoveAgentFromTeam(Agent)
# Entfernt einen Agent aus TeamAgents
# Wenn der entfernte Agent der letzte war, wird TeamEmptyEvent signalisiert.
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("{Count()} verbleibende(r) Agent(s) im Team")
if (Count() < 1):
Logger.Print("Keine Agents mehr im Team. Die Runde ist beendet.")
TeamEmptyEvent.Signal()
Nun, da du diese Klasse hast, kannst du die Klassen für das Prop-Team und das Hunter-Team erstellen. Da jede dieser Klassen von base_team erbt, gibt es ein paar Vorteile:
-
Der Code zur Implementierung der einzelnen Teams ist viel kürzer, da ihre gemeinsamen Funktionen und Daten bereits in
base_teamdefiniert sind. -
Es ist einfacher zu verstehen, welcher Code spezifisch für das Prop- und Hunter-Team ist, weil er in eigenen Klassen steht und nicht mit dem allgemeinen Code vermischt ist.
-
Das Hinzufügen weiterer Teams zum Spiel-Modus ist viel einfacher. Alle neuen Teams erben von
base_teamund der Code, der das neue Team unterscheidet, befindet sich in einer eigenen Klasse.
Denke daran, dass du keine Instanz einer Klasse mit dem Bezeichner <abstract> erstellen kannst. Du musst eine Klasse erstellen, die von der abstrakten Klasse erbt, und diese Klasse instanziieren.
Hunter-Team
Erstelle zunächst die Klasse für das Hunter-Team. Erstelle eine neue Verse-Datei in deinem Projekt mit dem Namen hunter_team.verse. Es handelt sich nicht um ein Verse-Gerät, du kannst es also als leere Verse-Datei erstellen.
Deklariere eine Klasse namens hunter_team. Sie sollte <concrete> sein und auch von base_team erben.
hunter_team := class<concrete>(base_team):
Wenn man eine Klasse <concrete> macht, müssen alle Felder der Klasse einen Standardwert haben. Siehe Bezeichner und Attribute um mehr zu erfahren.
Nachfolgend findest du den vollständigen Code für das Script hunter_team.verse.
Die Klasse hunter_team hat zwei Funktionen mit dem gleichen Namen wie die Funktionen in der Basis-Klasse base_team. Dies ist erlaubt, da beide den Bezeichner <override> haben. Das heißt, wenn diese Funktionen auf einer Instanz von hunter_team aufgerufen werden, wird die Version in der Klasse hunter_team verwendet.
Zum Beispiel wird im folgenden Code die Version von InitializeAgent(), die in hunter_team definiert ist, verwendet, da sie die gleichnamige Funktion in base_team überschreibt. Vergleiche dies mit dem Aufruf von Count(), der die in base_team definierte Version verwendet, da es keine Überschreibungsfunktion gibt.
HunterTeam:hunter_team = hunter_team{}
# Verwendet die Funktion von hunter_team
HunterTeam.InitializeAgent(StartingHunterAgent)
# Verwendet die Funktion von base_team
HunterTeam.Count()
Die beiden überschriebenen Funktionen verwenden ebenfalls (super:). Dadurch können sie die Version der in base_team definierten Funktionen aufrufen, da base_team die Superklasse von hunter_team ist. Im Fall von InitializeAgent() und EliminateAgent() verwenden beide Logger.Print(), um etwas in das Log zu schreiben. Dann rufen sie ihre jeweiligen Funktionen von base_team auf. Das bedeutet, dass die Funktionen genau so funktionieren wie die Versionen in base_team, mit Ausnahme der Aufrufe von Logger.Print().
Siehe Subklasse, um mehr über <override> und (super:) zu erfahren
Prop-Team
Erstelle nun die Klasse für das Prop-Team. Erstelle eine neue Verse-Datei in deinem Projekt mit dem Namen prop_team.verse. Es handelt sich nicht um ein Verse-Gerät, du kannst es also als leere Verse-Datei erstellen.
Für die Mitglieder des Prop-Teams musst du mehr verwalten. Sie haben Herzschlag-Effekte, die auf der Grundlage eines Timers und der zurückgelegten Entfernung gestartet und gestoppt werden müssen. Wenn sie ausscheiden, müssen sie ebenfalls in das Hunter-Team versetzt werden.
Um die Mitglieder des Prop-Teams zu verwalten, verwendest du die Methode RunPropGameLoop(). Du kannst dir diese Methode als den Manager für die gesamte Reise eines Props durch das Spiel vorstellen. Ab dem Spawnen bis zum Ausscheiden oder Verlassen des Spiels wird diese Methode für jedes Prop-Team-Mitglied angewendet.
# Wenn der Prop-Agent sich nicht mehr bewegt, wird eine Race-Bedingung gestartet, ob der Prop-Agent sich über MinimumMoveDistance hinaus bewegt, der Herzschlag-Timer abläuft oder der Prop-Agent eliminiert wird.
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Starten der Spielschleife für den Prop-Agent.")
# Ewige Schleife durch das Prop-Verhalten, bis der Prop-Agent eliminiert wird oder der Spieler die Sitzung verlässt
race:
PropAgent.AwaitNoLongerAProp()
loop:
# Warte, bis sich der Prop-Agent weniger als die Mindestdistanz bewegt, und fahre dann fort.
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# Bis sich der Prop-Agent über die Mindestdistanz hinaus bewegt, wird der Countdown bis zum Heartbeat heruntergezählt und dann der Heartbeat auf unbestimmte Zeit abgespielt.
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # Sobald der Race-Ausdruck abgeschlossen ist (der Prop-Agent bewegt sich), startet die Schleife erneut.
RunPropGameLoop() hat einen Parameter, PropAgent. Dies ist eine Konstante, die einen Spieler des Prop-Teams darstellt. Sie hat auch den Bezeichner <suspends>, was bedeutet, dass sie Zeit zum Beenden braucht. In diesem Fall wird sie erst beendet, wenn der übergebene PropAgent nicht mehr zum Prop-Team gehört.
Die gesamte Funktionalität dieser Methode ist in einem Race-Ausdruck enthalten. Das bedeutet, dass die Methode erst dann abgeschlossen ist, wenn einer der Ausdrücke innerhalb dieses Rennens abgeschlossen ist. Diese Ausdrücke sind:
-
PropAgent.AwaitNoLongerAProp() -
loop
Der Schleifenausdruck innerhalb dieses Rennens wird niemals enden. Er ist absichtlich unendlich. Das bedeutet, dass AwaitNoLongerAProp() die Methode ist, die immer das Rennen gewinnen und die Methode abschließen wird. Die Verwendung von "race" auf diese Weise ist so, als würde man seinem Programm sagen, es solle einen bestimmten Satz Code immer wieder ausführen, bis etwas passiert. Siehe Race, um mehr über diesen kraftvollen Ausdruck zu erfahren.
Mit diesem Code gewinnt AwaitNoLongerAProp() das Rennen.
# Schleife, bis der Prop-Agent nicht mehr Teil des PropAgents-Arrays ist. Die Entfernung erfolgt, wenn der Prop-Agent eliminiert und in einen Jäger verwandelt wird oder wenn der Spieler die Sitzung verlässt.
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[PropAgent]):
Logger.Print("Prop-Agent-Verhalten wird abgebrochen.")
break
Sleep(0.0) # Mit dem nächsten Spieltick fortfahren
Diese Methode prüft ständig, ob PropAgent im Prop-Team ist. Sie beginnt mit einer Schleife, die so lange läuft, bis not FindOnTeam[PropAgent] erfolgreich ist, und bricht dann ab, um die Methode zu beenden. Siehe Schleife und Pause, um mehr darüber zu erfahren.
FindOnTeam[] ist eine fehlbare Funktion, die in base_team deklariert wird. Sie ist erfolgreich, wenn der PropAgent im Prop-Team gefunden wird. Du musst jedoch den not-Operator verwenden, weil du die Schleife nur verlassen willst, wenn PropAgent nicht im Prop-Team gefunden wird. Siehe Operatoren, um mehr über not zu erfahren.
Zum Schluss musst du ein Sleep(0.0) am Ende der Schleife hinzufügen. Dadurch wird sichergestellt, dass die Schleife einmal durchläuft und dann zur nächsten Simulationsaktualisierung weitergeht. Du musst diese Prüfung nicht öfter durchführen, also Sleep(0.0) wird hinzugefügt, um die Performance zu verbessern. Weitere Informationen dazu findest du auf der Verse-API-Referenzseite für Schlaf.
Nun, da du weißt, wie AwaitNoLongerAProp() funktioniert, kannst du die Endlosschleife schreiben, die gegen sie in RunPropGameLoop() läuft.