Kompletny kod
Poniżej znajduje się kompletny kod gry infiltracyjnej z udziałem trzech drużyn, w których liczba graczy została zrównoważona asymetrycznie w celu zapewnienia dynamiki doświadczeń w trakcie gry.
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}
# Aby uniknąć sytuacji, w której gracze nie będą mogli dołączyć do drużyny, ustaw liczbę maksymalną
# graczy w ustawieniach wyspy tak, aby odpowiadała sumie wszystkich zmiennych Maximum(Team).
# Maksymalna liczba graczy w drużynie infiltratorów.
@editable
MaximumInfiltrators:int = 2
# Maksymalna liczba graczy w drużynie atakujących.
@editable
MaximumAttackers:int = 4
# Maksymalna liczba graczy w drużynie obrońców.
@editable
MaximumDefenders:int = 4
# Tablica teleporterów, które teleportują graczy do miejsca spawnu ich drużyny po rozpoczęciu gry.
@editable
Teleporters:[]teleporter_device = array{}
# Odwołanie do skryptu invisibility_manager, który kontroluje niewidzialność infiltratora.
@editable
InvisibilityManager:invisibility_manager = invisibility_manager{}
# Tablica przyznawaczy broni dla każdej drużyny.
@editable
var WeaponGranters:[]item_granter_device = array{}
# Tablica punktów spawnu graczy dla każdej drużyny.
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Odwołanie do drużyny infiltratorów.
var MaybeInfiltrators:?team = false
# Odwołanie do drużyny atakujących.
var MaybeAttackers:?team = false
# Odwołanie do drużyny obrońców.
var MaybeDefenders:?team = false
# Tablica wszystkich drużyn w grze.
var AllTeams:[]team = array{}
# Mapa drużyn do ich maksymalnej liczby graczy.
var TeamsAndTotals:[team]int = map{}
OnBegin<override>()<suspends>:void =
# Pobierz wszystkie drużyny.
set AllTeams = GetPlayspace().GetTeamCollection().GetTeams()
var AllPlayers:[]player = GetPlayspace().GetPlayers()
# Zapisz drużyny, aby móc się później do nich odwołać.
set MaybeInfiltrators = option{AllTeams[0]}
set MaybeAttackers = option{AllTeams[1]}
set MaybeDefenders = option{AllTeams[2]}
if:
Infiltrators := MaybeInfiltrators?
Attackers := MaybeAttackers?
Defenders := MaybeDefenders?
Logger.Print("Znaleziono wszystkie trzy drużyny")
set TeamsAndTotals[Infiltrators] = MaximumInfiltrators
set TeamsAndTotals[Attackers] = MaximumAttackers
set TeamsAndTotals[Defenders] = MaximumDefenders
Logger.Print("Ustaw wszystkie trzy drużyny w TeamsAndTotals")
then:
#Zasubskrybuj PlayerAddedEvent, aby umożliwić ponowne zrównoważenie drużyny, gdy do gry dołączy nowy gracz.
GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
BalanceTeams()
Logger.Print("Drużyny zrównoważone, wywoływanie skryptu niewidzialności")
InvisibilityManager.StartInvisibilityManager(AllTeams, AllPlayers, Infiltrators)
Sleep(0.25)
TeleportPlayersToStartLocations()
else:
Logger.Print("Nie udało się znaleźć wszystkich drużyn. Pamiętaj, aby przypisać właściwe drużyny w ustawieniach wyspy.")
# Przyznaje graczom broń w oparciu o indeks drużyny w tablicy Teams,
# wykonując indeksowanie tablicy 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("Gracz z drużyny {TeamIndex + 1} otrzymał broń")
# Kod uruchamiany, gdy dowolny gracz zostanie zespawnowany na panelu startowym.
# Wywołuje GrantTeamWeapon z zastosowaniem podanej zmiennej SpawnedAgent.
OnPlayerSpawn(SpawnedAgent:agent):void=
if(SpawnedPlayer := player[SpawnedAgent]):
Logger.Print("Próbuję przyznać zespawnowanemu graczowi broń")
GrantTeamWeapon(SpawnedPlayer)
# Określa zachowanie dotyczące nowego gracza dołączającego do gry.
OnPlayerAdded(InPlayer:player):void=
Logger.Print("Dołączył nowy gracz, przypisuję go do drużyny")
FortTeamCollection := GetPlayspace().GetTeamCollection()
# Przypisz nowego gracza do najmniejszej drużyny, asymetrycznie.
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("Zespawnowany gracz został teleportowany do jego lokalizacji startowej")
# Jeśli gracz był infiltratorem, wywołaj funkcję OnInfiltratorJoined w
# InvisibilityManager.
if(PlayerTeam = MaybeInfiltrators?):
InvisibilityManager.OnInfiltratorJoined(InPlayer)
# Równoważy wszystkich graczy we wszystkich drużynach w grze
BalanceTeams():void=
Logger.Print("Zaczynam równoważyć drużyny")
var AllPlayers:[]player := GetPlayspace().GetPlayers()
set AllPlayers = Shuffle(AllPlayers)
Logger.Print("Długość AllPlayers wynosi {AllPlayers.Length}")
for (TeamPlayer:AllPlayers):
BalancePlayer(TeamPlayer)
# Dla każdego gracza wykonaj iterację przez listę drużyn i przypisz gracza
# do drużyny, w której jest najmniej graczy, a jeśli liczebności są równe, przypisz go do jego drużyny początkowej.
BalancePlayer(InPlayer:player):void=
Logger.Print("Zaczynam równoważyć gracza")
var TeamToAssign:?team = false
set TeamToAssign = FindTeamWithLargestDifference()
if (AssignedTeam := TeamToAssign?, GetPlayspace().GetTeamCollection().AddToTeam[InPlayer, AssignedTeam]):
Logger.Print("Przypisano gracza do nowej drużyny")
else:
Logger.Print("Ten gracz był już w najmniejszej drużynie")
# Znajduje drużynę, w której różnica między aktualną liczbą graczy
# a maksymalną liczbą graczy jest największa.
FindTeamWithLargestDifference():?team =
Logger.Print("Próbuję znaleźć najmniejszą drużynę")
var TeamToAssign:?team = false
var LargestDifference:int = 0
for:
CandidateTeamIndex -> CandidateTeam:AllTeams
CurrentTeamSize := GetPlayspace().GetTeamCollection().GetAgents[CandidateTeam].Length
MaximumTeamSize := TeamsAndTotals[CandidateTeam]
do:
Logger.Print("Sprawdzanie drużyny...")
Logger.Print("Maksymalny rozmiar drużyny {CandidateTeamIndex + 1} wynosi {MaximumTeamSize}")
DifferenceFromMaximum := MaximumTeamSize - CurrentTeamSize
Logger.Print("Różnica względem wartości maksymalnej wynosi {DifferenceFromMaximum}")
if(LargestDifference < DifferenceFromMaximum):
set LargestDifference = DifferenceFromMaximum
set TeamToAssign = option{CandidateTeam}
Logger.Print("Znaleziono drużynę {CandidateTeamIndex + 1} o różnicy {DifferenceFromMaximum}")
return TeamToAssign
# Teleportuje graczy do strefy spawnu ich drużyny po zakończeniu równoważenia drużyn.
TeleportPlayersToStartLocations():void=
Logger.Print("Teleportuję graczy do lokalizacji startowych")
for:
TeamIndex -> PlayerTeam:AllTeams
TeamPlayers := GetPlayspace().GetTeamCollection().GetAgents[PlayerTeam]
TeamTeleporter := Teleporters[TeamIndex]
do:
for(TeamPlayer:TeamPlayers):
TeamTeleporter.Teleport(TeamPlayer)
Logger.Print("Ten gracz został teleportowany do jego lokalizacji startowej")
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}
# Tablica punktów spawnu graczy dla drużyny infiltratorów
@editable
PlayersSpawners:[]player_spawner_device = array{}
# Określ, czy uwidocznienie infiltratorów jest stosowane do wszystkich członków drużyny.
@editable
IsVisibilityShared:logic = true
# Określa, jak długo infiltratorzy pozostają widoczni po odniesieniu obrażeń.
@editable
VulnerableSeconds:float = 3.0
# Określ, jak szybko infiltratorzy będą migotać po odniesieniu obrażeń.
@editable
FlickerRateSeconds:float = 0.4
# Tablica wszystkich drużyn w grze.
var AllTeams:[]team = array{}
# Mapa graczy do liczby sekund, przez które będą oni migać.
var PlayerVisibilitySeconds:[agent]float = map{}
OnBegin<override>()<suspends>:void=
# Poczekaj, aż drużyny zostaną zrównoważone, przed zasubskrybowaniem zdarzeń, które sprawiają, że gracze stają się niewidoczni.
Logger.Print("Oczekiwanie na zrównoważenie drużyn...")
# Uruchamia logikę menedżera niewidzialności. Funkcja wywoływana z klasy triad_infiltration po zakończeniu równoważenia drużyn
StartInvisibilityManager<public>(GameTeams:[]team, AllPlayers:[]player, Infiltrators:team):void=
Logger.Print("Uruchomiono skrypt niewidzialności!")
set AllTeams = GameTeams
for(PlayerSpawner:PlayersSpawners):
PlayerSpawner.SpawnedEvent.Subscribe(OnPlayerSpawn)
# W przypadku każdego gracza, sprawdź, czy został zespawnowany w drużynie infiltratorów, a jeśli tak, zespawnuj
# dla niego funkcję OnInfiltratorDamaged. Następnie spraw, aby jego postać była niewidoczna.
for(TeamPlayer:AllPlayers):
if:
FortCharacter:fort_character = TeamPlayer.GetFortCharacter[]
CurrentTeam:team := GetPlayspace().GetTeamCollection().GetTeam[TeamPlayer]
Logger.Print("Pobrano bieżącą drużynę tego gracza")
Infiltrators = CurrentTeam
set PlayerVisibilitySeconds[TeamPlayer] = 0.0
Logger.Print("Dodano gracza do PlayerVisibilitySeconds")
then:
spawn{OnInfiltratorDamaged(TeamPlayer)}
Logger.Print("Gracz został zespawnowany jako infiltrator, ustawiam niewidoczność gracza")
FortCharacter.Hide()
else:
Logger.Print("Ten gracz nie jest infiltratorem")
# Zmienia widoczność agenta poprzez wielokrotne ukrywanie i pokazywanie jego fort_character
FlickerCharacter(InCharacter:fort_character)<suspends>:void=
Logger.Print("Wywołano FlickerCharacter()")
# Pętla powodująca ukrywanie i pokazywanie postaci w celu uzyskania efektu migotania.
loop:
InCharacter.Hide()
Sleep(FlickerRateSeconds)
InCharacter.Show()
Sleep(FlickerRateSeconds)
# W przypadku każdej pętli skróć czas migotania gracza o FlickerRateSeconds.
# Jeśli pozostały czas osiągnie wartość 0, przerwij pętlę.
if:
TimeRemaining := set PlayerVisibilitySeconds[InCharacter.GetAgent[]] -= FlickerRateSeconds * 2
TimeRemaining <= 0.0
then:
InCharacter.Hide()
break
# Powoduje migotanie widoczności agenta za każdym razem, gdy otrzyma on obrażenia
OnInfiltratorDamaged(InAgent:agent)<suspends>:void=
Logger.Print("Próbuję uruchomić migotanie dla tej postaci")
TeamCollection := GetPlayspace().GetTeamCollection()
if (FortCharacter := InAgent.GetFortCharacter[]):
loop:
if(IsVisibilityShared?, CurrentTeam := TeamCollection.GetTeam[InAgent], TeamAgents := TeamCollection.GetAgents[CurrentTeam]):
# Dla każdego członka drużyny, ustaw wartość w PlayerVisibility i zespawnuj FlickerEvent.
for(Teammate:TeamAgents):
Logger.Print("Wywoływanie StartOrResetFlickering dla członka drużyny")
StartOrResetFlickering(Teammate)
else:
# Włącz tylko migotanie postaci, która odniosła obrażenia.
Logger.Print("Wywoływanie StartOrResetFlickering dla InAgent")
StartOrResetFlickering(InAgent)
FortCharacter.DamagedEvent().Await()
# Uruchamia nowe zdarzenie migotania, jeśli agent był niewidoczny, w przeciwnym razie
# resetuje trwające migotanie agenta.
StartOrResetFlickering(InAgent:agent):void=
if (not IsFlickering[InAgent], FortCharacter := InAgent.GetFortCharacter[]):
Logger.Print("Próbuję uruchomić NOWE zdarzenie FlickerEvent dla tej postaci")
# Uruchomiono nowe migotanie.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
spawn{FlickerCharacter(FortCharacter)}
Logger.Print("Zespawnowano FlickerEvent dla tej postaci")
else:
# Zresetuj trwające migotanie.
if (set PlayerVisibilitySeconds[InAgent] = VulnerableSeconds):
Logger.Print("Resetuj wartość FlickerTimer postaci do VulnerableSeconds")
# Zwraca informację, czy graczowi pozostał czas do końca migotania
IsFlickering(InAgent:agent)<decides><transacts>:void=
PlayerVisibilitySeconds[InAgent] > 0.0
# Uruchamia funkcję OnInfiltratorDamaged, gdy do gry dołącza nowy infiltrator
OnInfiltratorJoined<public>(InAgent:agent):void=
spawn{OnInfiltratorDamaged(InAgent)}
# Obsługuje gracza spawnującego się z panelu startowego infiltratora
OnPlayerSpawn(SpawnedAgent:agent):void=
Logger.Print("Gracz właśnie zespawnował się z panelu startowego infiltratora!")
if:
FortCharacter:fort_character = SpawnedAgent.GetFortCharacter[]
CurrentTeam := GetPlayspace().GetTeamCollection().GetTeam[SpawnedAgent]
AllTeams[0] = CurrentTeam
Logger.Print("Gracz został zespawnowany jako infiltrator, ustawiam niewidoczność gracza")
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}
# Generator przedmiotów do przechwycenia, który spawnuje przedmiot do przechwycenia.
@editable
CaptureItemSpawner:capture_item_spawner_device = capture_item_spawner_device{}
# Rekwizyt, który unosi się nad głową gracza, gdy trzyma on przedmiot z
# CaptureItemSpawner.
@editable
CaptureItemIndicator:creative_prop = creative_prop{}
# Wskaźnik wyświetlany na mapie wmiejscach, gdzie znajdują się cele dla każdej drużyny.
@editable
MapIndicator:map_indicator_device = map_indicator_device{}
# Określ, jak często CaptureItemIndicator aktualizuje swoją pozycję.
@editable
UpdateRateSeconds:float = 0.15
# Określ, jak wysoko nad głową gracza unosi się wskaźnik CaptureItemIndicator.
@editable
VerticalOffset:float = 180.0
# Wyświetla komunikat, gdy gracz złapie przedmiot przechwytywania.
@editable
ItemGrabbedMessageDevice:hud_message_device = hud_message_device{}
# Czas oczekiwania przed powrotem wskaźników CaptureItem i Map.
# Ujemny czas wskazuje, że wskaźniki nigdy nie powrócą, chyba że cel zostanie
# ponownie podniesiony.
@editable
ReturnTime:float = 10.0
# Przyznaje punkty, gdy gracz zdobędzie przedmiot do przechwycenia.
@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()
# Teleportuj z powrotem do generatora, ukrywając CaptureItemIndicator pod mapą, poza zasięgiem wzroku.
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("Zwrócono nadajnik do generatora przedmiotów do przechwycenia")
# Informuj każdego gracza, gdy któryś gracz chwyci cel.
OnItemPickedUp(InAgent:agent):void=
Logger.Print("Przechwycono cel")
if(FortCharacter := InAgent.GetFortCharacter[]):
ItemGrabbedMessageDevice.Show()
spawn{FollowCharacter(FortCharacter)}
# Gdy gracz upuści przedmiot, uruchom funkcję WaitForReturn(),
# jeśli wartość ReturnTime jest większa niż 0.
OnItemDropped(InAgent:agent):void=
Logger.Print("Upuszczono cel")
if(ReturnTime >= 0.0):
spawn{WaitForReturn()}
else:
Logger.Print("Upuszczony cel nie wraca")
# Po przechwyceniu przedmiotu przyznaj punkty drużynie przechwytującej i zwróć wskaźniki.
OnItemCaptured(CapturingAgent:agent):void=
Logger.Print("Przechwycono cel")
ScoreManagerDevice.Activate()
ReturnIndicators()
# Odczekaj czas określony w ReturnTime, a następnie zwróć wskaźniki.
WaitForReturn()<suspends>:void=
Logger.Print("Oczekiwanie na powrót wskaźników...")
# Zwróć wskaźniki CaptureItem i Map, jeśli przedmiot do przechwycenia
# nie zostanie podniesiony przed upływem czasu.
ShouldReturn:logic := race:
block:
Sleep(ReturnTime)
true
block:
CaptureItemSpawner.ItemPickedUpEvent.Await()
false
if(ShouldReturn?):
ReturnIndicators()
# Powoduje, że CaptureItemIndicator stale podąża za graczem nad jego głową.
# Wykonuje wyrażenie race między pętlą aktualizacji dla CaptureItemIndictator, a tym, czy gracz
# przechwyci przedmiot, upuści go, czy zostanie wyeliminowany.
FollowCharacter(FortCharacter:fort_character)<suspends>:void=
Logger.Print("Zespawnowano funkcję FollowCharacter")
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)}
# Chcemy mieć pewność, że pętla ta zostanie uruchomiona tylko raz na aktualizację symulacji, więc uśpimy ją na jedno tyknięcie gry.
Sleep(0.0)
CaptureItemSpawner.ItemCapturedEvent.Await()
CaptureItemSpawner.ItemDroppedEvent.Await()
FortCharacter.EliminatedEvent().Await()
Logger.Print("Cel został upuszczony lub przechwycony")
# Zwraca znacznik na mapie i wskaźnik przedmiotu do przechwycenia z powrotem do ich pozycji początkowych nad generatorami.
ReturnIndicators():void=
SpawnerTransform := CaptureItemSpawner.GetTransform()
# Teleportuj z powrotem do generatora, ukrywając CaptureItemIndicator i MapIndicator nad mapą, poza zasięgiem wzroku.
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("Zwrócono wskaźnik do generatora przedmiotów do przechwycenia")
Praca własna
Dzięki tej instrukcji wiesz już, jak stworzyć w Verse grę, w której drużyny graczy będą równoważone asymetrycznie.
Wykorzystując zdobytą wiedzę, spróbuj wykonać następujące czynności:
- Spróbuj poeksperymentować z różnymi parametrami infiltratorów, atakujących i obrońców, aby stworzyć idealną przygodę w grze. A co, gdyby infiltratorzy mieli do dyspozycji broń białą? A co, gdyby obrońcy również byli niewidoczni?
- Czy infiltratorzy i atakujący mogą walczyć o ten sam cel? Czy można zmienić warunki zwycięstwa?