Medycy to typowy archetyp postaci często spotykany w wielu grach. Zadanie medyka to leczenie przebywających w pobliżu postaci, medycy pomagają towarzyszom z drużyny w dojściu do siebie po odniesieniu obrażeń. W zależności od gry, medycy pełnią różne role, np. doktorzy obsługują pacjentów w szpitalu, sanitariusze polowi pomagają drużynie w walce i leczeniu rannych, istnieją też stacje neutralne, które leczą wszystkich.
Tworzona w tym przykładzie postać medyka zachowuje się zgodnie z szeregiem reguł logicznych.
- Bezczynność:
- Rozpoczęcie leczenia agentów
- Pętla leczenia
- Nawigacja do agenta
Medyk rozpoczyna jako bezczynny i patroluje teren, aż do strefy leczenia wejdzie jakiś agent. Ten agent jest dodany do kolejki leczenia u medyka. Medyk musi śledzić agenta, którego ma leczyć w następnej kolejności, a kolejka zawiera strukturę danych przydatną do tego celu, ponieważ kolejki mają strukturę danych typu pierwszy na wejściu - pierwszy na wyjściu. To oznacza, że postać, która wejdzie do strefy leczenia jako pierwsza, zostanie wyleczona jako pierwsza.
Gdy medyk dostanie się do agenta, którego ma wyleczyć w następnej kolejności, najpierw sprawdza, czy zdrowie agenta spadło poniżej progu leczenia. Jeśli tak jest, zaczyna go leczyć z określoną szybkością, dopóki zdrowie agenta nie osiągnie wartości progowej lub dopóki agent nie opuści strefy leczenia. Podczas leczenia medyk będzie próbował pozostawać w pobliżu agenta, stale nawigując w jego kierunku. Gdy zdrowie agenta osiągnie wymaganą wartość progową, medyk przejdzie do kolejnego agenta, którego ma wyleczyć, ponownie rozpoczynając cały proces. Jeśli nie ma żadnych agentów, których trzeba wyleczyć, medyk powróci do stanu bezczynności.
Logikę postaci niezależnej medyka można zwizualizować za pomocą poniższego tzw. automatu skończonego. Aby dowiedzieć się więcej na temat automatów skończonych, przeczytaj stronę Rozumienie zachowań postaci niezależnych.

Z tego przewodnika dowiesz się, jak tworzyć niestandardową postać medyka przy użyciu skryptu zachowania postaci niezależnej, medyk będzie leczył postacie w pobliżu, gdy ich zdrowie spadnie poniżej określonej wartości progowej. Do celów odniesienia, na końcu tego przewodnika znajduje się kompletny skrypt.
Tworzenie nowego skryptu zachowania postaci niezależnej
Aby rozpocząć tworzenie własnej postaci niezależnej medyka, utwórz nowy skrypt zachowania postaci niezależnej o nazwie medic_example. Więcej informacji na temat tworzenia własnego skryptu zachowania postaci niezależnej można znaleźć na stronie Utwórz własne zachowanie postaci niezależnej. Otwórz plik Verse w Visual Studio Code.
Wykonaj te kroki, aby w UEFN utworzyć skrypt zachowania postaci niezależnej, który spawnuje postać medyka leczącego graczy w jego pobliżu.
Implementowanie kolejki do leczenia
Skrypt zachowania postaci niezależnej rozpoczyna się od kilku wartości używanych do ruchu postaci i wizualizacji debugowania. Nie potrzebujesz w skrypcie ich wszystkich, dlatego usuniesz teraz niepotrzebny kod.
-
Na początku definicji klasy
medic_example
usuń wartości przedOnBegin()
. Medyk nie będzie czekał na podejście do postaci, zamiast tego będzie podczas leczenia podążał za nimi. W tym przykładzie nie potrzebujesz wartości debugowania, użyjesz innych obiektów do wizualizacji, kiedy twój medyk leczy postacie. -
W
OnBegin()
, po pobraniu interfejsów postaci w pierwszej instrukcjiif
, usuń kod wewnątrz instrukcjithen
. Medyk nie musi po zespawnowaniu wchodzić w pętlę między punktami, zamiast tego będzie patrolował teren wokół swojego punktu spawnu, w oczekiwaniu na postacie do wyleczenia. -
Usuń funkcje
DrawDebugLocation()
iDrawDebugLookAt()
. W tym przykładzie nie są używane wartości debugowania, dlatego nie potrzebujesz też powiązanych funkcji, które ich używają.
Po usunięciu niepotrzebnego kodu możesz rozpocząć tworzenie postaci medyka.
-
Na początku definicji klasy
medic_example
dodaj następujące wartości:-
edytowalna wartość float
HealingThreshold
. Jest to wartość progowa zdrowia, poniżej której muszą się znaleźć postacie, aby otrzymać leczenie.# Wartość progowa PZ, poniżej której musi spaść postać, aby można ją było wyleczyć. @editable HealingThreshold:float = 50.0
-
Dodaj edytowalną wartość float
HealingDelay
. Jest to ilość czasu oczekiwania między każdą instancją leczenia podczas leczenia postaci. Zmień ją w zależności od tego, czy chcesz, aby medyk leczył szybciej, czy wolniej.# Wartość progowa PZ, poniżej której musi spaść postać, aby można ją było wyleczyć. @editable HealingThreshold:float = 50.0 # Jak długo czekać między leczeniem postaci @editable HealingDelay:float = 1.5
-
Edytowalna wartość float
HealingAmount
. Jest to ilość zdrowia, o jaką będą leczone postacie w każdej instancji leczenia. Gdy postać niezależna medyka leczy jakąś postać, będzie ją leczyła o określoną ilośćHealingAmount
co każdeHealingDelay
liczone w sekundach.# Jak długo czekać między leczeniem postaci @editable HealingDelay:float = 1.5 # O ile punktów leczyć postacie w każdej instancji leczenia @editable HealingAmount:float = 5.0
-
Edytowalna strefa mutatora
HealVolume
. Wolumen, do którego wchodzą postacie, aby otrzymać leczenie. W tym przykładzie użyjesz strefy mutatora, ponieważ strefa mutatora ma zdarzenieAgentEntersEvent
, które medyk może zasubskrybować i którego może użyć do sprawdzenia, czy istnieją postacie wymagające leczenia.# O ile punktów leczyć postacie w każdej instancji leczenia @editable HealingAmount:float = 5.0 # Wolumen, do którego wchodzą postacie, aby otrzymać leczenie. @editable HealVolume:mutator_zone_device = mutator_zone_device{}
-
Edytowalny generator efektów wizualnych
VFXSpawner
. Wizualna informacja zwrotna jest ważna, ponieważ pokazuje, czy kod działa, dlatego użyjesz generatora efektów wizualnych, aby spawnować efekty w trakcie leczenia postaci.# Wolumen, do którego wchodzą postacie, aby otrzymać leczenie. @editable HealVolume:mutator_zone_device = mutator_zone_device{} # Generator efektów wizualnych odtwarzający efekty wizualne w trakcie leczenia postaci. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
-
Zmienny opcjonalny
agent
o nazwieAgentToFollow
. Przechowuje odwołanie do postaci, za którą ma podążać medyk podczas jej leczenia.# Generator efektów wizualnych odtwarzający efekty wizualne w trakcie leczenia postaci. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} # Agent, za którym ma podążać medyk w trakcie leczenia var AgentToFollow:?agent = false
-
Zmienna kolejka agentów o nazwie
AgentsToHeal
. Jeśli leczenia potrzebuje wiele postaci, medyk będzie je leczył według kolejności, w jakiej weszły doHealVolume
. Kod kolejki skonfigurujesz w następnym kroku. W celu uzyskania dodatkowych informacji na temat struktury danych kolejki przeczytaj stronę stosy i kolejki w Verse.# Agent, za którym ma podążać medyk w trakcie leczenia var AgentToFollow:?agent = false # Kolejka agentów do wyleczenia w przypadku, gdy do wolumenu leczenia wchodzi wielu agentów. var AgentsToHeal<public>:queue(agent) = queue(agent){}
-
Zmienna wartość float
UpdateRateSeconds
. Jest to ilość czasu oczekiwania między aktualizacją pozycjiHealVolume
iVFXSpawner
.# Kolejka agentów do wyleczenia w przypadku, gdy do wolumenu leczenia wchodzi wielu agentów. var AgentsToHeal<public>:queue(agent) = queue(agent){} # Używane do określenia, jak szybko aktualizować pozycję HealVolume i VFXSpawner UpdateRateSeconds<private>:float = 0.1
-
-
Aby zaimplementować kolejkę
AgentsToHeal
, użyjesz kodu podanego pod koniec tego kroku.- Wróć do Eksploratora Verse, kliknij PPM nazwę projektu i wybierz Dodaj nowy plik Verse do projektu, aby otworzyć okno Utwórz skrypt Verse.
-
W oknie Utwórz skrypt Verse kliknij Klasa Verse, aby wybrać ją jako swój skrypt.
-
Nadaj nazwę klasie Verse, zmieniając tekst w polu Nazwa klasy na
queue
. -
Kliknij opcję Utwórz, aby utworzyć plik Verse.
-
W Eksploratorze Verse kliknij dwukrotnie nazwę pliku Verse, aby go otworzyć w Visual Studio Code.
-
Zastąp kod w pliku
queue
następującym kodem. Ten kod implementuje rodzajową kolejkę typutype
przy użyciu struktury danych listy. Jest to przykład typu parametrycznego, ponieważ implementacja kolejki będzie działała niezależnie od typu, z którego się ją utworzy. W przykładzie użyjesz kolejki postaci, dlatego definicja kolejki wmedic_example
będziequeue(agent)
.list(t:type) := class: Data:t Next:?list(t) queue<public>(t:type) := class<internal>: Elements<internal>:?list(t) = false Size<public>:int = 0 Enqueue<public>(NewElement:t):queue(t) = queue(t): Elements := option: list(t): Data := NewElement Next := Elements Size := Size + 1 Dequeue<public>()<decides><transacts>:tuple(queue(t), t) = List := Elements? (queue(t){Elements := List.Next, Size := Size - 1}, List.Data) Front<public>()<decides><transacts>:t = Elements?.Data CreateQueue<public><constructor>(InData:t where t:type) := queue(t): Elements := option: list(t): Data := InData Next := false Size := 1
-
Umieszczenie skryptów Verse w odrębnych folderach pomaga w organizacji pracy oraz umożliwia łatwe odwoływanie się do często używanych plików. Przykładowo, utworzysz folder, w którym będą przechowywane zachowania postaci niezależnej w tym projekcie. W Visual Studio Code naciśnij przycisk Nowy folder, aby utworzyć nowy folder w projekcie UEFN. Folderowi nadaj nazwę
npc_behaviors
. Przeciągnij plik Versemedic_example
do nowego folderu.
Kod w medic_example
powinien się teraz prawidłowo skompilować.
Leczenie postaci wewnątrz wolumenu
Gdy do HealVolume
wejdzie zraniona postać, medyk powinien zacząć ją leczyć, gdy zdrowie postaci jest mniejsze niż wartość progowa HealingThreshold
. Gdy zdrowie postaci przekroczy wartość HealingThreshold
, medyk powinien przestać leczyć tę postać i przejść do kolejnej postaci, która wymaga leczenia. W przypadku wielu postaci, medyk powinien je leczyć według kolejności, w której weszły do HealVolume
. Wykonaj te kroki, aby leczyć postacie, gdy wejdą do HealVolume
.
-
Wróć do pliku
medic_example
, w częściOnBegin()
po instrukcjithen
rozpocznij pętlęloop
. Wewnątrzloop
pobierz wynik funkcjiDequeue()
z kolejkiAgentsToHeal
i zapisz go w zmiennejDequeueResult
.then: loop: # Pobierz kolejnego agenta z kolejki do leczenia. Jeśli istnieje agent, który ma być wyleczony, wylecz go, wywołując AgentToHeal. # Jeśli nie ma agentów do leczenia, poczekaj, aż do HealVolume wejdzie jakiś agent if: DequeueResult := AgentsToHeal.Dequeue[]
-
Zmienna
DequeueResult
jest krotkątuple
, która zwraca zarówno kopię kolejkiAgentsToHeal
z usuniętym pierwszym elementem, jak i agenta na początku kolejki. ZaktualizujAgentsToHeal
, ustawiając ją na pierwszą wartość w krotce, i zapisz drugą wartość jakoAgentToHeal
.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1)
-
Gdy masz już agenta do leczenia, musisz zacząć go leczyć, dopóki przebywa w
HealVolume
. W tym celu zdefiniujesz nową funkcję o nazwieHealCharacter()
. Dodaj nową funkcję o nazwieHealCharacter()
do definicji klasymedic_example
. Ta funkcja przyjmujeAgentToHeal
oraz oba interfejsy postaci medyka,Navigatable
iFocusable
, jako argumenty funkcji. Dodaj do tej funkcji modyfikator<suspends>
, ponieważ musi ona wykonywać kilka zadań asynchronicznych podczas leczenia postaci.# Wylecz postać, następnie odczekaj ilość czasu określoną przez HealingDelayAmount. # Kończy się, gdy zdrowie postaci osiągnie wartość HealingThreshold # lub gdy postać opuści HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
-
W
HealCharacter
sprawdź, czyAgentToHeal
znajduje się wewnątrz wolumenu, wywołującIsInVolume[]
i przekazującAgentToHeal
jako argument. Jeśli agent znajduje się wewnątrz wolumenu, możesz zacząć go leczyć. Wszyscy możliwi do wyleczenia agenci implementują interfejshealthful
, który stanowi częśćfort_character
agenta. Pobierzfort_character
agenta i zapisz go w wartościCharacterToHeal
.HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Lecz postać tylko, gdy znajduje się wewnątrz HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[]
-
Gdy postać jest gotowa do leczenia, musisz mieć pewność, że medyk pozostaje w pobliżu leczonej postaci. Utwórz
navigation_target
zAgentToHeal
przy użyciuMakeNavigationTarget
i zapisz go w zmiennejNavigationTarget
. Następnie w instrukcjibranch
wywołaj funkcjęNavigateTo()
przy użyciu interfejsunavigatable
postaci niezależnej, aby medyk nawigował doAgentToHeal
. Ponadto, w funkcjibranch
wywołaj funkcjęMaintainFocus()
, aby mieć pewność, że medyk skupia się naAgentToHeal
. Użycie instrukcjibranch
w tym kontekście pozwala na jednoczesne asynchroniczne wykonanie zarównoNavigateTo()
, jak iMaintainFocus()
i umożliwia niezwłoczne wykonanie dowolnego kodu pobranch
. W celu uzyskania dodatkowych informacji o wyrażeniach branch przeczytaj stronę Wyrażenie branch w języku Verse.# Lecz postać tylko, gdy znajduje się wewnątrz HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Postać jest wewnątrz wolumenu, rozpoczynanie leczenia") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal)
-
Włącz
VFXSpawner
, aby odtwarzać efekty wizualne w trakcie leczenia postaci przez medyka. Następnie w wyrażeniudefer
wyłączVFXSpawner
. Kod wyłączającyVFXSpawner
znajduje się w wyrażeniudefer
, dlatego nie zostanie wykonany, dopóki nie wyjdziemy z bieżącego zakresu. W danej sytuacji oznacza to, że kod zostanie wykonany tylko, gdy funkcja się zakończy, co gwarantuje, że będzie to ostatnia rzecz zrealizowana w funkcji. W celu uzyskania dodatkowych informacji o wyrażeniach defer przeczytaj stronę defer.branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable()
-
Podczas leczenia
CharacterToHeal
, leczenie powinno zostać przerwane, gdy wystąpi jeden z dwóch warunków. Albo postać zostanie wyleczona na tyle, że jej zdrowie przekroczy wartośćHealingThreshold
, albo postać opuściHealVolume
. Aby to osiągnąć, użyjesz wyrażeniarace
. Skonfiguruj wyrażenierace
międzyloop
iAwait()
wHealVolume.AgentExitsEvent.
branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: HealVolume.AgentExitsEvent.Await()
-
Wewnątrz
loop
pobierz bieżące zdrowie postaci przy użyciuGetHealth()
i zapisz je w wartościCurrentHealth
. Następnie w instrukcjiif
sprawdź, czyCurrentHealth
plusHealingAmount
jest większe niżHealingThreshold
. Jeśli tak jest, medyk powinien zakończyć leczenie i wyjść (instrukcjabreak
) z pętli. Jeśli jednak bieżące zdrowie postaci jest tylko trochę niższe niż wartość progowa leczenia, medyk ma kontynuować leczenie aż do jej osiągnięcia. Dodaj drugą instrukcjęif
wewnątrz pierwszej, która będzie sprawdzała, czyCurrentHealth
jest mniejsze niżHealingThreshold
. Jeśli tak jest, ustaw zdrowie postaci naHealingThreshold
.race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Postać osiągnęła próg leczenia HealingThreshold, kończenie leczenia") break HealVolume.AgentExitsEvent.Await()
-
W przeciwnym razie, jeśli
CurrentHealth
plusHealingAmount
nie jest większe niżHealingThreshold
, ustaw zdrowie postaci naCurrent Health
plusHealingAmount
.if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Postać osiągnęła próg leczenia HealingThreshold, kończenie leczenia") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
-
Na końcu pętli
loop
, dodaj instrukcję sleep przez długość czasu równąHealingDelay
. Bez instrukcji sleep postacie będą uzdrawiane co każdą aktualizację symulacji, opóźnienieHealingDelay
zapobiegnie ich natychmiastowemu wyleczeniu. Gotowy kodHealCharacter()
powinien wyglądać następująco.# Wylecz postać, następnie odczekaj ilość czasu określoną przez HealingDelayAmount. # Kończy się, gdy zdrowie postaci osiągnie wartość HealingThreshold # lub gdy postać opuści HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Lecz postać tylko, gdy znajduje się wewnątrz HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Postać jest wewnątrz wolumenu, rozpoczynanie leczenia") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Postać osiągnęła próg leczenia HealingThreshold, kończenie leczenia") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) Sleep(HealingDelay) HealVolume.AgentExitsEvent.Await()
-
Wróć do
OnBegin()
, w wyrażeniuthen
wewnątrzloop
wywołajHealCharacter()
, przekazującAgentToHeal
, interfejsNavigatable
i interfejsFocusable
.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Usunięto z kolejki kolejnego agenta do wyleczenia") HealCharacter(AgentToHeal, Navigatable, Focusable)
-
Nie zawsze w pobliżu medyka będzie przebywała postać do wyleczenia, a funkcja
Dequeue[]
zakończy się niepowodzeniem, jeśli w kolejceAgentsToHeal
nie ma agentów. Aby to obsłużyć, dodaj instrukcjęelse
na końculoop
. Wewnątrz tej instrukcjiif
wywołajSleep()
przez długość czasu równąHealingDelay
, następnie wywołajAwait()
zHealVolume.AgentEntersEvent
. Dzięki temu medyk nie będzie bez przerwy wywoływałDequeue[]
na kolejceAgentsToHeal
, zamiast tego przed ponownym uruchomieniem pętli będzie czekał, aż doHealVolume
wejdzie nowa postać. Gotowy kod pętli powinien wyglądać następująco.loop: # Pobierz kolejnego agenta z kolejki do leczenia. Jeśli istnieje agent, który ma być wyleczony, wylecz go, wywołując AgentToHeal. # Jeśli nie ma agentów do leczenia, poczekaj, aż do HealVolume wejdzie jakiś agent if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Usunięto z kolejki kolejnego agenta do wyleczenia") HealCharacter(AgentToHeal, Navigatable, Focusable) else: Print("Kolejka AgentsToHeal jest pusta!") Sleep(HealingDelay) HealVolume.AgentEntersEvent.Await()
Śledzenie, kiedy w wolumenie leczenia przebywają postacie
Aby wiedzieć, kiedy postać wchodzi do wolumenu leczenia HealVolume
lub go opuszcza, subskrybujesz zarówno AgentEntersEvent, jak i
AgentExitsEvent z
HealVolume` na nowe funkcje.
-
Dodaj nową funkcję o nazwie
OnAgentEnters()
do definicji klasymedic_example
. Ta funkcja przyjmuje agenta, który właśnie wszedł doHealVolume
, i dodaje go do kolejkiAgentsToHeal
.OnAgentEnters(EnteredAgent:agent):void= Print("Agent wszedł do wolumenu leczenia")
-
W
OnAgentEnters()
sprawdź, czy agent wewnątrz wolumenu nie jest postacią medyka. Jeśli tak jest, ustaw kolejkęAgentsToHeal
na wynik wywołaniaEnqueue[]
z podaniemEnteredAgent
. Gotowa funkcjaOnAgentEnters()
powinna wyglądać następująco:OnAgentEnters(EnteredAgent:agent):void= Print("Agent wszedł do wolumenu leczenia") if (EnteredAgent <> GetAgent[]): set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
-
Gdy agent opuści
HealVolume
, nie musisz go usuwać z kolejkiAgentsToHeal
. Dzieje się tak dlatego, że pętla wOnBegin()
już wywołujeDequeue[]
w pętli. Możesz jednak chcieć wykonać kod, gdy agent opuszcza wolumen w twoich przykładach, dlatego teraz napiszesz odpowiednią funkcję. Dodaj nową funkcję o nazwieOnAgentExits()
do definicji klasymedic_example
.OnAgentExits(ExitAgent:agent):void= Print("Agent opuścił wolumen leczenia")
-
W
OnBegin()
zasubskrybujAgentEntersEvent
iAgentExitsEvent
zHealVolume
odpowiednio naOnAgentEnters
iOnAgentExits
. Funkcja powinna się uruchamiać jako wyłączona, dlatego jest to dobre miejsce, aby wywołaćDisable()
na generatorze postaci.OnBegin<override>()<suspends>:void= Print("Cześć, SI!") VFXSpawner.Disable() HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters) HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
Przemieszczanie wolumenu leczenia wraz z medykiem
Gdy postać medyka się przemieszcza, trzeba wraz z nią przemieszczać HealVolume
, aby dopasować go do obecnej pozycji medyka. To samo dotyczy również generatora VFXSpawner
. W tym celu użyjesz nowej funkcji DeviceFollowCharacter()
.
-
Dodaj nową funkcję o nazwie
DeviceFollowCharacter()
do definicji klasymedic_example
. Ta funkcja musi być wykonywana asynchronicznie, aby bez przerwy aktualizować pozycje urządzenia, dlatego dodaj do niej modyfikator<suspends>
.DeviceFollowCharacter()<suspends>:void=
-
Wewnątrz funkcji
DeviceFollowCharacter()
pobierzfort_character
medyka, najpierw pobierając agenta przy użyciuGetAgent[]
, a następnie wywołującGetFortCharater[]
.DeviceFollowCharacter()<suspends>:void= if: # Pobierz agenta (postać SI), z którym jest powiązane to zachowanie. Agent := GetAgent[] # Pobierz interfejs fort_character agenta, aby uzyskać dostęp do charakterystycznych dla postaci Fortnite zachowań, zdarzeń, funkcji i interfejsów. Character := Agent.GetFortCharacter[]
-
Teraz musisz ciągle przemieszczać
HealVolume
iVFXSpawner
do pozycjiCharacter
. Robi się to, zapętlającMoveTo()
na obu urządzeniach. Rozpocznijloop
, pobierz przekształcenieCharacter
i zapisz je w zmiennejCharacterTransform
.if: # Pobierz agenta (postać SI), z którym jest powiązane to zachowanie. Agent := GetAgent[] # Pobierz interfejs fort_character agenta, aby uzyskać dostęp do charakterystycznych dla postaci Fortnite zachowań, zdarzeń, funkcji i interfejsów. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform()
-
Wywołaj
MoveTo()
zarówno naVFXSpawner
, jak i naHealVolume
, przemieszczając je doCharacterTransform.Translation
iCharacterTransform.Rotation
. Ustaw czas trwania naUpdateRateSeconds
w sekundach. Na koniec wywołajSleep()
dla ilości czasu równejUpdateRateSeconds
, aby zapobiec aktualizacji pozycji urządzeń co każdą aktualizację symulacji. Aktualizacja pozycji urządzenia co każdą aktualizację symulacji może spowodować rozedrgany ruch na urządzeniach. Gotowy kodDeviceFollowCharacter()
powinien wyglądać następująco.DeviceFollowCharacter()<suspends>:void= if: # Pobierz agenta (postać SI), z którym jest powiązane to zachowanie. Agent := GetAgent[] # Pobierz interfejs fort_character agenta, aby uzyskać dostęp do charakterystycznych dla postaci Fortnite zachowań, zdarzeń, funkcji i interfejsów. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) Sleep(UpdateRateSeconds)
-
W
OnBegin()
, po instrukcjiif
, w której są zapisane interfejsy postaci, ale przed pętlą, zespawnuj instancjęDeviceFollowCharacter()
.
Dodawanie postaci do poziomu
-
Utwórz definicję postaci niezależnej medyka o nazwie Medic. Kliknij nową definicję postaci niezależnej, aby otworzyć ekran Definicja postaci niezależnej.
-
Na ekranie Definicja postaci niezależnej zmodyfikuj następujące właściwości:
-
W Typ postaci niezależnej ustaw Typ na Strażnik. Interfejs strażnika daje dostęp do funkcjonalności charakterystycznych dla postaci strażnika, takich jak zdarzenia, gdy strażnik zostanie zaalarmowany lub coś podejrzewa, umożliwia też wynajmowanie strażników jako sojuszników. Strażnicy mogą też brać broń do ręki, a postacie typu Niestandardowe i Dzikie zwierzęta obecnie nie mogą tego robić. Możesz też zmienić nazwę postaci na karcie Nazwa.
-
W Zachowanie postaci niezależnej ustaw Zachowanie na Zachowanie Verse. Następnie ustaw Skrypt zachowania postaci niezależnej na
medic_example
. Postać będzie miała dalej dostęp do funkcjonalności z interfejsu strażnika, ale będzie używała twojego skryptu Verse, aby zdecydować, co robić w trakcie fazOnBegin
iOnEnd
. -
Na karcie Modyfikatory, w pozycji Modyfikator spawnu strażnika, kliknij kartę Elementy kosmetyczne, aby zmienić wygląd kosmetyczny postaci. Możesz wybrać spośród istniejących elementów kosmetycznych lub włączyć Dopasowanie elementów kosmetycznych postaci, aby używać niestandardowego modelu. Uwaga: dopasowania elementów kosmetycznych mogą używać tylko strażnicy i postacie typu Niestandardowe, dzikie zwierzęta nie mogą tego używać. W celu uzyskania dodatkowych informacji o modyfikatorach postaci i o tym, które mają zastosowanie do różnych typów postaci, przeczytaj stronę Definicja postaci.
-
-
Zapisz definicję swojej postaci niezależnej. W Przeglądarce zawartości przeciągnij definicję postaci niezależnej do poziomu. Automatycznie utworzy to nowy generator postaci i przypisze do niego twoją definicję postaci niezależnej.

-
Przeciągnij do poziomu jedną strefę mutatora i jedno urządzenie generatora efektów wizualnych.
-
Wybierz generator postaci. W Outlinerze w Opcjach użytkownika:
-
Ustaw Zastąpienie skryptu AIBehavior na swój skrypt
medic_example
. Zastąpienie skryptu AIBehavior w Outlinerze umożliwia odwoływanie się do urządzeń na poziomie, potrzebujesz tej funkcjonalności, aby przypisywać HealVolume i VFXSpawner. -
Ustaw HealVolume na strefę mutatora, a VFXSpawner na generator efektów wizualnych umieszczony na poziomie.
-

-
Wybierz strefę mutatora. W Outlinerze, w Opcjach użytkownika, ustaw Widoczność strefy podczas gry na Prawda. To pomaga zwizualizować, w którym miejscu znajduje się
HealVolume
i w jaki sposób przemieszcza się wraz z postacią medyka. -
Wybierz generator efektów wizualnych. W Outlinerze w Opcjach użytkownika ustaw Efekt wizualny na wybrany efekt. W tym przykładzie zastosowano efekt Bańki, aby pokazywać leczenie, ale możesz chcieć użyć innego efektu, takiego jak fajerwerki lub iskry. Zmień efekt wizualny na taki, który najlepiej pasuje do postaci.
-
Na pasku narzędzi UEFN kliknij Uruchom sesję, aby przetestować poziom w grze. W trakcie testu gry twoja postać powinna leczyć zranione postacie, które wchodzą do strefy mutatora. Podczas leczenia postaci powinien być odtwarzany efekt wizualny, a medyk powinien podążać za leczoną postacią i utrzymywać na niej skupienie.

Kompletny skrypt
Poniżej znajduje się kompletny skrypt postaci niezależnej, która leczy postacie, gdy ich punkty zdrowia spadną poniżej określonego progu.
medic_example.verse
using { /Fortnite.com/AI }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Verse.org/Colors }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
# Utworzone w Verse zachowanie postaci niezależnej, którego można używać w definicji postaci niezależnej lub w zastąpieniu skryptu zachowania urządzenia generatora postaci.
medic_example<public> := class(npc_behavior):
# Wartość progowa PZ, poniżej której musi spaść postać, aby można ją było wyleczyć.
@editable
HealingThreshold:float = 50.0
# Jak długo czekać między leczeniem postaci
@editable
HealingDelay:float = 1.5
# O ile punktów leczyć postacie w każdej instancji leczenia
@editable
HealingAmount:float = 5.0
# Wolumen, do którego wchodzą postacie, aby otrzymać leczenie.
@editable
HealVolume:mutator_zone_device = mutator_zone_device{}
# Generator efektów wizualnych odtwarzający efekty wizualne w trakcie leczenia postaci.
@editable
VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
# Agent, za którym ma podążać medyk w trakcie leczenia
var AgentToFollow:?agent = false
# Kolejka agentów do wyleczenia w przypadku, gdy do wolumenu leczenia wchodzi wielu agentów.
var AgentsToHeal<public>:queue(agent) = queue(agent){}
# Używane do określenia, jak szybko aktualizować pozycję HealVolume i VFXSpawner
UpdateRateSeconds<private>:float = 0.1
OnBegin<override>()<suspends>:void=
VFXSpawner.Disable()
HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters)
HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
if:
# Pobierz agenta (postać SI), z którym jest powiązane to zachowanie.
Agent := GetAgent[]
# Pobierz interfejs fort_character agenta, aby uzyskać dostęp do charakterystycznych dla postaci Fortnite zachowań, zdarzeń, funkcji i interfejsów.
Character := Agent.GetFortCharacter[]
# Pobierz interfejs navigatable postaci, aby ustawić konkretne cele, do których ma się przemieszczać postać.
Navigatable := Character.GetNavigatable[]
# Pobierz interfejs focus_interface postaci, aby ustawić konkretne cele, na których postać ma się skupiać, gdy się do nich przemieści.
Focusable := Character.GetFocusInterface[]
then:
# Ustaw HealVolume i VFXSpawner, aby stale podążały za postacią niezależną
spawn{DeviceFollowCharacter()}
loop:
# Pobierz kolejnego agenta z kolejki do leczenia. Jeśli istnieje agent, który ma być wyleczony, wylecz go, wywołując AgentToHeal.
# Jeśli nie ma agentów do leczenia, poczekaj, aż do HealVolume wejdzie jakiś agent
if:
DequeueResult := AgentsToHeal.Dequeue[]
set AgentsToHeal = DequeueResult(0)
AgentToHeal := DequeueResult(1)
then:
PrintNPCB("Usunięto z kolejki kolejnego agenta do wyleczenia")
HealCharacter(AgentToHeal, Navigatable, Focusable)
else:
PrintNPCB("Kolejka AgentsToHeal jest pusta!")
Sleep(HealingDelay)
HealVolume.AgentEntersEvent.Await()
else:
# Jeśli w tym miejscu kod ulegnie awarii, to znaczy, że coś się nie udało podczas zbierania agenta i jego interfejsów
PrintNPCB( "Błąd w skrypcie zachowania postaci niezależnej przy konfiguracji postaci niezależnej",
?Duration := 6.0,
?TextColor := NamedColors.Red )
# Wylecz postać, następnie odczekaj ilość czasu określoną przez HealingDelayAmount.
# Kończy się, gdy zdrowie postaci osiągnie wartość HealingThreshold
# lub gdy postać opuści HealVolume.
HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
# Lecz postać tylko, gdy znajduje się wewnątrz HealVolume
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
PrintNPCB("Postać jest wewnątrz wolumenu, rozpoczynanie leczenia")
NavigationTarget := MakeNavigationTarget(AgentToHeal)
branch:
Navigatable.NavigateTo(NavigationTarget)
Focusable.MaintainFocus(AgentToHeal)
VFXSpawner.Enable()
defer:
VFXSpawner.Disable()
race:
loop:
CurrentHealth := CharacterToHeal.GetHealth()
if(CurrentHealth + HealingAmount > HealingThreshold):
if (CurrentHealth < HealingThreshold):
CharacterToHeal.SetHealth(HealingThreshold)
PrintNPCB("Postać osiągnęła próg leczenia HealingThreshold, kończenie leczenia")
break
else:
CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
Sleep(HealingDelay)
HealVolume.AgentExitsEvent.Await()
# Ustawia HealVolume i VFXSpawner, aby stale podążały za postacią poprzez zapętlenie
# MoveTo do pozycji postaci.
DeviceFollowCharacter()<suspends>:void=
if:
# Pobierz agenta (postać SI), z którym jest powiązane to zachowanie.
Agent := GetAgent[]
# Pobierz interfejs fort_character agenta, aby uzyskać dostęp do charakterystycznych dla postaci Fortnite zachowań, zdarzeń, funkcji i interfejsów.
Character := Agent.GetFortCharacter[]
then:
# Zapętl MoveTo na HealVolume i VFXSpawner, aby dopasować ich pozycję do
# postaci niezależnej
loop:
CharacterTransform := Character.GetTransform()
VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
Sleep(UpdateRateSeconds)
# Gdy agent wejdzie do HealVolume, dodaj go do
# kolejki AgentsToHeal, jeśli nie jest postacią niezależną.
OnAgentEnters(EnteredAgent:agent):void=
PrintNPCB("Agent wszedł do wolumenu leczenia")
if (EnteredAgent <> GetAgent[]):
set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
# Gdy agent opuści HealVolume, PrintNPCB w dzienniku
OnAgentExits(ExitAgent:agent):void=
PrintNPCB("Agent opuścił wolumen leczenia")
# Niestandardowa otoka, która zapewnia domyślny czas trwania i kolor.
PrintNPCB(Msg:string,?Duration:float = 3.0, ?TextColor:color = NamedColors.Green):void =
Print("[new_npc_behavior] {Msg}", ?Color := TextColor, ?Duration := Duration)
# Ta funkcja jest uruchamiana, gdy postać niezależna zostaje odspawnowana albo zlikwidowana ze świata.
OnEnd<override>():void =
if(Agent := GetAgent[]):
Print(medic_example_message_module.OnEndMessage(Agent))
else:
PrintNPCB("OnEnd")
queue.verse
list(t:type) := class:
Data:t
Next:?list(t)
queue<public>(t:type) := class<internal>:
Elements<internal>:?list(t) = false
Size<public>:int = 0
Enqueue<public>(NewElement:t):queue(t) =
queue(t):
Elements := option:
list(t):
Data := NewElement
Next := Elements
Size := Size + 1
Dequeue<public>()<decides><transacts>:tuple(queue(t), t) =
List := Elements?
(queue(t){Elements := List.Next, Size := Size - 1}, List.Data)
Front<public>()<decides><transacts>:t = Elements?.Data
CreateQueue<public><constructor>(InData:t where t:type) := queue(t):
Elements := option:
list(t):
Data := InData
Next := false
Size := 1
We własnym zakresie
Dzięki temu przewodnikowi wiesz już, jak utworzyć postać medyka, automatycznie leczącego postacie, których zdrowie spadnie poniżej określonej wartości progowej. Wykorzystując zdobytą wiedzę, spróbuj utworzyć własną postać medyka z własnymi specjalnymi zachowaniami.
-
Czy potrafisz utworzyć medyka, który przechodzi między wolumenami obrażeń i leczenia na podstawie tego, czy w wolumenie znajduje się wróg?
-
A może utworzysz medyka, który do leczenia postaci używa zużywającego się zasobu? W jaki sposób medyk mógłby przywracać ten zasób? Czy może go przywrócić wraz z upływem czasu, czy poprzez atakowanie wrogów?