Dieser Abschnitt enthält den vollständigen Code, der zu den von dir erstellten Verse-Dateien hinzugefügt werden muss.
Vollständiger Code
Es gibt mehrere Verse-Dateien in diesem Projekt.
-
heartbeat.verse: Unten findest du den vollständigen Code der Datei.
-
base_team.verse: Unten findest du den vollständigen Code der Datei.
-
hunter_team.verse: Unten findest du den vollständigen Code der Datei.
-
prop_team.verse: Unten findest du den vollständigen Code der Datei.
-
round_timer.verse: Unten findest du den vollständigen Code der Datei.
-
waiting_for_more_players.verse: Unten findest du den vollständigen Code der Datei.
heartbeat.verse
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Colors }
using { /Verse.org/Simulation }
log_heart_beat := class(log_channel){}
# Diese Nachrichten werden verwendet, um einem Prop-Agenten mit einer Nachricht mitzuteilen (oder diese zu verbergen), wenn er sich bewegen muss, damit sein Herzschlag nicht sichtbar wird.
HeartBeatWarningMessage<localizes>(Time:int):message = "Herzschlag in {Time} Sekunden. Beweg dich!"
HeartBeatWarningClear<localizes>:message = ""
# Diese Klasse gibt die bearbeitbaren Eigenschaften für den Herzschlag für das prop_hunt-Gerät frei.
heart_beat := class<concrete>():
Logger:log = log{Channel:=log_heart_beat}
@editable # Die Anzahl der Sekunden, bevor sich ein Prop-Agent bewegen muss, bevor der Herzschlag seine Position verrät
MoveTime:float = 15.0
@editable # Die verbleibenden Sekunden, bevor die Herzschlag-Warnung erscheint Sollte nicht > als HeartBeatTimer sein.
WarningTime:float = 5.0
@editable # Ein Array von Herzschlag-VFX-Geräten Es gibt eines pro Spieler.
AgentVFX:[]heartbeat_vfx = array{}
@editable # Das Audioplayer-Gerät, das für die Wiedergabe der Herzschlag-Soundeffekte (SFX) verwendet wird
SFXPlayer:radio_device = radio_device{}
# Diese Karte ordnet jedem Prop-Agent eine Benutzeroberfläche für die Anzeige der Herzschlag-Warnung zu.
var WarningUI:[agent]heartbeat_warning_ui = map{}
# Behält im Auge, wie viele Spieler einen aktiven Herzschlag haben, damit wir das SFX-Gerät verwalten können
var NumberOfHeartBeats:int = 0
# Richtet die Herzschlag-Benutzeroberfläche für den Agenten ein
SetUpUI(PropAgent:agent):void =
if:
not WarningUI[PropAgent]
AsPlayer := player[PropAgent]
PlayerUI := GetPlayerUI[AsPlayer]
then:
UIData:heartbeat_warning_ui = heartbeat_warning_ui{}
UIData.CreateCanvas()
PlayerUI.AddWidget(UIData.Canvas, player_ui_slot{ZOrder := 1})
if (set WarningUI[PropAgent] = UIData) {}
# Aktiviert die Herzschlag-VFX und SFX für den angegebenen Spieler
Enable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
if:
# Ruft den Charakter ab, der verwendet wird, um die Position des Prop-Agent in der Szene zu finden
Character := PropAgent.GetFortCharacter[]
then:
# Setzt die Position der Herzschlag-VFX auf die Position des Prop-Agent
HeartBeatVFXData.Activate(Character.GetTransform())
# Inkrementiert die Herzschlag-Zahl; wenn dies der erste Herzschlag ist, müssen wir den Ton abspielen, um ihn zu starten
set NumberOfHeartBeats += 1
if (NumberOfHeartBeats = 1) then SFXPlayer.Play()
# Registriert den Prop-Agent beim Audioplayer, sodass der Herzschlag-Ton von dieser Position aus abgespielt wird
SFXPlayer.Register(PropAgent)
else:
Logger.Print("Charakter, Index oder HeartBeatVFXData nicht gefunden. Kann Herzschlag nicht starten.")
# Löscht die Herzschlag-VFX und SFX für den angegebenen Prop-Agent
Disable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
Logger.Print("Herzschlag wird deaktiviert.")
# Deaktiviert die visuellen VFX-Effekte
HeartBeatVFXData.Deactivate()
# Hebt die Registrierung des Prop-Agent vom Audioplayer-Gerät auf, wodurch der Herzschlag-Ton gestoppt wird
SFXPlayer.Unregister(PropAgent)
# Dekrementiert den Herzschlagzähler. Dieser Zähler sollte nie unter 0 fallen.
set NumberOfHeartBeats -= 1
if (NumberOfHeartBeats < 0) then set NumberOfHeartBeats = 0
# Löscht die Herzschlag-VFX und SFX für alle Prop-Agents
DisableAll():void =
Logger.Print("Alle Herzschläge werden deaktiviert.")
# Iteriert durch alle VFX und verschiebt sie auf 0,0,0
for (HeartBeatVFXDevice : AgentVFX):
HeartBeatVFXDevice.Deactivate()
# Hebt die Registrierung aller Spieler aus dem Herzschlag-Audio auf
SFXPlayer.UnregisterAll()
# Reinitialisiert den Herzschlag-Zähler auf 0
set NumberOfHeartBeats = 0
# Die Klasse heartbeat_warning_ui enthält eine Struktur von Daten, um die UI-Canvas und text_block pro Spieler zu verfolgen, sowie die Funktion, um einen neuen Herzschlag-Warnung-UI-Canvas zu erstellen.
heartbeat_warning_ui := class:
var Canvas:canvas = canvas{}
var Text:text_block = text_block{}
# Erzeugt die UI-Canvas für die Warnmeldung
CreateCanvas():void =
set Text = text_block{DefaultTextColor := NamedColors.White, DefaultShadowColor := NamedColors.Black}
set Canvas = canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := vector2{X := 0.5, Y := 0.75}, Maximum := vector2{X := 0.5, Y := 0.75}}
Versätze := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := vector2{X := 0.5, Y := 1.0}
SizeToContent := true
Widget := Text
# Die Klasse heartbeat_vfx enthält eine Struktur von Daten, um die Stamm- und vfx_spawner_device-Objekte der VFX pro Spieler zu verfolgen, sowie die Funktionen, um die VFX auf eine Position zu setzen oder sie zurückzusetzen.
heartbeat_vfx := class<concrete>:
@editable # Das VFX-Gerät für jeden Herzschlag
VFXDevice:vfx_spawner_device = vfx_spawner_device{}
# Dieser Versatz wird verwendet, um den Herzschlag über dem Kopf des Prop-Agent zu positionieren
HeartBeatVFXOffset:vector3 = vector3{X := 0.0, Y := 0.0, Z := 110.0}
# Setzt die Position der Herzschlag-VFX und aktiviert dann die VFX
Activate(Transform:transform):void =
VFXPosition := Transform.Translation + HeartBeatVFXOffset
if (VFXDevice.TeleportTo[VFXPosition, Transform.Rotation]):
VFXDevice.Enable()
# Deaktiviert die VFX und blendet die Herzschlaganzeige aus
Deactivate():void =
VFXDevice.Disable()
base_team.verse
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()
hunter_team.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation }
# Wird von der Klasse base_team geerbt. Die Klasse hunter_team enthält die Gerätedefinitionen und -funktionen in Bezug auf das Jägerteam und seine Agents.
hunter_team := class<concrete>(base_team):
@editable # Für jeweils n Spieler wird in jeder Runde ein Hunter-Agent erstellt. Beispiel: HunterTeamPerNumberOfPlayers = 5.0 ist 1 pro 5 Spieler. Wenn Spieler = 6, werden 2 Hunter-Agents erstellt.
HunterAgentPerNumberOfPlayers:float = 5.0 # Ein Minimum von 1:1 wird erzwungen, um sicherzustellen, dass mindestens 1 Prop-Agent erstellt wird.
@editable # Die Anzahl Sekunden vor dem Spawnen der Hunter-Agents, wodurch die Prop-Agents einen Vorsprung haben, um sich zu verstecken
SpawnDelay:float = 15.0
@editable # Die maximalen Basispunkte, die ein Hunter-Agent für das Eliminieren eines Prop-Agent erhält. Diese Punkte werden durch die Anzahl der verbleibenden Prop-Agents geteilt.
MaxEliminationScore:int = 5000
@editable # Das Timer-Gerät wird verwendet, um Objekten eine Schonfrist zum Verstecken zu geben.
WaitTimer:timer_device = timer_device{}
# Setzt den Agent auf einen Hunter-Agent
InitializeAgent<override>(NewHunterAgent:agent):void =
Logger.Print("Ein neuer Hunter-Agent wird festgelegt.")
(super:)InitializeAgent(NewHunterAgent)
# Wenn ein Hunter-Agent das Spiel verlässt, wird er aus dem Array HunterAgents entfernt und es wird auf das Ende der Runde geprüft.
# Beachte, dass wir diese Funktion außer Kraft setzen, weil wir hier keine zusätzlichen Daten übergeben müssen, wie es beim Objekt-Team der Fall ist.
EliminateAgent<override>(HunterAgent:agent)<suspends>:void =
Logger.Print("Hunter-Agent eliminiert.")
(super:)EliminateAgent(HunterAgent)
prop_team.verse
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 }
# Mit dieser Meldung wird allen Spielern in einer Runde die Anzahl der verbleibenden Objekte ausgegeben.
PropAgentsRemainingMessage<localizes>(Count:int):message = "{Count} Prop(s) verbleibend"
# Wird von der Klasse base_team geerbt. Die Klasse prop_team enthält die Gerätedefinitionen und -funktionen in Bezug auf das Objektteam und seine Agents.
# In dieser Klasse findet sich vor allem auch das Herzschlag-Verhalten eines Prop-Agent.
prop_team := class<concrete>(base_team):
@editable # Die Punkte, die ein Prop-Agent pro Sekunde erhält
ScorePerSecond:int = 10
@editable # Der Mindestabstand, um den sich ein Prop-Agent bewegen muss, um den Herzschlag-Timer zurückzusetzen
MinimumMoveDistance:float = 100.0
@editable # Das Timer-Gerät, mit dem ein Objekt Punkte erhält
ScoreTimer:timer_device = timer_device{}
@editable # Dieses Tracker-Gerät wird verwendet, um die verbleibende Anzahl Objekte auf dem Bildschirm anzuzeigen.
PropsRemainingTracker:tracker_device = tracker_device{}
@editable # Ruft die Eigenschaften aus der Klasse heart_beat ab
HeartBeat:heart_beat = heart_beat{}
# Setzt den Agent auf einen Prop-Agent und weist eine Herzschlag-Warn-UI zu
InitializeAgent<override>(NewPropAgent:agent):void =
Logger.Print("Es wird ein neuer Prop-Agent festgelegt.")
(super:)InitializeAgent(NewPropAgent)
# Wenn der PropScoreTimer abgelaufen ist, erhalten alle Prop-Agents Punkte. PropInstigator ist erforderlich, damit das Event-Abonnement nicht verwendet wird.
OnPropsScore(PropInstigator:?agent):void =
if (PropAgents := GetAgents[]):
for (PropAgent : PropAgents):
ScoreManager.Activate(PropAgent)
# Wenn ein Prop-Agent eliminiert wird oder das Spiel verlässt, wird er aus dem Array PropAgents entfernt und es wird auf das Ende der Runde geprüft.
# Beachte, dass dies nicht überschrieben wird, da wir alle Spieler an die Funktion übergeben, um die Meldung für die verbleibenden Objekte zu aktualisieren.
EliminateAgent<override>(PropAgent:agent)<suspends>:void =
Logger.Print("Prop-Agent eliminiert.")
(super:)EliminateAgent(PropAgent)
# Aktualisiere die verbleibende Anzahl der Objekte.
UpdatePropsRemainingTracker()
# Aktualisiert den Werte des Tracker-Geräts, das die Anzahl der verbleibenden Objekte anzeigt
UpdatePropsRemainingTracker():void =
PropsRemainingTracker.SetValue(Count())
# 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 Herzschlag heruntergezählt und dann der Herzschlag 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.
# 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
# Schleife, bis der Agent sich weniger als MinimumDistance bewegt
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Überprüfung, ob der Agent sich weniger als die Mindestdistanz bewegt hat")
# Ruft die Anfangsposition des Agent vom Charakter des Agent in der Szene ab
if (Tracked := PropAgent.GetFortCharacter[]):
var StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Ermittelt die Position des Agent im nächsten Spieltick.
NewPosition := Tracked.GetTransform().Translation
# Wenn der Abstand der neuen Position von der Startposition kleiner als MinimumDistance ist, hat sich der Agent nicht bewegt und wir brechen die Schleife ab.
if (Distance(StartPosition, NewPosition) < MinimumDistance):
Logger.Print("Der Agent hat sich weniger als den Mindestabstand bewegt.")
break
# Andernfalls setzen wir StartPosition zurück, um sicherzustellen, dass sich der Spieler von der neuen Position aus bewegt.
else:
set StartPosition = NewPosition
# Schleife, bis der Agent sich mehr als die MinimumDistance bewegt
(PropAgent:agent).AwaitStartMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Überprüfung, ob der Agent sich um mehr als die Mindestdistanz bewegt hat")
# Ruft die Anfangsposition des Agent vom Charakter des Agent in der Szene ab
if (Tracked := PropAgent.GetFortCharacter[]):
StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Ermittelt die Position des Agent im nächsten Spieltick.
NewPosition := Tracked.GetTransform().Translation
# Wenn der Abstand der neuen Position von der Startposition größer oder gleich MinimumDistance ist, hat sich der Agent bewegt und wir unterbrechen die Schleife.
if (Distance(StartPosition, NewPosition) >= MinimumDistance):
Logger.Print("Der Agent hat sich mehr als oder gleich der Mindestdistanz bewegt.")
break
# Verzögert, bis HeartBeatWarningTime beginnen soll. Zählt dann um HeartBeatWarningTime herunter und setzt den Countdown-Text. Löscht den Text, wenn zurückgestellt.
CountdownTimer(PropAgent:agent)<suspends>:void =
Logger.Print("Herzschlag-Countdown beginnt.")
if (UIData := HeartBeat.WarningUI[PropAgent]):
Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # Schläft so lange, bis die Warnung erscheint.
Logger.Print("Herzschlag-Warnung beginnt.")
var WarningTimeRemaining:int = 0
if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {}
# Eine Zurückstellung erfolgt, wenn die Funktion abgeschlossen oder abgebrochen wird, z. B. wenn sie die Race-Bedingung nicht zuerst abschließt.
# In diesem Fall wird der Warntext gelöscht, wenn der Countdown abgelaufen ist oder wenn sich der Prop-Agent bewegt, bevor der Countdown abgelaufen ist.
defer:
UIData.Text.SetText(HeartBeatWarningClear)
# Setzt den Warntext auf die verbleibende Zeit, wartet eine Sekunde und dekrementiert dann die verbleibende Zeit. Wenn der Countdown beendet ist, wird die Schleife unterbrochen.
loop:
Logger.Print("Herzschlag in {WarningTimeRemaining} Sekunden")
UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining))
Sleep(1.0)
set WarningTimeRemaining -= 1
if (WarningTimeRemaining <= 0):
break
else:
Logger.Print("UIData nicht gefunden.")
# Schaltet die Herzschlag-VFX und -SFX ein. Wartet unendlich, bis eine Zurückstellung erfolgt, und deaktiviert dann den Herzschlag-VFX und -SFX
(PropAgent:agent).StartHeartbeat()<suspends>:void =
Logger.Print("Herzschlag wird gespawnt.")
# Speichert die Herzschlagdaten, damit wir sie später bei der Zurückstellung übergeben können, nachdem der PropAgent zerstört wurde oder das Spiel verlässt
var HeartBeatVFXData:heartbeat_vfx = heartbeat_vfx{}
if:
# Ruft den Index des Prop-Agent im PropAgents-Array ab, um dann auf den entsprechenden Herzschlag-VFX-Actor zuzugreifen
Index := FindOnTeam[PropAgent]
set HeartBeatVFXData = HeartBeat.AgentVFX[Index]
then:
HeartBeat.Enable(PropAgent, HeartBeatVFXData)
# Wenn diese Funktion abgebrochen wird, weil sich der Prop-Agent bewegt, eliminiert wird oder der Spieler die Sitzung verlässt, wird der Herzschlag deaktiviert.
defer:
HeartBeat.Disable(PropAgent, HeartBeatVFXData)
Sleep(Inf) # Sleep erst stoppen, wenn der Race-Ausdruck abgeschlossen ist.
round_timer.verse
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_round_timer_device := class(log_channel){}
# Eine Int, die Werte zwischen den angegebenen Bereichen zulässt. Dieser Typ ist durch player_ui_slot.ZOrder erforderlich.
round_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Mit dieser Meldung wird die verbleibende Zeit bis zum Rundenende ausgegeben.
TimeRemainingMessage<localizes>(Minutes:string, Seconds:string):message = "{Minutes}:{Seconds}"
<#
Diese Klasse enthält die gesamte Logik für die Verwaltung der Rundenzeit und die Anzeige der Zeit auf dem Bildschirm.
Du kannst dieses Gerät mit einem round_settings_device verwenden, um die Runde zu beenden.
Dieses Gerät verwaltet die Zeit ohne die Verwendung eines Timers.
So verwendest du diese Klasse:
1) Füge die Datei zu deinem Projekt hinzu.
2) Kompiliere den Verse-Code über das Verse-Menü in der Werkzeugleiste.
3) Ziehe das Gerät über den Ordner Inhalt-/Kreativmodus-Geräte deiner Insel im Inhaltsbrowser auf deine Insel.
4) Nimm die Klasse waiting_for_more_players in einem andere Verse-Script mit folgenden Angaben auf:
@editable
RoundTimer:round_timer = round_timer{}
5) Kompiliere den Verse-Code über das Verse-Menü in der Werkzeugleiste.
6) Verbinde das Gerät, das du in Schritt 3 erstellt hast, mit dem Verse-Gerät.
7) Starte den Rundentimer mit dem folgenden Verse-Code:
RoundTimer.Start()
8) Starte den Timer mit den entsprechenden Funktionen neu oder stoppe ihn entsprechend.
9) Warte mit diesem Code auf den Start des Timers:
RoundTimer.AwaitStart()
10) Warte mit diesem Code auf den Abschluss des Timers:
RoundTimer.AwaitEnd()
Rufe die Funktion EndRound auf einem round_settings_device auf, um die Spielrunde zu beenden.
#>
round_timer := class(creative_device):
Logger:log = log{Channel:=log_prop_hunt_device}
@editable # Die Zeit in Minuten, die eine Runde dauert
RoundTimeInMinutes:float = 5.0
@editable # Die horizontale und vertikale Position der Timer-UI auf dem Bildschirm. X 0-1 ist links-rechts und Y 0-1 ist oben-unten.
UIPosition:vector2 = vector2{X:= 0.98, Y:=0.13}
@editable # Die horizontale und vertikale Position der Timer-UI auf dem Bildschirm. X 0-1 ist links-rechts und Y 0-1 ist oben-unten.
UIAlignment:vector2 = vector2{X := 1.0, Y := 0.0}
@editable # Die ZOrder der UI im Vergleich zu anderen UI-Elementen
UIZOrder:round_int_clamped = 4
# Signalisiert, wann die Runde gestartet wurde
RoundStarted:event() = event(){}
# Signalisiert, wenn die Runde kurz vor dem Ende steht
RoundEndedEvent:event() = event(){}
# Diese Karte ordnet ein Textfeld für die Anzeige der Zeit für jeden Spieler zu.
var TimeRemainingTextBlocks:[player]text_block = map{}
# Die verbleibende Zeit bis zum Abschluss der Runde als Ganzzahl
var TimeRemainingInSeconds:int = 0
# Wartet, bis der Rundentimer gestartet wurde
AwaitStart()<suspends>:void =
RoundStarted.Await()
Logger.Print("Rundentimer gestartet")
# Wird verwendet, um den Rundentimer zu starten
Start():void =
Logger.Print("Rundentimer wird gestartet.")
RoundStarted.Signal()
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
spawn{ Running() }
# Startet den Timer für RoundTime neu
Restart():void =
Logger.Print("Rundentimer wird neu gestartet.")
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
# Führt die Timer-Logik aus
Running()<suspends>:void =
Logger.Print("Rundentimer läuft.")
loop:
UpdateTimeUI()
Sleep(1.0)
# Verringert TimeRemaining um 1 Sekunde und prüft dann, ob die Zeit abgelaufen ist. Ist dies der Fall, endet die Runde.
set TimeRemainingInSeconds -= 1
if (TimeRemainingInSeconds < 0):
Stop()
break
# Stoppt den Timer und beendet die Runde
Stop():void =
Logger.Print("Rundentimer wird beendet.")
# Wir rufen einen der in der Szene verbleibenden Spieler ab, um die Runde zu beenden.
Players:[]player = GetPlayspace().GetPlayers()
if (Instigator := Players[0]):
RoundEndedEvent.Signal()
# Wartet, bis der Rundentimer fast abgelaufen ist
AwaitEnd()<suspends>:void =
RoundEndedEvent.Await()
Logger.Print("Rundentimer beendet.")
# Akzeptiert einen Zeitwert in Minuten und gibt den Wert in Sekunden zurück
GetRoundTimeInSeconds():int =
var InSeconds:int = 0
if (set InSeconds = Round[RoundTimeInMinutes * 60.0]) {}
InSeconds
# Wenn der Timer abgeschlossen wird, wird die verbleibende Zeit aktualisiert und geprüft, ob die Zeit abgelaufen ist.
UpdateTimeUI():void =
# Setzt Minuten auf TimeRemainingInSeconds/60 ohne Rest
var Minutes:int = 0
if (set Minutes = Floor(TimeRemainingInSeconds / 60)) {}
# Setzt Sekunden auf den Rest von TimeRemainingInSeconds/60
var Seconds:int = 0
if (set Seconds = Mod[TimeRemainingInSeconds, 60]) {}
# Wandelt Minuten und Sekunden in Strings um
MinutesAsString := string("{Minutes}")
# Wenn Sekunden < 10, müssen wir 0 vor den Wert setzen, damit er als :0# und nicht als :# angezeigt wird.
SecondsAsString := if (Seconds < 10) then Join(array{string("{0}"),string("{Seconds}")},string()) else string("{Seconds}")
# Iteriert durch alle Spieler und prüft, ob sie TimeRemainingTextBlock aufweisen. Wenn nicht, erhalten sie einen. Dann wird der Text aktualisiert.
Players:[]player = GetPlayspace().GetPlayers()
for (Player : Players):
var TextBlock:text_block = text_block{}
if (set TextBlock = TimeRemainingTextBlocks[Player]) {}
else:
set TextBlock = SetUpTimeRemainingUI(Player)
TextBlock.SetText(TimeRemainingMessage(MinutesAsString, SecondsAsString))
# Akzeptiert einen Spieler und fügt dann einen Rundentimer-UI-Canvas zu ihrem Bildschirm hinzu und speichert ihren TimeRemainingTextBlock für die spätere Aktualisierung
SetUpTimeRemainingUI(Player:player):text_block =
Logger.Print("Hinzufügen der Rundentimer-UI zu einem Spieler")
# Dies ist der text_block, der den Text mit der verbleibenden Zeit auf dem Bildschirm ausgibt.
TextBlock:text_block = text_block:
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
if (PlayerUI := GetPlayerUI[Player]):
if (set TimeRemainingTextBlocks[Player] = TextBlock) {}
# Dies ist der Canvas, der den text_block auf dem Bildschirm hält und positioniert.
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Versätze := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# Der Canvas wird dem Spieler zugewiesen.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Der text_block wird zurückgegeben, sodass er unter der Karte gespeichert und später aktualisiert werden kann, wenn die Zeit heruntertickt.
return TextBlock
waiting_for_more_players.verse
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_waiting_for_more_players_device := class(log_channel){}
# Eine Int, die Werte zwischen den angegebenen Bereichen zulässt. Dieser Typ ist durch player_ui_slot.ZOrder erforderlich.
waiting_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Mit dieser Meldung wird die Anzahl der Spieler ausgegeben, die erforderlich sind, bevor eine Runde beginnen kann.
WaitingForMorePlayersMessage<localizes>(Count:int):message = "Es wird auf {Count} weitere(n) Spieler gewartet."
# Diese Klasse dient der Anzeige, wie viele Spieler benötigt werden, um die Runde zu starten.
waiting_for_more_players_ui := class:
var Canvas:canvas
var TextBlock:text_block
<#
Diese Klasse enthält die gesamte Logik für das Festlegen einer Mindestanzahl Spieler und die Prüfung, ob genug vorhanden sind, um die Runde zu starten.
So verwendest du diese Klasse:
1) Füge die Datei zu deinem Projekt hinzu.
2) Kompiliere den Verse-Code über das Verse-Menü in der Werkzeugleiste.
3) Ziehe das Gerät über den Ordner Inhalt-/Kreativmodus-Geräte deiner Insel im Inhaltsbrowser auf deine Insel.
4) Verbinde ein Timer-Gerät mit der Eigenschaft WaitingForMorePlayersTimer dieses Geräts.
5) Nimm die Klasse waiting_for_more_players in einem andere Verse-Script mit folgenden Angaben auf:
@editable
WaitingForMorePlayers:waiting_for_more_players = waiting_for_more_players{}
6) Kompiliere den Verse-Code über das Verse-Menü in der Werkzeugleiste.
7) Verbinde das Gerät, das du in Schritt 3 erstellt hast, mit dem Verse-Gerät und der Eigenschaft, die du in Schritt 6 freigegeben hast.
8) Warte auf die CheckForMinimumNumberOfPlayers-Funktionen, indem du ihnen einen Spieler übergibst. Zum Beispiel:
Players = GetPlayspace().GetPlayers()
CheckForMinimumNumberOfPlayers(Players)
9) Setze unter IslandSettings den Spielstart-Countdown auf 0,0.
#>
waiting_for_more_players := class(creative_device):
Logger:log = log{Channel:=log_waiting_for_more_players_device}
@editable # Die Mindestanzahl Spieler, die im Match erforderlich ist, damit eine Runde gestartet wird
MinimumNumberOfPlayers:int = 2
@editable # Die horizontale und vertikale Position der Timer-UI auf dem Bildschirm. X 0-1 ist links-rechts und Y 0-1 ist oben-unten.
UIPosition:vector2 = vector2{X:= 0.5, Y:=0.4}
@editable # Die horizontale und vertikale Position der Timer-UI auf dem Bildschirm. X 0-1 ist links-rechts und Y 0-1 ist oben-unten.
UIAlignment:vector2 = vector2{X := 0.5, Y := 0.5}
@editable # Die ZOrder der UI im Vergleich zu anderen UI-Elementen
UIZOrder:waiting_int_clamped = 3
@editable # Dieser Timer wird verwendet, um einen Countdown für den Start der Runde durchzuführen, nachdem darauf gewartet wurde, dass Spieler dem Match beitreten.
WaitingForMorePlayersTimer:timer_device = timer_device{}
# Diese Karte ordnet jedem Spieler einen UI-Canvas für die Anzeige der Anzahl der Spieler zu, die erforderlich sind, um eine Runde zu starten.
var WaitingForMorePlayersUI:[player]?waiting_for_more_players_ui = map{}
# Überprüfe, ob es genug Spieler gibt, um die Runde zu beginnen. Warte andernfalls, bis die Anzahl der Spieler >= MinimumNumberOfPlayers ist.
WaitForMinimumNumberOfPlayers(Players:[]player)<suspends>:[]player =
Logger.Print("Es wird darauf gewartet, ob genug Spieler zum Start der Runde vorhanden sind.")
# Erstelle eine neue Variable, damit ich sie modifizieren kann, wenn weitere Spieler beitreten. Sie wird mit einem Array der Spieler initialisiert, das der Funktion übergeben wird.
var PlayersWaiting:[]player = Players
# Wenn die Anzahl der Spieler kleiner als das erforderliche Minimum für den Start der Runde ist ...
if (PlayersWaiting.Length < MinimumNumberOfPlayers):
loop: # Schleife, bis die Anzahl der Spieler größer als oder gleich dem erforderlichen Minimum ist
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} werden zum Start der Runde benötigt.")
# Aktualisiere die UI zum Warten auf weitere Spieler
DisplayWaitingForMorePlayers(PlayersWaiting)
Sleep(2.0) # Warte, um zu prüfen, ob weitere Spieler dem Match beitreten, und prüfe dann, ob ausreichend Spieler für den Start der Runde vorhanden sind
set PlayersWaiting = GetPlayspace().GetPlayers()
if (PlayersWaiting.Length >= MinimumNumberOfPlayers):
# Wenn genug Spieler vorhanden sind, lösche die UI zum Warten auf weitere Spieler.
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} Spieler in der Runde. Der Rundenstart wird vorbereitet.")
ClearWaitingForMorePlayers()
# Unterbrich dann die Schleife.
break
# Starte den Rundenstart-Countdown und warte, bis der Countdown abgelaufen ist.
WaitingForMorePlayersTimer.Start()
WaitingForMorePlayersTimer.SuccessEvent.Await()
Logger.Print("Die Runde wird gestartet.")
# Gib die Liste der Spieler in der Sitzung zurück.
return PlayersWaiting
# Zeigt jedem Spieler die UI-Meldung „Es wird auf weitere Spieler gewartet.“ an, wenn sie nicht bereits angezeigt wird. Aktualisiert den Spieler-Zähler für alle Spieler.
DisplayWaitingForMorePlayers(Players:[]player):void =
PlayersNeededCount := MinimumNumberOfPlayers - Players.Length
Logger.Print("{Players.Length} Spieler in der Runde. Es wird darauf gewartet, dass noch {PlayersNeededCount} Spieler beitritt/beitreten.")
for (Player : Players):
# Wenn der Spieler über WaitingForMorePlayersUI verfügt, wird TextBlock abgerufen und der Text wird aktualisiert, damit die richtige Anzahl der erforderlichen Spieler für den Start der Runde angezeigt wird.
if (UIData := WaitingForMorePlayersUI[Player]?):
UIData.TextBlock.SetText(WaitingForMorePlayersMessage(PlayersNeededCount))
# Andernfalls wird WaitingForMorePlayersUI für den Spieler erzeugt.
else:
SetUpWaitingForMorePlayersUI(Player, PlayersNeededCount)
# Akzeptiert player und player_ui und fügt ihrem Bildschirm einen UI-Canvas zum Warten auf weitere Spieler hinzu
SetUpWaitingForMorePlayersUI(Player:player, PlayersNeededCount:int):void =
Logger.Print("UI ‚Warten auf weitere Spieler‘ wird erstellt.")
if (PlayerUI := GetPlayerUI[Player]):
# Dies ist der text_block, der den Text zum Warten auf weitere Spieler auf dem Bildschirm ausgibt.
TextBlock:text_block = text_block:
DefaultText := WaitingForMorePlayersMessage(PlayersNeededCount)
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
# Dies ist der Canvas, der den text_block auf dem Bildschirm hält und positioniert.
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Versätze := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# Der Canvas wird dem Spieler zugewiesen.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Dieser text_block wird unter der Karte gespeichert, damit wir den Text später aktualisieren können, wenn weitere Spieler dem Spiel beitreten.
if (set WaitingForMorePlayersUI[Player] = option{ waiting_for_more_players_ui{Canvas := Canvas, TextBlock := TextBlock} }) {}
# Entfernt die UI-Meldung „Es wird auf weitere Spieler gewartet.“ bei jedem Spieler, der über eine verfügt.
ClearWaitingForMorePlayers():void =
Logger.Print("UI ‚Warten auf weitere Spieler‘ wird gelöscht.")
for (Player -> UIData : WaitingForMorePlayersUI):
if:
PlayerUI := GetPlayerUI[Player]
Canvas := UIData?.Canvas
set WaitingForMorePlayersUI[Player] = false
then:
PlayerUI.RemoveWidget(Canvas)