Vollständiger Code
Im Folgenden findest du den vollständigen Code für ein Infiltrationsspiel für drei Teams, das die Spieler asymmetrisch ausbalanciert, um dynamische Spielerlebnisse zu schaffen.
triad_infiltration_game.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/FortPlayerUtilities }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/Diagnostics }
triad_infiltration_log_channel := class(log_channel){}
triad_infiltration := class(creative_device):
Logger:log = log{Channel := triad_infiltration_log_channel}
# Um zu vermeiden, dass Spieler einem Team nicht beitreten können, solltest du die maximale Anzahl
# von Spielern in den Inseleinstellungen auf die Summe aller Maximum(Team)-Variablen setzen.
# Maximale Anzahl von Spielern im Infiltrator-Team.
@editable
MaximumInfiltrators:int = 2
# Maximale Anzahl von Spielern im Angreifer-Team.
@editable
MaximumAttackers:int = 4
# Maximale Anzahl von Spielern im Verteidiger-Team.
@editable
MaximumDefenders:int = 4
# Eine Reihe von Teleportern, die Spieler zum Spawnpunkt ihres Teams teleportieren, sobald das Spiel beginnt.
@editable
Teleporters:[]teleporter_device = array{}
# Referenz auf das invisibility_manager-Script, das die Unsichtbarkeit des Infiltrators steuert.
@editable
InvisibilityManager:invisibility_manager = invisibility_manager{}
# Array von Waffengebern für jedes Team.
@editable
var WeaponGranters:[]item_granter_device = array{}
# Array von Spieler-Spawner für jedes Team.
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Referenz auf das Infiltrator-Team.
var MaybeInfiltrators:?team = false
# Referenz auf das Angreifer-Team.
var MaybeAttackers:?team = false
# Referenz auf das Verteidiger-Team.
var MaybeDefenders:?team = false
# Array aller Teams im Spiel
var AllTeams:[]team = array{}
# Map der Teams mit ihrer maximalen Spielerzahl.
var TeamsAndTotals:[team]int = map{}
OnBegin<override>()<suspends>:void =
# Ruft alle Teams ab.
set AllTeams = GetPlayspace().GetTeamCollection().GetTeams()
var AllPlayers:[]player = GetPlayspace().GetPlayers()
# Speichert die Teams, um sie später zu referenzieren.
set MaybeInfiltrators = option{AllTeams[0]}
set MaybeAttackers = option{AllTeams[1]}
set MaybeDefenders = option{AllTeams[2]}
if:
Infiltrators := MaybeInfiltrators?
Angreifer:= MaybeAttackers?
Defenders := MaybeDefenders?
Logger.Print("Alle drei Teams wurden gefunden")
set TeamsAndTotals[Infiltrators] = MaximumInfiltrators
set TeamsAndTotals[Attackers] = MaximumAttackers
set TeamsAndTotals[Defenders] = MaximumDefenders
Logger.Print("Setze alle drei Teams in TeamsAndTotals")
then:
#Abonniere PlayerAddedEvent, um eine Neuausrichtung des Teams zu ermöglichen, wenn ein neuer Spieler dem Spiel beitritt.
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
BalanceTeams()
Logger.Print("Teams balanciert, Unsichtbarkeitsscript wird aufgerufen")
InvisibilityManager.StartInvisibilityManager(AllTeams, AllPlayers, Infiltrators)
Sleep(0.25)
TeleportPlayersToStartLocations()
else:
Logger.Print("Es konnten nicht alle Teams gefunden werden, stelle sicher, dass du die richtigen Teams in deinen Inseleinstellungen zugewiesen hast.")
# Gewährt den Spielern eine Waffe basierend auf dem Index ihres Teams im Array Teams
# durch Indizierung in das Array WeaponGranters.
GrantTeamWeapon(InPlayer:player):void=
if(CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[InPlayer]):
for(TeamIndex -> PlayerTeam:AllTeams, PlayerTeam = CurrentTeam):
if(WeaponGranter := WeaponGranters[TeamIndex]):
WeaponGranter.GrantItem(InPlayer)
Logger.Print("Spieler in Team {TeamIndex + 1} hat eine Waffe erhalten")
# Läuft, wenn ein Spieler von einer Spawnfläche spawnt.
# Ruft GrantTeamWeapon unter Verwendung des angegebenen SpawnedAgent auf.
OnPlayerSpawn(SpawnedAgent:agent):void=
if(SpawnedPlayer := player[SpawnedAgent]):
Logger.Print("Versuch, einem gespawnten Spieler eine Waffe zu geben")
GrantTeamWeapon(SpawnedPlayer)
# Behandelt einen neuen Spieler, der dem Spiel beitritt.
OnPlayerAdded(InPlayer:player):void=
Logger.Print("Ein neuer Spieler ist beigetreten, Zuweisung zu Team erfolgt!")
FortTeamCollection := GetPlayspace().GetTeamCollection()
# Weist den neuen Spieler dem kleinsten Team zu, asymmetrisch.
BalancePlayer(InPlayer)
for:
TeamIndex -> PlayerTeam:AllTeams
PlayerTeam = FortTeamCollection.GetTeam[InPlayer]
TeamTeleporter := Teleporters[TeamIndex]
Transform := TeamTeleporter.GetTransform()
do:
InPlayer.Respawn(Transform.Translation, Transform.Rotation)
Logger.Print("Der gespawnte Spieler wurde an seine Startposition teleportiert")
# Wenn der Spieler ein Infiltrator war, rufe OnInfiltratorJoined auf.
# InvisibilityManager.
if(PlayerTeam = MaybeInfiltrators?):
InvisibilityManager.OnInfiltratorJoined(InPlayer)
# Gleicht alle Spieler in allen Teams im Spiel aus.
BalanceTeams():void=
Logger.Print("Teams werden ausgeglichen")
var AllPlayers:[]player := GetPlayspace().GetPlayers()
set AllPlayers = Shuffle(AllPlayers)
Logger.Print("Länge von AllPlayers ist {AllPlayers.Length}")
for (TeamPlayer:AllPlayers):
BalancePlayer(TeamPlayer)
# Iteriere für jeden Spieler durch die Liste der Teams und weise ihn
# dem Team mit den wenigsten Spielern zu, oder bei Gleichstand dem Start-Team.
BalancePlayer(InPlayer:player):void=
Logger.Print("Ausgleich der Spieler begonnen")
var TeamToAssign:?team = false
set TeamToAssign = FindTeamWithLargestDifference()
if (AssignedTeam := TeamToAssign?, GetPlayspace().GetTeamCollection().AddToTeam[InPlayer, AssignedTeam]):
Logger.Print("Spieler wurde neuem Team zugewiesen")
else:
Logger.Print("Der Spieler war bereits im kleinsten Team")
# Findet das Team mit der größten Differenz zwischen der Anzahl der Spieler
# maximale Spieleranzahl.
FindTeamWithLargestDifference():?team =
Logger.Print("Suche nach dem kleinsten Team")
var TeamToAssign:?team = false
var LargestDifference:int = 0
for:
CandidateTeamIndex -> CandidateTeam:AllTeams
CurrentTeamSize := GetPlayspace().GetTeamCollection().GetAgents[CandidateTeam].Length
MaximumTeamSize := TeamsAndTotals[CandidateTeam]
do:
Logger.Print("Team wird überprüft...")
Logger.Print("Maximalgröße des Teams {CandidateTeamIndex + 1} ist {MaximumTeamSize}")
DifferenceFromMaximum := MaximumTeamSize - CurrentTeamSize
Logger.Print("Differenz zur Maximalgröße ist {DifferenceFromMaximum}")
if(LargestDifference < DifferenceFromMaximum):
set LargestDifference = DifferenceFromMaximum
set TeamToAssign = option{CandidateTeam}
Logger.Print("Team {CandidateTeamIndex + 1} mit Differenz {DifferenceFromMaximum} gefunden")
return TeamToAssign
# Teleportiert Spieler zum Spawnpunkt ihres Teams, nachdem das Team-Balancing abgeschlossen ist.
TeleportPlayersToStartLocations():void=
Logger.Print("Spieler werden an Startpositionen teleportiert")
for:
TeamIndex -> PlayerTeam:AllTeams
TeamPlayers := GetPlayspace().GetTeamCollection().GetAgents[PlayerTeam]
TeamTeleporter := Teleporters[TeamIndex]
do:
for(TeamPlayer:TeamPlayers):
TeamTeleporter.Teleport(TeamPlayer)
Logger.Print("Dieser Spieler wurde an seine Startposition teleportiert")
invisibility.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
triad_invisibility_log_channel := class(log_channel){}
invisibility_manager := class(creative_device):
Logger:log = log{Channel := triad_invisibility_log_channel}
# Array von Spieler-Spawner für das Infiltrator-Team.
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Ob die Sichtbarkeit der Infiltratoren mit Teamkameraden geteilt wird.
@editable
IsVisibilityShared:logic = true
# Wie lange die Infiltratoren sichtbar sind, nachdem sie getroffen wurden.
@editable
VulnerableSeconds:float = 3.0
# Wie schnell die Infiltratoren flackern, nachdem sie beschädigt wurden.
@editable
FlickerRateSeconds:float = 0.4
# Array aller Teams im Spiel
var AllTeams:[]team = array{}
# Map der Spieler mit der Anzahl der Sekunden, die sie noch blinken können.
var PlayerVisibilitySeconds:[agent]float = map{}
OnBegin<override>()<suspends>:void=
# Wartet, bis die Teams ausbalanciert sind, bevor Events abonniert werden, die die Spieler unsichtbar machen.
Logger.Print("Warten, bis die Teams ausbalanciert sind...")
# Startet die Unsichbarkeitsmanagerlogik. Aufgerufen von der Klasse triad_infiltration nach Abschluss des Teamausgleichs
StartInvisibilityManager<public>(GameTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Unsichtbarkeitsscript gestartet!")
set AllTeams = GameTeams
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# Für jeden Spieler, wenn er im Infiltrator-Team gespawnt ist, wird eine OnInfiltratorDamaged-Funktion für diesen
# Spieler gespawnt. Mache dann seinen Charakter unsichtbar.
for(TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam:team := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Aktuelles Team des Spielers abgerufen")
Infiltrators = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Spieler hinzugefügt zu PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Spieler wurde als Infiltrator gespawnt, er wird unsichtbar gemacht")
FortCharacter.Hide()
else:
Logger.Print("Dieser Spieler ist kein Infiltrator)")
# Lässt die Sichtbarkeit eines Agents flackern, indem sein fort_character wiederholt ausgeblendet und angezeigt wird
FlickerCharacter(InCharacter:fort_character)<suspends>:void=
Logger.Print("FlickerCharacter() aufgerufen")
# Der Charakter wird in einer Schleife ein- und ausgeblendet, um einen Flackereffekt zu erzeugen.
loop:
InCharacter.Hide()
Sleep(FlickerRateSeconds)
InCharacter.Show()
Sleep(FlickerRateSeconds)
# In jeder Schleife wird die Zeit, in der der Charakter flackert, um FlickerRateSeconds verringert.
# Wenn die verbleibende Zeit 0 ist, wird die Schleife verlassen.
if:
TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2
TimeRemaining <= 0.0
then:
InCharacter.Hide()
break
# Die Sichtbarkeit eines Agents flackert, wenn er Schaden erleidet
OnInfiltratorDamaged(InAgent:agent)<suspends>:void=
Logger.Print("Versuch, diesen Charakter flackern zu lassen")
TeamCollection := GetPlayspace().GetTeamCollection()
if (FortCharacter := InAgent.GetFortCharacter[]):
loop:
if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]):
# Stelle für jeden Teamkollegen die PlayerVisibility-Sekunden ein und erzeuge ein FlickerEvent.
for(Teammate:TeamAgents):
Logger.Print("Aufruf von StartOrResetFlickering bei einem Teammitglied")
StartOrResetFlickering(Teammate)
else:
# Einfach den beschädigten Charakter flackern lassen.
Logger.Print("Aufruf von StartOrResetFlickering auf InAgent")
StartOrResetFlickering(InAgent)
FortCharacter.DamagedEvent().Await()
# Startet ein neues Flacker-Event, wenn der Agent unsichtbar war, andernfalls
# wird das laufende Flackern des Agenten zurückgesetzt.
StartOrResetFlickering(InAgent:agent):void=
if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]):
Logger.Print("Versuch, ein NEUES FlickerEvent für diesen Charakter zu starten")
# Neues Flackern gestartet
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
spawn{FlickerCharacter(FortCharacter)}
Logger.Print("FlickerEvent für diesen Charakter gespawnt")
else:
# Laufendes Flackern zurücksetzen.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
Logger.Print("FlickerTimer des Charakters auf VulnerableSeconds zurückgesetzt")
# Gibt zurück, ob der Spieler noch Zeit zum Flackern hat.
IsFlickering(InAgent:agent)<decides><transacts>:void=
PlayerVisibilitySeconds[InAgent] > 0.0
# Spawnt eine OnInfiltratorDamaged-Funktion, wenn ein neuer Infiltrator dem Spiel beitritt
OnInfiltratorJoined<public>(InAgent:agent):void=
spawn{OnInfiltratorDamaged(InAgent)}
# Behandelt einen Spieler, der von einer Infiltrator-Spawnfläche spawnt.
OnPlayerSpawn(SpawnedAgent:agent):void=
Logger.Print("Ein Spieler wurde gerade von einer Infiltrator-Spawnfläche gespawnt!")
if:
FortCharacter:fort_character = SpawnedAgent.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[SpawnedAgent]
AllTeams[0] = CurrentTeam
Logger.Print("Spieler wurde als Infiltrator gespawnt, er wird unsichtbar gemacht")
then:
FortCharacter.Hide()
item_capture_manager.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
triad_item_capture_log_channel := class(log_channel){}
item_capture_manager := class(creative_device):
Logger:log = log{Channel := triad_item_capture_log_channel}
# Gegenstands-Spawnpunkt erfassen, der den zu erobernden Gegenstand spawnt.
@editable
CaptureItemSpawner:capture_item_spawner_device = capture_item_spawner_device{}
# Prop, das über dem Kopf eines Spielers schwebt, wenn dieser den Gegenstand
# CaptureItemSpawner hält
@editable
CaptureItemIndicator:creative_prop = creative_prop{}
# Indikator, der auf der Karte anzeigt, wo sich die Ziele für jedes Team befinden.
@editable
MapIndicator:map_indicator_device = map_indicator_device{}
# Wie oft der CaptureItemIndicator seine Position aktualisiert.
@editable
UpdateRateSeconds:float = 0.15
# Wie hoch über dem Kopf eines Spielers der CaptureItemIndicator schwebt.
@editable
VerticalOffset:float = 180.0
# Zeigt eine Nachricht an, wenn ein Spieler den Eroberungsgegenstand holt
@editable
ItemGrabbedMessageDevice:hud_message_device = hud_message_device{}
# Die Zeit, die gewartet werden soll, bevor die Indikatoren CaptureItem und Map zurückgegeben werden.
# Eine negative Zeit bedeutet, dass die Indikatoren nie wieder zurückkehren, es sei denn, das Ziel
# wird erneut erreicht.
@editable
ReturnTime:float = 10.0
# Vergibt Punkte, wenn ein Spieler den Eroberungsgegenstand erobert.
@editable
ScoreManagerDevice:score_manager_device = score_manager_device{}
OnBegin<override>()<suspends>:void=
CaptureItemSpawner.ItemPickedUpEvent.Subscribe(OnItemPickedUp)
CaptureItemSpawner.ItemCapturedEvent.Subscribe(OnItemCaptured)
CaptureItemSpawner.ItemDroppedEvent.Subscribe(OnItemDropped)
SpawnerTransform := CaptureItemSpawner.GetTransform()
# Teleportiere dich zurück zum Spawner und verstecke den CaptureItemIndicator unter der Karte außerhalb des Standorts.
CaptureItemIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)
MapIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)
Logger.Print("Leuchtfeuer zurück zum Eroberungs-Spawner")
# Signalisiere jedem Spieler, wenn ein Spieler das Ziel holt.
OnItemPickedUp(InAgent:agent):void=
Logger.Print("Ziel geholt!")
if(FortCharacter := InAgent.GetFortCharacter[]):
ItemGrabbedMessageDevice.Show()
spawn{FollowCharacter(FortCharacter)}
# Wenn ein Spieler einen Gegenstand fallen lässt, wird eine WaitForReturn()-Funktion erzeugt,
# wenn ReturnTime größer als 0 ist.
OnItemDropped(InAgent:agent):void=
Logger.Print("Ziel fallengelassen!")
if(ReturnTime >= 0.0):
spawn{WaitForReturn()}
else:
Logger.Print("Das fallengelassene Ziel kehrt nicht zurück")
# Wenn der Gegenstand erbeutet wurde, erhält das erobernde Team Punkte und gibt die Indikatoren zurück.
OnItemCaptured(CapturingAgent:agent):void=
Logger.Print("Ziel erobert!")
ScoreManagerDevice.Activate()
ReturnIndicators()
# Wartet eine ReturnTime-Zeitspanne und gibt dann die Indikatoren zurück.
WaitForReturn()<suspends>:void=
Logger.Print("Warten, um die Indikatoren zurückzugeben …")
# Gibt das CaptureItem und die Map-Indikator zurück, wenn der Eroberungsgegenstand
# nicht aufgehoben wird, bevor die Zeit abläuft.
ShouldReturn:logic := race:
block:
Sleep(ReturnTime)
true
block:
CaptureItemSpawner.ItemPickedUpEvent.Await()
false
if(ShouldReturn?):
ReturnIndicators()
# Bewirkt, dass der CaptureItemIndicator einem Spieler kontinuierlich über seinem Kopf folgt.
# Wechselt zwischen der Aktualisierungsschleife für den CaptureItemIndictator und der Frage, ob der Spieler
# den Gegenstand fängt, fallen lässt oder eliminiert wird.
FollowCharacter(FortCharacter:fort_character)<suspends>:void=
Logger.Print("FollowCharacter-Funktion gespawnt")
race:
loop:
Transform := FortCharacter.GetTransform()
spawn{CaptureItemIndicator.MoveTo(Transform.Translation + vector3{Z := VerticalOffset}, Transform.Rotation, UpdateRateSeconds)}
spawn{MapIndicator.MoveTo(Transform.Translation + vector3{Z := VerticalOffset}, Transform.Rotation, UpdateRateSeconds)}
# Wir möchten sicherstellen, dass diese Schleife nur einmal pro Simulationsaktualisierung ausgeführt wird, also schlafen wir für einen Spieltick.
Sleep(0.0)
CaptureItemSpawner.ItemCapturedEvent.Await()
CaptureItemSpawner.ItemDroppedEvent.Await()
FortCharacter.EliminatedEvent().Await()
Logger.Print("Ziel fallengelassen oder erobert")
# Setzt die Karten- und Eroberungsgegenstand-Indikatoren wieder an ihre ursprüngliche Position über den Spawnpunkten zurück.
ReturnIndicators():void=
SpawnerTransform := CaptureItemSpawner.GetTransform()
# Teleportiere dich zurück zum Spawner und verstecke den CaptureItemIndicator und den MapIndicator über der Karte außerhalb der Site.
spawn{CaptureItemIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)}
spawn{MapIndicator.MoveTo(SpawnerTransform.Translation + vector3{Z := VerticalOffset * 10.0}, SpawnerTransform.Rotation, UpdateRateSeconds)}
Logger.Print("Indikatoren zurück an Eroberungs-Spawner gegeben")
Auf eigene Faust
Nach Abschluss dieses Handbuchs hast du gelernt, wie du mit Verse ein Spiel erstellen kannst, bei dem die Teams asymmetrisch zusammengesetzt sind.
Versuche mit dem, was du gelernt hast, Folgendes:
- Versuche, mit verschiedenen Parametern für die Infiltratoren, Angreifer und Verteidiger zu spielen, um dein ideales Spielerlebnis zu schaffen. Was wäre, wenn die Infiltratoren Nahkampfwaffen hätten? Was wäre, wenn die Verteidiger ebenfalls unsichtbar wären?
- Können die Infiltratoren und die Angreifer um das gleiche Ziel kämpfen? Kannst du die Siegbedingung ändern?