Ta sekcja zawiera kompletny kod do dodania do utworzonych przez ciebie plików Verse.
Kompletny kod
W tym projekcie istnieje wiele plików Verse.
-
heartbeat.verse: Kompletny kod pliku, patrz niżej.
-
base_team.verse: Kompletny kod pliku, patrz niżej.
-
hunter_team.verse: Kompletny kod pliku, patrz niżej.
-
prop_team.verse: Kompletny kod pliku, patrz niżej.
-
round_timer.verse: Kompletny kod pliku, patrz niżej.
-
waiting_for_more_players.verse: Kompletny kod pliku, patrz niżej.
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){}
# Komunikaty te są używane do powiadamiania agenta rekwizytów za pomocą komunikatu (lub ukrywania go), gdy musi się on poruszyć, aby jego bicie serca nie stało się widoczne.
HeartBeatWarningMessage<localizes>(Time:int):message = "Bicie serca za {Time} sek. Rusz się!"
HeartBeatWarningClear<localizes>:message = ""
# Ta klasa uwidacznia edytowalne właściwości bicia serca dla urządzenia prop_hunt.
heart_beat := class<concrete>():
Logger:log = log{Channel:=log_heart_beat}
@editable # Liczba sekund, w ciągu których agent rekwizytów musi się poruszyć, zanim bicie serca ujawni jego położenie.
MoveTime:float = 15.0
@editable # Liczba sekund pozostałych do wyświetlenia ostrzeżenia o biciu serca. Nie powinno to być > niż HeartBeatTimer.
WarningTime:float = 5.0
@editable # Tablica urządzeń efektów wizualnych bicia serca. Na każdego gracza przypada jedno.
AgentVFX:[]heartbeat_vfx = array{}
@editable # Urządzenie odtwarzacza dźwięku służy do odtwarzania efektów dźwiękowych (SFX) bicia serca.
SFXPlayer:radio_device = radio_device{}
# Ta mapa kojarzy UI do wyświetlania ostrzeżenia o biciu serca z każdym agentem rekwizytów.
var WarningUI:[agent]heartbeat_warning_ui = map{}
# Śledzi, ilu graczy ma aktywne bicie serca, dzięki czemu możemy zarządzać urządzeniem efektów dźwiękowych.
var NumberOfHeartBeats:int = 0
# Konfiguruje UI bicia serca dla agenta.
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) {}
# Aktywuje efekty wizualne i dźwiękowe bicia serca dla określonego gracza.
Enable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
if:
# Pobierz postać, która jest używana do znalezienia położenia agenta rekwizytów w scenie.
Character := PropAgent.GetFortCharacter[]
then:
# Ustaw położenie efektu wizualnego bicia serca na położenie agenta rekwizytów.
HeartBeatVFXData.Activate(Character.GetTransform())
# Zwiększ liczbę uderzeń serca, a jeśli jest to pierwsze odtwarzane bicia serca, musimy odtworzyć dźwięk, aby je rozpocząć.
set NumberOfHeartBeats += 1
if (NumberOfHeartBeats = 1) then SFXPlayer.Play()
# Zarejestruj agenta rekwizytów na urządzeniu odtwarzacza dźwięku, aby dźwięk bicia serca był odtwarzany z tej pozycji.
SFXPlayer.Register(PropAgent)
else:
Logger.Print("Postać, Indeks lub HeartBeatVFXData nie zostały znalezione. Nie można uruchomić bicia serca")
# Czyści efekty wizualne i dźwiękowe bicia serca dla określonego agenta rekwizytów.
Disable(PropAgent:agent, HeartBeatVFXData:heartbeat_vfx):void =
Logger.Print("Wyłączanie bicia serca.")
# Dezaktywuj efekty wizualne.
HeartBeatVFXData.Deactivate()
# Wyrejestruj agenta rekwizytów z urządzenia odtwarzacza dźwięków, co spowoduje zatrzymanie dźwięku bicia serca.
SFXPlayer.Unregister(PropAgent)
# Zmniejsz licznik bicia serca. Licznik nigdy nie powinien zejść poniżej 0.
set NumberOfHeartBeats -= 1
if (NumberOfHeartBeats < 0) then set NumberOfHeartBeats = 0
# Czyści wszystkie efekty wizualne i dźwiękowe dla wszystkich agentów rekwizytów.
DisableAll():void =
Logger.Print("Wyłączenie wszystkich bić serca.")
# Iteruj przez wszystkie efekty wizualne i przenieś je do 0,0,0.
for (HeartBeatVFXDevice : AgentVFX):
HeartBeatVFXDevice.Deactivate()
# Wyrejestruj wszystkich graczy z dźwięku bicia serca.
SFXPlayer.UnregisterAll()
# Ponownie zainicjuj licznik bicia serca do 0
set NumberOfHeartBeats = 0
# Klasa heartbeat_warning_ui zawiera strukturę danych do śledzenia kanwy UI i text_block dla każdego gracza, a także funkcję do tworzenia nowej kanwy UI z ostrzeżeniem o biciu serca.
heartbeat_warning_ui := class:
var Canvas:canvas = canvas{}
var Text:text_block = text_block{}
# Tworzy kanwę UI dla komunikatu ostrzegawczego.
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}}
Offsets := 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
# Klasa heartbeat_vfx zawiera strukturę danych do śledzenia obiektów głównych efektów wizualnych i vfx_spawner_device dla każdego gracza, a także funkcje do ustawiania efektów wizualnych na określonej pozycji lub resetowania ich.
heartbeat_vfx := class<concrete>:
@editable # Urządzenie efektów wizualnych dla każdego bicia serca.
VFXDevice:vfx_spawner_device = vfx_spawner_device{}
# Przesunięcie to jest używane do umieszczenia bicia serca nad głową agenta rekwizytów.
HeartBeatVFXOffset:vector3 = vector3{X := 0.0, Y := 0.0, Z := 110.0}
# Ustawia położenie efektu wizualnego bicia serca, a następnie włącza efekt wizualny.
Activate(Transform:transform):void =
VFXPosition := Transform.Translation + HeartBeatVFXOffset
if (VFXDevice.TeleportTo[VFXPosition, Transform.Rotation]):
VFXDevice.Enable()
# Wyłącza efekty wizualne, ukrywając wizualizacje bicia serca.
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){}
# Ta klasa definiuje urządzenia potrzebne różnym drużynom podczas rozgrywki.
# Ta klasa jest abstrakcyjna, więc nie może być używana samodzielnie. Musi być dziedziczona przez inną klasę.
base_team := class<abstract>:
Logger:log = log{Channel:=log_team}
@editable # Używane do przypisania gracza do drużyny.
ClassSelector:class_and_team_selector_device = class_and_team_selector_device{}
@editable # Używane do przyznawania punktów agentom w drużynie.
ScoreManager:score_manager_device = score_manager_device{}
@editable # Używane do wyświetlania tytułu zadania drużyny.
TeamTitle:hud_message_device = hud_message_device{}
@editable # Używane do wyświetlania opisu zadania drużyny.
TeamDescription:hud_message_device = hud_message_device{}
@editable # Używane do subskrybowania zdarzeń likwidacji członków drużyny (drużyna rekwizytów) lub wrogów (drużyna łowców).
TeamManager:team_settings_and_inventory_device = team_settings_and_inventory_device{}
# Jest to tablica agentów w drużynie.
var TeamAgents<private>:[]agent = array{}
# To zdarzenie jest sygnalizowane, gdy tablica TeamAgents staje się pusta (sygnalizując koniec rundy).
TeamEmptyEvent:event() = event(){}
# Zwraca bieżącą tablicę TeamAgents.
# Jest to wymagane, ponieważ tablica TeamAgents jest prywatna, więc inne klasy nie mogą uzyskać do niej bezpośredniego dostępu.
GetAgents()<decides><transacts>:[]agent =
TeamAgents
# Zwraca rozmiar tablicy TeamAgents
# Wymaga to funkcji, ponieważ tablica TeamAgents jest prywatna, więc inne klasy nie mogą uzyskać do niej bezpośredniego dostępu.
Count()<transacts>:int =
TeamAgents.Length
# Zwraca indeks agenta w tablicy TeamAgents, w przeciwnym razie zawodzi.
FindOnTeam(Agent:agent)<decides><transacts>: int =
Index := TeamAgents.Find[Agent]
# Ustaw agenta w drużynie i powiadom o tym gracza.
InitializeAgent(Agent:agent):void =
AddAgentToTeam(Agent)
ClassSelector.ChangeTeamAndClass(Agent)
DisplayTeamInformation(Agent)
# Dodaj agenta do TeamAgents.
AddAgentToTeam(AgentToAdd:agent):void =
if (not FindOnTeam[AgentToAdd]):
Logger.Print("Dodawanie agenta do drużyny.")
set TeamAgents += array{AgentToAdd}
# Aktywuje urządzenia komunikatów w interfejsie, aby pokazać graczowi, w której drużynie się znajduje
DisplayTeamInformation(Agent:agent):void =
TeamTitle.Show(Agent)
TeamDescription.Show(Agent)
# Gdy agent opuści mecz, usuń go z tablicy TeamAgents i sprawdź, czy runda dobiegła końca.
EliminateAgent(Agent:agent)<suspends>:void =
Sleep(0.0) # Opóźnienie o 1 tyknięcie gry, aby upewnić się, że gracz respawnował się przed kontynuowaniem.
RemoveAgentFromTeam(Agent)
# Usuń agenta z TeamAgents.
# Jeśli usunięty agent był ostatnim, zasygnalizuj TeamEmptyEvent.
RemoveAgentFromTeam(AgentToRemove:agent):void =
set TeamAgents = TeamAgents.RemoveAllElements(AgentToRemove)
Logger.Print("Pozostała liczba agentów w drużynie: {Count()}.")
if (Count() < 1):
Logger.Print("Brak agentów w drużynie. Kończenie rundy.")
TeamEmptyEvent.Signal()
hunter_team.verse
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Simulation }
# Dziedzicząca po klasie base_team, klasa hunter_team zawiera definicje urządzeń i funkcje związane z drużyną łowców i jej agentami.
hunter_team := class<concrete>(base_team):
@editable # W każdej rundzie tworzony jest jeden agent łowców dla każdego z n graczy. Przykład: HunterTeamPerNumberOfPlayers = 5.0 to 1 na 5 graczy. Jeśli liczba graczy = 6, 2 agentów łowców zostanie utworzonych.
HunterAgentPerNumberOfPlayers:float = 5.0 # Minimum 1.1 jest wymuszane, aby zapewnić utworzenie co najmniej 1 agenta rekwizytów.
@editable # Liczba sekund przed pojawieniem się agentów łowców, dająca agentom rekwizytów przewagę, aby mogli się ukryć.
SpawnDelay:float = 15.0
@editable # Maksymalna liczba punktów bazowych, które agent łowców otrzymuje za likwidację agenta rekwizytów. Punkty dzielone są przez liczbę pozostałych agentów rekwizytów.
MaxEliminationScore:int = 5000
@editable # Urządzenie licznika czasu jest używane, aby dać rekwizytom czas na ukrycie się.
WaitTimer:timer_device = timer_device{}
# Ustaw agenta dla agenta łowców.
InitializeAgent<override>(NewHunterAgent:agent):void =
Logger.Print("Ustawianie nowego agenta łowców.")
(super:)InitializeAgent(NewHunterAgent)
# Gdy agent łowców opuści mecz, usuń go z tablicy HunterAgents i sprawdź, czy runda dobiegła końca.
# Zauważ, że nadpisujemy tę funkcję, ponieważ nie musimy przekazywać tutaj dodatkowych danych, tak jak w przypadku zespołu rekwizytów.
EliminateAgent<override>(HunterAgent:agent)<suspends>:void =
Logger.Print("Agent łowców zlikwidowany.")
(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 }
# Ten komunikat jest używany do wyświetlania liczby pozostałych rekwizytów dla wszystkich graczy podczas rundy.
PropAgentsRemainingMessage<localizes>(Count:int):message = "Pozostała liczba rekwizytów {Count}"
# Dziedzicząca po klasie base_team, klasa prop_team zawiera definicje urządzeń i funkcje związane z drużyną rekwizytów i jej agentami.
# W szczególności można w tej klasie znaleźć zachowanie bicia serca agenta rekwizytów.
prop_team := class<concrete>(base_team):
@editable # Punkty otrzymywane przez agenta rekwizytów na sekundę.
ScorePerSecond:int = 10
@editable # Minimalna odległość, na jaką musi przemieścić się agent rekwizytów, aby zresetować licznik uderzeń serca.
MinimumMoveDistance:float = 100.0
@editable # Urządzenie licznika czasu używane do przyznawania punktów rekwizytom.
ScoreTimer:timer_device = timer_device{}
@editable # To urządzenie postępometru używane jest do wyświetlania na ekranie liczby pozostałych w grze rekwizytów.
PropsRemainingTracker:tracker_device = tracker_device{}
@editable # Pobierz właściwości z klasy heart_beat.
HeartBeat:heart_beat = heart_beat{}
# Ustaw agenta dla agenta rekwizytów i przypisz ostrzeżenie o biciu serca w UI.
InitializeAgent<override>(NewPropAgent:agent):void =
Logger.Print("Ustawianie nowego agenta rekwizytów.")
(super:)InitializeAgent(NewPropAgent)
# Po zakończeniu PropScoreTimer, przyznaj punkty wszystkim agentom rekwizytów. PropInstigator jest wymagany do subskrypcji zdarzeń, ale nie jest używany.
OnPropsScore(PropInstigator:?agent):void =
if (PropAgents := GetAgents[]):
for (PropAgent : PropAgents):
ScoreManager.Activate(PropAgent)
# Gdy agent rekwizytów zostanie zlikwidowany lub opuści mecz, usuń go z tablicy PropAgents i sprawdź, czy runda dobiegła końca.
# Zauważ, że nie jest to nadpisywanie, ponieważ przekazujemy wszystkich graczy do funkcji, aby zaktualizować komunikat o pozostałych rekwizytach.
EliminateAgent<override>(PropAgent:agent)<suspends>:void =
Logger.Print("Agent rekwizytów zlikwidowany.")
(super:)EliminateAgent(PropAgent)
# Zaktualizuj liczbę pozostałych rekwizytów.
UpdatePropsRemainingTracker()
# Aktualizuje wartość urządzenia postępometru, pokazując liczbę pozostałych rekwizytów.
UpdatePropsRemainingTracker():void =
PropsRemainingTracker.SetValue(Count())
# Jeśli agent rekwizytów przestanie się poruszać, użyj wyrażenia race, aby sprawdzić, czy agent rekwizytów poruszy się poza MinimumMoveDistance, licznik czasu bicia serca zostanie zakończony lub agent rekwizytów zostanie zlikwidowany.
RunPropGameLoop(PropAgent:agent)<suspends>:void =
Logger.Print("Uruchamianie pętli gry agenta rekwizytów.")
# Wykonuj pętlę w nieskończoność przez zachowanie rekwizytów, aż agent rekwizytów zostanie zlikwidowany lub gracz opuści sesję.
race:
PropAgent.AwaitNoLongerAProp()
loop:
# Poczekaj, aż agent rekwizytów przemieści się na odległość mniejszą niż minimalna, a następnie przejdź dalej.
PropAgent.AwaitStopMoving(MinimumMoveDistance)
# Dopóki agent rekwizytów nie przesunie się poza minimalną odległość, odliczaj do bicia serca, a następnie odtwarzaj bicie serca w nieskończoność.
race:
PropAgent.AwaitStartMoving(MinimumMoveDistance)
block:
CountdownTimer(PropAgent)
PropAgent.StartHeartbeat()
Sleep(0.0) # Gdy wyścig dobiegnie końca (agent rekwizytów poruszy się), uruchom pętlę ponownie.
# Wykonuj pętlę do momentu, gdy agent rekwizytów przestanie być częścią tablicy PropAgents. Usunięcie następuje, gdy agent rekwizytów zostanie zlikwidowany i zamieniony w łowcę lub gdy gracz opuści sesję.
(PropAgent:agent).AwaitNoLongerAProp()<suspends>:void =
loop:
if (not FindOnTeam[PropAgent]):
Logger.Print("Usuwanie zachowania agenta rekwizytów.")
break
Sleep(0.0) # Przejdź do następnego tyknięcia gry.
# Wykonuje pętlę do momentu, gdy agent poruszy się na odległość mniejszą niż MinimumDistance.
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Sprawdzanie, czy agent przemieścił się na odległość mniejszą niż minimalna.")
# Pobierz początkowe położenia agenta z postaci agenta w scenie.
if (Tracked := PropAgent.GetFortCharacter[]):
var StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Pobierz położenie agenta w następnym tyknięciu gry.
NewPosition := Tracked.GetTransform().Translation
# Jeśli odległość nowego położenia w odniesieniu do położenia początkowego jest mniejsza niż MinimumDistance, agent nie poruszył się i przerywamy pętlę.
if (Distance(StartPosition, NewPosition) < MinimumDistance):
Logger.Print("Agent przemieścił się na odległość mniejszą niż minimalna.")
break
# W przeciwnym razie resetujemy StartPosition, aby upewnić się, że gracz porusza się od nowego położenia.
else:
set StartPosition = NewPosition
# Wykonuje pętlę do momentu, gdy agent przesunie się o więcej niż MinimumDistance.
(PropAgent:agent).AwaitStartMoving(MinimumDistance:float)<suspends>:void =
Logger.Print("Sprawdzanie, czy agent przemieścił się dalej niż minimalna odległość.")
# Pobierz początkowe położenia agenta z postaci agenta w scenie.
if (Tracked := PropAgent.GetFortCharacter[]):
StartPosition:vector3 = Tracked.GetTransform().Translation
loop:
Sleep(0.0) # Pobierz położenie agenta w następnym tyknięciu gry.
NewPosition := Tracked.GetTransform().Translation
# Jeśli odległość nowego położenia w odniesieniu do początkowego położenia jest większa lub równa MinimumDistance, agent przemieścił się i przerywamy pętlę.
if (Distance(StartPosition, NewPosition) >= MinimumDistance):
Logger.Print("Agent przemieścił się na odległość większą lub równą minimalnej odległości.")
break
# Opóźnia do czasu uruchomienia HeartBeatWarningTime. Następnie odlicza przez HeartBeatWarningTime i ustawia tekst odliczania. Czyści tekst w przypadku odroczenia.
CountdownTimer(PropAgent:agent)<suspends>:void =
Logger.Print("Rozpoczęcie odliczania bicia serca.")
if (UIData := HeartBeat.WarningUI[PropAgent]):
Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # Uśpienie na czas przed pojawieniem się ostrzeżenia.
Logger.Print("Ostrzeżenie o rozpoczęciu bicia serca.")
var WarningTimeRemaining:int = 0
if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {}
# Odroczenie następuje, gdy funkcja zakończy działanie lub zostanie anulowana, np. gdy przegra wyścig.
# W tym przypadku tekst ostrzeżenia jest usuwany po zakończeniu odliczania lub jeśli agent rekwizytów poruszy się przed zakończeniem odliczania.
defer:
UIData.Text.SetText(HeartBeatWarningClear)
# Ustaw tekst ostrzeżenia na pozostały czas, odczekaj sekundę, a następnie zmniejsz pozostały czas. Jeśli odliczanie dobiegnie końca, przerwij pętlę.
loop:
Logger.Print("Bicie serca za {WarningTimeRemaining} sek.")
UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining))
Sleep(1.0)
set WarningTimeRemaining -= 1
if (WarningTimeRemaining <= 0):
break
else:
Logger.Print("Nie znaleziono UIData.")
# Włącza efekty wizualne i dźwiękowe bicia serca. Czeka w nieskończoność do odroczenia, a następnie wyłącza efekty wizualne i dźwiękowe bicia serca.
(PropAgent:agent).StartHeartbeat()<suspends>:void =
Logger.Print("Spawnowanie bicia serca.")
# Zapisz dane bicia serca, abyśmy mogli przekazać je później, po tym jak PropAgent zostanie zniszczony lub opuści grę.
var HeartBeatVFXData:heartbeat_vfx = heartbeat_vfx{}
if:
# Pobierz indeks agenta rekwizytu w tablicy PropAgents, aby uzyskać dostęp do odpowiadającego mu aktora efektów wizualnych bicia serca.
Index := FindOnTeam[PropAgent]
set HeartBeatVFXData = HeartBeat.AgentVFX[Index]
then:
HeartBeat.Enable(PropAgent, HeartBeatVFXData)
# Gdy ta funkcja zostanie anulowana przez poruszającego się lub zlikwidowanego agenta rekwizytów lub opuszczającego sesję gracza, wyłącz bicie serca.
defer:
HeartBeat.Disable(PropAgent, HeartBeatVFXData)
Sleep(Inf) # Nie usypiaj, dopóki wyścig nie zostanie ukończony.
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){}
# Wartość typu int, która dopuszcza wartości pomiędzy określonymi zakresami. Ten typ jest wymagany przez player_ui_slot.ZOrder.
round_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Ten komunikat służy do wyświetlania czasu pozostałego do zakończenia rundy.
TimeRemainingMessage<localizes>(Minutes:string, Seconds:string):message = "{Minutes}:{Seconds}"
<#
Klasa ta zawiera całą logikę zarządzania czasem rundy i wyświetlania czasu na ekranie.
Możesz użyć tego urządzenia z round_settings_device, aby faktycznie zakończyć rundę.
To urządzenie zarządza czasem bez użycia licznika czasu.
Aby użyć tej klasy:
1) Dodaj plik do projektu.
2) Skompiluj kod Verse z menu Verse na pasku narzędzi.
3) Przeciągnij urządzenie na swoją wyspę z folderu Zawartość/Urządzenia trybu kreatywnego twojej wyspy w przeglądarce zawartości.
4) Dołącz klasę waiting_for_more_players do innego skryptu Verse za pomocą:
@editable
RoundTimer:round_timer = round_timer{}
5) Skompiluj kod Verse z menu Verse na pasku narzędzi.
6) Podłącz urządzenie utworzone w kroku 3 do urządzenia Verse.
7) Uruchom licznik czasu rundy następującym kodem Verse:
RoundTimer.Start()
8) Ponownie uruchom lub zatrzymaj licznik czasu za pomocą równoważnych funkcji.
9) Poczekaj na uruchomienie licznika czasu za pomocą:
RoundTimer.AwaitStart()
10) Poczekaj na zakończenie odliczania czasu za pomocą:
RoundTimer.AwaitEnd()
Wywołaj funkcję EndRound na urządzeniu round_settings_device, aby faktycznie zakończyć rundę gry.
#>
round_timer := class(creative_device):
Logger:log = log{Channel:=log_prop_hunt_device}
@editable # Czas trwania rundy w minutach.
RoundTimeInMinutes:float = 5.0
@editable # Pozioma i pionowa pozycja UI licznika czasu na ekranie. X 0-1 to lewo-prawo, a Y 0-1 to góra-dół.
UIPosition:vector2 = vector2{X:= 0.98, Y:=0.13}
@editable # Pozioma i pionowa pozycja UI licznika czasu na ekranie. X 0-1 to lewo-prawo, a Y 0-1 to góra-dół.
UIAlignment:vector2 = vector2{X := 1.0, Y := 0.0}
@editable # ZOrder interfejsu użytkownika w porównaniu do innych elementów interfejsu użytkownika.
UIZOrder:round_int_clamped = 4
# Sygnalizowane po rozpoczęciu rundy.
RoundStarted:event() = event(){}
# Sygnalizowane, gdy runda ma zostać zakończona.
RoundEndedEvent:event() = event(){}
# Ta mapa kojarzy pole tekstowe do wyświetlania czasu dla każdego gracza.
var TimeRemainingTextBlocks:[player]text_block = map{}
# Czas pozostały do zakończenia rundy, w postaci liczby całkowitej.
var TimeRemainingInSeconds:int = 0
# Czeka, aż licznik czasu rundy zostanie uruchomiony.
AwaitStart()<suspends>:void =
RoundStarted.Await()
Logger.Print("Licznik czasu rundy uruchomiony.")
# Używane do uruchomienia licznika czasu rundy.
Start():void =
Logger.Print("Uruchomienie licznika czasu rundy.")
RoundStarted.Signal()
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
spawn{ Running() }
# Restartuje licznik czasu do RoundTime
Restart():void =
Logger.Print("Restartowanie licznika czasu rundy.")
set TimeRemainingInSeconds = GetRoundTimeInSeconds()
# Uruchamia logikę licznika czasu.
Running()<suspends>:void =
Logger.Print("Licznik czasu rundy działa.")
loop:
UpdateTimeUI()
Sleep(1.0)
# Zmniejsz TimeRemaining o 1 sekundę, a następnie sprawdź, czy czas się skończył. Jeśli tak, zakończ rundę.
set TimeRemainingInSeconds -= 1
if (TimeRemainingInSeconds < 0):
Stop()
break
# Zatrzymuje licznik czasu i kończy rundę.
Stop():void =
Logger.Print("Zatrzymanie licznika czasu rundy.")
# Aby zakończyć rundę, wybieramy gracza spośród graczy pozostałych w scenie.
Players:[]player = GetPlayspace().GetPlayers()
if (Instigator := Players[0]):
RoundEndedEvent.Signal()
# Czeka, aż licznik czasu rundy dobiegnie końca.
AwaitEnd()<suspends>:void =
RoundEndedEvent.Await()
Logger.Print("Licznik czasu rundy zakończony.")
# Akceptuje wartość czasu w minutach i zwraca wartość w sekundach.
GetRoundTimeInSeconds():int =
var InSeconds:int = 0
if (set InSeconds = Round[RoundTimeInMinutes * 60.0]) {}
InSeconds
# Po zakończeniu odliczania zaktualizuj pozostały czas i sprawdź, czy czas upłynął.
UpdateTimeUI():void =
# Ustaw minuty na TimeRemainingInSeconds/60 bez reszty.
var Minutes:int = 0
if (set Minutes = Floor(TimeRemainingInSeconds / 60)) {}
# Ustaw sekundy na pozostałą część z TimeRemainingInSeconds/60.
var Seconds:int = 0
if (set Seconds = Mod[TimeRemainingInSeconds, 60]) {}
# Konwersja minut i sekund na ciągi znaków.
MinutesAsString := string("{Minutes}")
# Jeśli sekundy < 10, musimy dodać 0 przed wartością, aby była wyświetlana jako :0# zamiast :#
SecondsAsString := if (Seconds < 10) then Join(array{string("{0}"),string("{Seconds}")},string()) else string("{Seconds}")
# Iteruj po wszystkich graczach, sprawdź, czy mają TimeRemainingTextBlock, jeśli nie, daj im go. Następnie zaktualizuj tekst.
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))
# Akceptuje gracza, a następnie dodaje kanwę UI z czasem rundy do jego ekranu i przechowuje jego TimeRemainingTextBlock do późniejszej aktualizacji.
SetUpTimeRemainingUI(Player:player):text_block =
Logger.Print("Dodanie UI licznika czasu rundy do gracza.")
# Jest to text_block, który wyświetla na ekranie tekst informujący o pozostałym czasie.
TextBlock:text_block = text_block:
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
if (PlayerUI := GetPlayerUI[Player]):
if (set TimeRemainingTextBlocks[Player] = TextBlock) {}
# Jest to kanwa, która przechowuje i pozycjonuje text_block na ekranie.
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# Kanwa jest przypisana do gracza.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Text_block jest zwracany, aby można go było zapisać na mapie i zaktualizować później w miarę upływu czasu.
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){}
# Wartość typu int, która dopuszcza wartości pomiędzy określonymi zakresami. Ten typ jest wymagany przez player_ui_slot.ZOrder.
waiting_int_clamped := type{_X:int where 0 <= _X, _X <= 2147483647}
# Ten komunikat służy do wyświetlania liczby graczy potrzebnej do rozpoczęcia rundy.
WaitingForMorePlayersMessage<localizes>(Count:int):message = "Oczekiwanie na następującą liczbę graczy: {Count}"
# Ta klasa pokazuje, ilu graczy jest potrzebnych do rozpoczęcia rundy.
waiting_for_more_players_ui := class:
var Canvas:canvas
var TextBlock:text_block
<#
Ta klasa zawiera całą logikę do ustawiania minimalnej liczby graczy i sprawdzania, czy jest ich wystarczająco dużo, aby rozpocząć rundę.
Aby użyć tej klasy:
1) Dodaj plik do projektu.
2) Skompiluj kod Verse z menu Verse na pasku narzędzi.
3) Przeciągnij urządzenie na swoją wyspę z folderu Zawartość/Urządzenia trybu kreatywnego twojej wyspy w przeglądarce zawartości.
4) Podłącz urządzenie licznika czasu do właściwości "WaitingForMorePlayersTimer" tego urządzenia.
5) Dołącz klasę waiting_for_more_players do innego skryptu Verse za pomocą:
@editable
WaitingForMorePlayers:waiting_for_more_players = waiting_for_more_players{}
6) Skompiluj kod Verse z menu Verse na pasku narzędzi.
7) Podłącz urządzenie utworzone w kroku 3 do urządzenia Verse i właściwości ujawnionych w kroku 6.
8) Oczekuje funkcji CheckForMinimumNumberOfPlayers, przekazując jej gracza. Na przykład:
Players = GetPlayspace().GetPlayers()
CheckForMinimumNumberOfPlayers(Players)
9) W IslandSettings ustaw opcję Odliczanie do początku gry na 0.0.
#>
waiting_for_more_players := class(creative_device):
Logger:log = log{Channel:=log_waiting_for_more_players_device}
@editable # Minimalna liczba graczy potrzebna do rozpoczęcia rundy.
MinimumNumberOfPlayers:int = 2
@editable # Pozioma i pionowa pozycja UI licznika czasu na ekranie. X 0-1 to lewo-prawo, a Y 0-1 to góra-dół.
UIPosition:vector2 = vector2{X:= 0.5, Y:=0.4}
@editable # Pozioma i pionowa pozycja UI licznika czasu na ekranie. X 0-1 to lewo-prawo, a Y 0-1 to góra-dół.
UIAlignment:vector2 = vector2{X := 0.5, Y := 0.5}
@editable # ZOrder interfejsu użytkownika w porównaniu do innych elementów interfejsu użytkownika.
UIZOrder:waiting_int_clamped = 3
@editable # Ten licznik czasu jest używany do odliczania do rozpoczęcia rundy po oczekiwaniu na dołączenie graczy do meczu.
WaitingForMorePlayersTimer:timer_device = timer_device{}
# Ta mapa kojarzy kanwę UI do wyświetlania liczby graczy potrzebnych do rozpoczęcia rundy dla każdego gracza.
var WaitingForMorePlayersUI:[player]?waiting_for_more_players_ui = map{}
# Sprawdź, czy jest wystarczająca liczba graczy, aby rozpocząć rundę. Jeśli nie, poczekaj, aż liczba graczy >= MinimumNumberOfPlayers.
WaitForMinimumNumberOfPlayers(Players:[]player)<suspends>:[]player =
Logger.Print("Oczekiwanie na wystarczającą liczbę graczy do rozpoczęcia rundy.")
# Tworzenie nowej zmiennej, aby można było ją modyfikować w miarę dołączania kolejnych graczy. Inicjowanie jej tablicą graczy przekazaną do funkcji.
var PlayersWaiting:[]player = Players
# Jeśli liczba graczy jest mniejsza niż minimum wymagane do rozpoczęcia rundy…
if (PlayersWaiting.Length < MinimumNumberOfPlayers):
loop: # Wykonuj pętlę do momentu, gdy liczba graczy będzie większa lub równa wymaganemu minimum.
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} graczy potrzebnych do rozpoczęcia rundy.")
# Zaktualizuj interfejs oczekiwania na kolejnych graczy.
DisplayWaitingForMorePlayers(PlayersWaiting)
Sleep(2.0) # Poczekaj, aż do meczu dołączy więcej graczy, a następnie sprawdź, czy jest ich wystarczająco dużo, aby rozpocząć rundę.
set PlayersWaiting = GetPlayspace().GetPlayers()
if (PlayersWaiting.Length >= MinimumNumberOfPlayers):
# Jeśli jest już wystarczająca liczba graczy, wyczyść interfejs oczekiwania na kolejnych graczy,
Logger.Print("{PlayersWaiting.Length}/{MinimumNumberOfPlayers} graczy w rundzie, przygotowanie do rozpoczęcia rundy.")
ClearWaitingForMorePlayers()
# Następnie wyjdź z pętli.
break
# uruchom odliczanie rozpoczęcia rundy i poczekaj, aż odliczanie dobiegnie końca.
WaitingForMorePlayersTimer.Start()
WaitingForMorePlayersTimer.SuccessEvent.Await()
Logger.Print("Rozpoczynanie rundy.")
# Zwróć listę graczy w sesji.
return PlayersWaiting
# Wyświetla komunikat UI "Oczekiwanie na więcej graczy" dla każdego gracza, jeśli jeszcze go nie mają. Aktualizuje licznik graczy dla wszystkich graczy.
DisplayWaitingForMorePlayers(Players:[]player):void =
PlayersNeededCount := MinimumNumberOfPlayers - Players.Length
Logger.Print("{Players.Length} graczy w rundzie, oczekiwanie na dołączenie {PlayersNeededCount} kolejnych graczy.")
for (Player : Players):
# Jeśli gracz ma WaitingForMorePlayersUI, pobierz TextBlock i odśwież tekst, aby wyświetlał prawidłową liczbę graczy potrzebną do rozpoczęcia rundy.
if (UIData := WaitingForMorePlayersUI[Player]?):
UIData.TextBlock.SetText(WaitingForMorePlayersMessage(PlayersNeededCount))
# W przeciwnym razie utwórz WaitingForMorePlayersUI dla gracza.
else:
SetUpWaitingForMorePlayersUI(Player, PlayersNeededCount)
# Akceptuje gracza i player_ui i dodaje do jego ekranu kanwę UI oczekiwania na kolejnych graczy.
SetUpWaitingForMorePlayersUI(Player:player, PlayersNeededCount:int):void =
Logger.Print("Tworzenie UI 'oczekiwanie na więcej graczy'.")
if (PlayerUI := GetPlayerUI[Player]):
# To jest text_block, który wyświetla na ekranie tekst oczekiwania na więcej graczy.
TextBlock:text_block = text_block:
DefaultText := WaitingForMorePlayersMessage(PlayersNeededCount)
DefaultTextColor := NamedColors.White
DefaultShadowColor := NamedColors.Black
# Jest to kanwa, która przechowuje i pozycjonuje text_block na ekranie.
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{Minimum := UIPosition, Maximum := UIPosition}
Offsets := margin{Top := 0.0, Left := 0.0, Right := 0.0, Bottom := 0.0}
Alignment := UIAlignment
SizeToContent := true
Widget := TextBlock
# Kanwa jest przypisana do gracza.
PlayerUI.AddWidget(Canvas, player_ui_slot{ZOrder := UIZOrder})
# Text_block jest zapisywany na mapie, dzięki czemu możemy zaktualizować tekst później, gdy do gry dołączy więcej graczy.
if (set WaitingForMorePlayersUI[Player] = option{ waiting_for_more_players_ui{Canvas := Canvas, TextBlock := TextBlock} }) {}
# Usuwa komunikat UI " Oczekiwanie na więcej graczy" dla każdego gracza, który go posiada.
ClearWaitingForMorePlayers():void =
Logger.Print("Usuwanie UI 'oczekiwanie na więcej graczy'.")
for (Player -> UIData : WaitingForMorePlayersUI):
if:
PlayerUI := GetPlayerUI[Player]
Canvas := UIData?.Canvas
set WaitingForMorePlayersUI[Player] = false
then:
PlayerUI.RemoveWidget(Canvas)