Sanitäter sind ein gewöhnlicher Charaktertyp in vielen Spielen. Die Aufgabe eines Sanitäters ist es, Charaktere in der Nähe zu heilen; außerdem helfen sie ihren Teamkameraden, sich nach einem Schaden zu erholen. Sanitäter haben je nach Spiel unterschiedliche Rollen, z. B. Ärzte, die Patienten in einem Krankenhaus versorgen, Militärsanitäter, die ihrem Team sowohl im Kampf als auch bei der Heilung helfen, oder neutrale Stationen, die jeden heilen.
Der Sanitäter-Charakter, den du in diesem Beispiel erstellst, folgt einer Reihe von Logikregeln.
- Inaktiv:
- Beginnen, Agents zu heilen
- Heilen-Schleife
- Zum Agent navigieren
Der Sanitäter beginnt inaktiv und patrouilliert, bis ein Agent die Heilungszone betritt. Dieser Agent wird der Warteschlange des Sanitäters für die Heilung hinzugefügt. Der Sanitäter muss den Agent verfolgen, den er als Nächstes heilen muss, und eine Warteschlange ist eine nützliche Datenstruktur für diesen Zweck, da Warteschlangen eine First-in-First-out-Datenstruktur aufweisen. Das bedeutet, dass der Charakter, der die Heilungszone zuerst betritt, auch als erster geheilt wird.
Sobald der Sanitäter den Agent geholt hat, den er als Nächstes heilen muss, prüft er zunächst, ob die Gesundheit des Agents unter dem Schwellenwert für die Heilung liegt. Ist dies der Fall, beginnt er mit der Heilung mit einer bestimmten Rate, bis die Gesundheit des Agents den Schwellenwert erreicht oder der Agent die Heilungszone verlässt. Während der Heilung versucht der Sanitäter, in der Nähe des Agents zu bleiben, indem er ständig zu ihm hin navigiert. Sobald die Gesundheit des Agents wieder auf dem Schwellenwert ist, holt der Sanitäter den nächsten zu heilenden Agent und beginnt den Vorgang von neuem. Wenn es keine Agents zu heilen gibt, geht der Sanitäter wieder in den Inaktiv-Zustand über.
Du kannst die Logik des Sanitäter-NPCs mit dem folgenden endlichen Automaten visualisieren. Weitere Informationen über endliche Automaten findest du unter NPC-Verhalten verstehen.

Durch den Abschluss dieser Anleitung lernst du, wie du mit dem NPC-Verhaltensskript einen benutzerdefinierten Sanitäter-Charakter erstellst, der andere Charaktere in der Nähe heilt, wenn ihre Gesundheit unter einem bestimmten Schwellenwert liegt. Das vollständige Script ist am Ende dieses Leitfadens als Referenz enthalten.
Ein neues NPC-Verhaltensskript erstellen
Um deinen eigenen NPC-Sanitäter-Charakter zu erstellen, erstelle ein neues NPC-Verhaltensskript mit dem Namen medic_example. Weitere Informationen zum Erstellen deines eigenen NPC-Verhaltensskripts findest du unter Dein eigenes NPC-Verhalten erstellen. Öffne die Verse-Datei in Visual Studio Code
Befolge diese Schritte, um ein NPC-Verhaltensskript in UEFN zu erstellen, das einen Sanitäter-Charakter spawnen lässt, der Spieler in der Nähe heilt.
Implementierung der Warteschlange für die Heilung
Das NPC-Verhaltensskript beginnt mit mehreren Werten, die für die Charakterbewegung und die Debugging-Visualisierung verwendet werden. Du wirst nicht alle in diesem Skript brauchen, also entfernst du den überflüssigen Code jetzt.
-
Entferne oben in der Klassendefinition von „medic_example“ die Werte vor „OnBegin()“. Dein Sanitäter-Charakter wartet nicht darauf, sich zu den Charakteren zu bewegen, sondern folgt ihnen stattdessen bei der Heilung. Für dieses Beispiel brauchst du die Debugging-Werte nicht, und du wirst andere Objekte verwenden, um zu visualisieren, wann dein Sanitäter Charaktere heilt.
-
Entferne in „OnBegin()“ den Code innerhalb der „then“-Anweisung, nachdem du die Interfaces des Charakters in der ersten „if“-Anweisung erhalten hast. Dein Sanitäter-Charakter muss nach dem Spawnen nicht mehr zwischen den Punkten hin und her laufen, sondern patrouilliert um seinen Spawnpunkt herum und wartet auf Charaktere, die geheilt werden müssen.
-
Entferne die Funktionen
DrawDebugLocation()
undDrawDebugLookAt()
. Da du die Debugging-Werte in diesem Beispiel nicht verwendest, brauchst du auch die assoziierten Funktionen nicht, die sie benutzen.
Nachdem du den überflüssigen Code entfernt hast, kannst du mit dem Erstellen deines Sanitäter-Charakters beginnen.
-
Füge am oben in der Klassendefinition von „medic_example“ die folgenden Werte ein:
-
bearbeitbarer Float „HealingThreshold“. Dies ist der Schwellenwert der Gesundheit, unter dem ein Charakter liegen muss, um Heilung zu erhalten.
# Der Kondition-Schwellenwert, den ein Charakter haben muss, bevor er geheilt werden kann. @editable HealingThreshold:float = 50.0
-
Füge einen bearbeitbaren Float
HealingDelay
hinzu. Dies ist die Zeit, die bei der Heilung von Charakteren zwischen den einzelnen Instanzen der Heilung gewartet werden muss. Ändere diesen Wert, je nachdem, ob du möchtest, dass dein Sanitäter langsamer oder schneller heilt.# Der Kondition-Schwellenwert, den ein Charakter haben muss, bevor er geheilt werden kann. @editable HealingThreshold:float = 50.0 # Wie lange gewartet werden soll, bevor Charaktere geheilt werden @editable HealingDelay:float = 1.5
-
Ein bearbeitbarer Float „HealingAmount“. Dies ist die Menge an Kondition, die Charaktere pro Heilungsinstanz heilen können. Wenn dein Sanitäter-NPC einen Charakter heilt, heilt er ihn alle „HealingDelay“ Sekunden um einen Betrag von „HealingAmount“.
# Wie lange gewartet werden soll, bevor Charaktere geheilt werden @editable HealingDelay:float = 1.5 # Wie viel Charaktere pro Heilungsinstanz geheilt werden @editable HealingAmount:float = 5.0
Ein Eine bearbeitbare Mutator-Zone „HealVolume“. Dies ist das Volumen, das die Charaktere betreten, um Heilung zu erhalten. In diesem Beispiel verwendest du eine Mutator-Zone, weil die Mutator-Zone ein „AgentEntersEvent“ hat, das dein Sanitäter abonnieren und auf Charaktere prüfen kann, die Heilung brauchen könnten.
# Wie viel Charaktere pro Heilungsinstanz geheilt werden @editable HealingAmount:float = 5.0 # Das Volumen, das Charaktere betreten, um Heilung zu erhalten. @editable HealVolume:mutator_zone_device = mutator_zone_device{}
-
Ein bearbeitbarer VFX-Spawner „VFXSpawner“. Visuelles Feedback ist wichtig, um zu wissen, dass dein Code funktioniert. Deshalb benutzt du einen VFX-Spawner, um Effekte zu spawnen, wenn ein Charakter geheilt wird.
# Das Volumen, das Charaktere betreten, um Heilung zu erhalten. @editable HealVolume:mutator_zone_device = mutator_zone_device{} # Der VFX-Spawner, um VFX abzuspielen, während Charaktere geheilt werden. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
-
Eine optionale Variable „Agent“ mit dem Namen „AgentToFollow“. Hier wird eine Referenz auf den Charakter gespeichert, dem der Sanitäter bei seiner Heilung folgen soll.
# Der VFX-Spawner, um VFX abzuspielen, während Charaktere geheilt werden. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} # Der Agent, dem gefolgt werden soll, während er geheilt wird var AgentToFollow:?agent = false
-
Eine variable Warteschlange von Agents mit dem Namen „AgentsToHeal“. Wenn mehrere Charaktere Heilung brauchen, heilt dein Sanitäter die Charaktere in der Reihenfolge, in der sie das „HealVolume“ betreten haben. Den Code für die Warteschlange richtest du im nächsten Schritt ein. Weitere Informationen über die Datenstruktur der Warteschlange findest du unter Stapel und Warteschlangen in Verse.
# Der Agent, dem gefolgt werden soll, während er geheilt wird var AgentToFollow:?agent = false # Die Warteschlange der Agents, die geheilt werden sollen, wenn mehrere Agents das Heilen-Volumen betreten. var AgentsToHeal<public>:queue(agent) = queue(agent){}
-
Eine Float-Variable „UpdateRateSeconds“. Das ist die Zeit, die zwischen dem Aktualisieren der Position des „HealVolume“ und des „VFXSpawner“ gewartet wird.
# Die Warteschlange der Agents, die geheilt werden sollen, wenn mehrere Agents das Heilen-Volumen betreten. var AgentsToHeal<public>:queue(agent) = queue(agent){} # Wird verwendet, um festzulegen, wie schnell die Position von HealVolume und VFXSpawner aktualisiert werden soll UpdateRateSeconds<private>:float = 0.1
-
-
Für die Implementierung der
AgentsToHeal
-Warteschlange verwendest du den Code am Ende dieses Schrittes.- Klicke im Verse-Explorer mit der rechten Maustaste auf deinen Projektnamen und wähle Neue Verse-Datei zum Projekt hinzufügen aus, um das Fenster Verse-Script erstellen zu öffnen.
-
Klicke im Fenster „Verse-Skript erstellen“ auf Verse-Klasse, um sie als dein Skript auszuwählen.
-
Benenne deine Verse-Klasse, indem du den Text im Feld Klassenname in
Warteschlange
änderst. -
Klicke auf Erstellen, um die Verse-Datei zu erstellen.
-
Doppelklicke im Verse-Explorer auf den Namen deiner Verse-Datei, um sie in Visual Studio Code zu öffnen.
-
Ersetze den Code in deiner
Warteschlange
-Datei durch den folgenden Code. Dieser Code implementiert eine generische Warteschlange vom TypTyp
mit einer Listendatenstruktur. Dies ist ein Beispiel für einen parametrischen Typ, da die Implementierung der Warteschlange unabhängig von dem Typ funktioniert, aus dem du sie erstellst. In deinem Beispiel verwendest du eine Warteschlange für Charaktere, also wird deine Warteschlangendefinition in „medic_example“queue(agent)
sein.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
-
Die Einteilung deiner Verse-Skripte in verschiedene Ordner kann dir bei der Organisation helfen und bietet dir die Möglichkeit, auf gewöhnlich verwendete Dateien zu verweisen. Als Beispiel dafür erstellst du in diesem Projekt einen Ordner, in dem du dein NPC-Verhalten speicherst. Klicke in Visual Studio Code auf die Schaltfläche Neuer Ordner, um einen neuen Ordner in deinem UEFN-Projekt zu erstellen. Nenne den Ordner
npc_behaviors
. Ziehe dann deine Dateimedic_example
Verse in den neuen Ordner.
Dein Code in „medic_example“ sollte nun korrekt kompiliert werden.
Charaktere innerhalb eines Volumens heilen
Wenn ein verletzter Charakter das „HealVolume“ betritt, sollte dein Sanitäter-Charakter damit beginnen, ihn zu heilen, wenn seine Kondition kleiner ist als der „HealingThreshold“. Sobald die Kondition des Charakters über dem „HealingThreshold“ liegt, soll dein Sanitäter die Heilung des Charakters stoppen und sich zum nächsten Charakter bewegen, der Heilung benötigt. Bei mehreren Charakteren sollte dein Sanitäter die Charaktere in der Reihenfolge heilen, in der sie das „HealVolume“ betreten haben. Befolge diese Schritte, um Charaktere zu heilen, wenn sie das „HealVolume“ betreten.
-
Beginne in deiner Datei
medic_example
inOnBegin()
nach derthen
-Anweisung eineSchleife
. Innerhalb derSchleife
holst du das Ergebnis der FunktionDequeue()
aus der WarteschlangeAgentsToHeal
und speicherst es in der VariableDequeueResult
.then: loop: # Hole den nächsten Agent in der Warteschlange zur Heilung. Wenn es einen Agent zu heilen gibt, heile ihn, indem du AgentToHeal aufrufst. # Wenn es keine Agents zu heilen gibt, warte, bis ein Agent das HealVolume betritt if: DequeueResult := AgentsToHeal.Dequeue[]
-
Die Variable „DequeueResult“ ist ein „Tupel“, das sowohl eine Kopie der „AgentsToHeal“-Warteschlange, aus der das erste Element entfernt wurde, als auch den Agent vorne in der Warteschlange zurückgibt. Aktualisiere „AgentsToHeal“, indem du ihn auf den ersten Wert im Tupel einstellst und den zweiten Wert als „AgentToHeal“ speicherst.
if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1)
-
Sobald du den zu heilenden Agent hast, musst du mit der Heilung beginnen, solange er sich im „HealVolume“ befindet. Um dies zu handhaben, definierst du eine neue Funktion mit dem Namen
HealCharacter()
. Füge eine neue Funktion mit dem NamenHealCharacter()
zur Klassendefinition von „medic_example“ hinzu. Diese Funktion übernimmt denAgentToHeal
und sowohl dasNavigatable
- als auch dasFocusable
-Interface des Sanitäter-Charakters als Funktionsargumente. Füge den<suspends>
-Modifikator zu dieser Funktion hinzu, da sie bei der Heilung eines Charakters mehrere asynchrone Aufgaben ausführen muss.# Heile den Charakter und warte dann eine Zeit lang (HealingDelayAmount). # Endet, wenn die Kondition des Charakters den HealingThreshold erreicht # oder der Charakter das HealVolume verlässt. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
-
Prüfe in
HealCharacter
, ob sichAgentToHeal
“ im Volumen befindet, indem duIsInVolume[]
aufrufst undAgentToHeal
als Argument übergibst. Wenn sich der Agent im Volumen befindet, kannst du mit seiner Heilung beginnen. Alle heilbaren Agents implementieren dashealthful
-Interface, das Teil desfort_character
des Agent ist. Rufe denfort_character
des Agent ab und speichere ihn in einemCharacterToHeal
-Wert.HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Heile den Charakter nur, wenn er sich innerhalb des HealVolume befindet if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[]
Ein Wenn der Charakter zur Heilung bereit ist, musst du darauf achten, dass dein Sanitäter in der Nähe des zu heilenden Charakters bleibt. Erstelle mit „MakeNavigationTarget“ ein „navigation_target“ aus „AgentToHeal“ und speichere es in einer Variable „NavigationTarget“. Rufe dann in einer „branch“-Anweisung die Funktion „NavigateTo()“ auf und verwende das „navigatable“-Interface des NPCs, damit dein Sanitäter zum „AgentToHeal“ navigieren kann. Rufe auch in der „branch“-Funktion die Funktion „MaintainFocus()“ auf, um sicherzustellen, dass dein Sanitäter den „AgentToHeal“ fokussiert. Wenn du in diesem Kontext eine „branch“-Anweisung verwendest, kannst du sowohl „NavigateTo()“ als auch „MaintainFocus()“ asynchron zur gleichen Zeit ausführen und jeden Code nach dem „branch“ sofort ausführen. Mehr Informationen zu Verzweigungsausdrücken findest du auf der Seite Verzweigung in Verse.
# Heile den Charakter nur, wenn er sich innerhalb des HealVolume befindet
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
Print("Character im Volumen, Heilung starten")
NavigationTarget := MakeNavigationTarget(AgentToHeal)
branch:
Navigatable.NavigateTo(NavigationTarget)
Focusable.MaintainFocus(AgentToHeal)
-
Aktiviere den
VFXSpawner
, um VFX abzuspielen, wenn dein Sanitäter einen Charakter heilt. Deaktiviere dann in einemdefer
-Ausdruck denVFXSpawner
. Da der Code zum Deaktivieren desVFXSpawner
in einemdefer
-Ausdruck steht, wird er erst ausgeführt, wenn der aktuelle Geltungsbereich beendet wird. In diesem Fall bedeutet das, dass der Code erst ausgeführt wird, wenn die Funktion endet, sodass er garantiert das Letzte ist, was in der Funktion passiert. Mehr Informationen über defer-Ausdrücke findest du auf der Seite defer.branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable()
-
Wenn du den zu heilenden Charakter heilst, sollte die Heilung stoppen, wenn eine von zwei Bedingungen eintritt. Entweder die Kondition des Charakters wird über den „HealingThreshold“ hinaus geheilt, oder der Charakter verlässt das „HealVolume“. Um dies zu erreichen, verwendest du einen „race“-Ausdruck. Richte einen „race“-Ausdruck zwischen einer „Schleife“ und einem „Await()“ auf dem „HealVolume.AgentExitsEvent“ ein.
branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: HealVolume.AgentExitsEvent.Await()
-
Innerhalb der Schleife ermittelst du die aktuelle Kondition des Charakters mit
GetHealth()
und speicherst sie in einem WertCurrentHealth
. Prüfe dann in einer if-Anweisung, obCurrentHealth
plusHealingAmount
größer als derHealingThreshold
ist. Wenn ja, soll dein Sanitäter die Heilung stoppen und die Schleife perbreak
verlassen. Ist die Kondition des Charakters jedoch nur etwas kleiner als der Heilungsschwellenwert, willst du ihn bis zum Heilungsschwellenwert heilen. Füge eine zweite if-Anweisung innerhalb der ersten ein, die prüft, ob der aktuelle Wert vonCurrentHealth
kleiner als derHealingThreshold
ist. Wenn ja, setze die Kondition des Charakters auf denHealingThreshold
.race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character hat das HealingThreshold erreicht, Heilung stoppen") break HealVolume.AgentExitsEvent.Await()
-
Andernfalls, wenn der Wert von
CurrentHealth
plusHealingAmount
nicht größer ist als derHealingThreshold
, wird die Kondition des Charakters aufCurrentHealth
plusHealingAmount
gesetzt.if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character hat das HealingThreshold erreicht, Heilung stoppen") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
-
Schlafe am Ende der „Schleife“ für eine Zeitspanne von „HealingDelay“. Ohne diesen Schlaf werden die Charaktere bei jeder Simulationsaktualisierung geheilt, so dass „HealingDelay“ verhindert, dass sie sofort geheilt werden. Dein fertiger „HealCharacter()“-Code sollte wie folgt aussehen.
# Heile den Charakter und warte dann eine Zeit lang (HealingDelayAmount). # Endet, wenn die Kondition des Charakters den HealingThreshold erreicht # oder der Charakter das HealVolume verlässt. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Heile den Charakter nur, wenn er sich innerhalb des HealVolume befindet if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Character im Volumen, Heilung starten") 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("Character hat das HealingThreshold erreicht, Heilung stoppen") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) Sleep(HealingDelay) HealVolume.AgentExitsEvent.Await()
-
Zurück in
OnBegin()
, imthen
-Ausdruck innerhalb deinerSchleife
, rufst duHealCharacter()
auf, indem du denAgentToHeal
, dasNavigable
-Interface und dasFocusable
-Interface übergibst.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Nächster Agent zum Heilen aus der Warteschlange genommen") HealCharacter(AgentToHeal, Navigatable, Focusable)
-
Dein Sanitäter hat nicht immer einen Charakter in seiner Nähe, den er heilen kann, und die Funktion „Dequeue[]“ schlägt fehl, wenn sich keine Agents in der „AgentsToHeal“-Warteschlange befinden. Um dies zu handhaben, füge am Ende der „Schleife“ eine „else“-Anweisung hinzu. In dieser if-Anweisung rufst du „Sleep()“ für eine durch „HealingDelay“ bestimmte Zeit auf und wartest dann per „Await()“ auf das „HealVolume.AgentEntersEvent“. Auf diese Weise ruft dein Sanitäter-Charakter nicht endlos „Dequeue[]“ in der „AgentsToHeal“-Warteschlange auf, sondern wartet darauf, dass ein neuer Charakter das „HealVolume“ betritt, bevor er die Schleife neu startet. Deine fertige Schleife sollte wie folgt aussehen.
loop: # Hole den nächsten Agent in der Warteschlange zur Heilung. Wenn es einen Agent zu heilen gibt, heile ihn, indem du AgentToHeal aufrufst. # Wenn es keine Agents zu heilen gibt, warte, bis ein Agent das HealVolume betritt if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Nächster Agent zum Heilen aus der Warteschlange genommen") HealCharacter(AgentToHeal, Navigatable, Focusable) else: Print(„AgentsToHeal ist leer!") Sleep(HealingDelay) HealVolume.AgentEntersEvent.Await()
Verfolgung, wenn Charaktere im Heilen-Volumen sind
Um zu wissen, wann Charaktere das „HealVolume“ betreten oder verlassen, abonnierst du „AgentEntersEvent“ und „AgentExitsEvent“ des „HealVolume“ zu neuen Funktionen.
-
Füge eine neue Funktion mit dem Namen
OnAgentEnters()
zur Klassendefinition vonmedic_example
hinzu. Diese Funktion übernimmt den Agent, der gerade dasHealVolume
betreten hat, und stellt ihn in dieAgentsToHeal
-Warteschlange.OnAgentEnters(EnteredAgent:agent):void= Print("Agent ist dem Heilungsvolumen beigetreten")
-
Prüfe in „OnAgentEnters()“, ob der Agent im Volumen nicht der Sanitäter-Charakter ist. Wenn ja, setze die „AgentsToHeal“-Warteschlange auf das Ergebnis des Aufrufs von „Enqueue[]“ mit dem „EnteredAgent“. Deine abgeschlossene Funktion „OnAgentEnters()“ sollte wie folgt aussehen:
OnAgentEnters(EnteredAgent:agent):void= Print("Agent ist dem Heilungsvolumen beigetreten") if (EnteredAgent <> GetAgent[]): set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
-
Wenn ein Agent das „HealVolume“ verlässt, musst du ihn nicht aus der „AgentsToHeal“-Warteschlange entfernen. Das liegt daran, dass die Schleife in „OnBegin()“ bereits „Dequeue[]“ in einer Schleife aufruft. Du möchtest aber vielleicht einen Code ausführen, wenn ein Agent das Volumen in deinen Beispielen verlässt, also richtest du jetzt eine Funktion dafür ein. Füge eine neue Funktion mit dem Namen „OnAgentExits()“ zur Klassendefinition von „medic_example“ hinzu.
OnAgentExits(ExitAgent:agent):void= Print("Agent hat das Heilungsvolumen verlassen")
-
Abonniere in „OnBegin()“ das „AgentEntersEvent“ und das „AgentExitsEvent“ des „HealVolume“ zu „OnAgentEnters“ bzw. „OnAgentExits“. Da er deaktiviert starten soll, ist dies ein guter Ort, um „Disable()“ für den Charakter-Spawner aufzurufen.
OnBegin<override>()<suspends>:void= Print("Hallo, KI!") VFXSpawner.Disable() HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters) HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
Bewegen des Heilen-Volumens mit dem Sanitäter
Wenn sich der Sanitäter-Charakter bewegt, muss sich das „HealVolume“ mitbewegen, um seiner aktuellen Position zu entsprechen. Dasselbe gilt für den „VFXSpawner“. Dazu verwendest du eine neue Funktion „DeviceFollowCharacter()“.
-
Füge eine neue Funktion mit dem Namen
DeviceFollowCharacter()
zur Klassendefinition vonmedic_example
hinzu. Diese Funktion muss asynchron laufen, um die Positionen der Geräte kontinuierlich zu aktualisieren, also füge ihr den Modifikator<suspends>
hinzu.DeviceFollowCharacter()<suspends>:void=
-
In der Funktion
DeviceFollowCharacter()
holst du dir denfort_character
des Sanitäters, indem du zuerst den Agent mitGetAgent[]
abrufst und dannGetFortCharater[]
aufrufst.DeviceFollowCharacter()<suspends>:void= if: # Hole den Agent (KI-Charakter), mit dem dieses Verhalten assoziiert ist. Agent := GetAgent[] # Hole das fort_character-Interface des Agents, um Zugang zu Fortnite-spezifischen Verhaltensweisen, Events, Funktionen und Interfaces zu erhalten. Character := Agent.GetFortCharacter[]
-
Jetzt musst du das
HealVolume
und denVFXSpawner
kontinuierlich an die Position desCharacter
bewegen. Dazu führst du auf beiden Geräten eine SchleifeMoveTo()
durch. Starte eineSchleife
, hole die Transformation desCharacter
und speichere sie in der VariableCharacterTransform
.if: # Hole den Agent (KI-Charakter), mit dem dieses Verhalten assoziiert ist. Agent := GetAgent[] # Hole das fort_character-Interface des Agents, um Zugang zu Fortnite-spezifischen Verhaltensweisen, Events, Funktionen und Interfaces zu erhalten. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform()
-
Rufe „MoveTo()“ für den „VFXSpawner“ und das „HealVolume“ auf und bewege sie zu „CharacterTransform.Translation“ und „CharacterTransform.Rotation“. Setze die Dauer auf „UpdateRateSeconds“ Sekunden. Rufe schließlich „Sleep()“ für eine durch „UpdateRateSeconds“ bestimmte Zeit auf, um zu verhindern, dass die Geräte ihre Position bei jeder Simulationsaktualisierung aktualisieren. Das Aktualisieren der Geräteposition bei jeder Simulationsaktualisierung kann zu unruhigen Bewegungen auf deinen Geräten führen. Dein fertiger „DeviceFollowCharacter()“-Code sollte wie folgt aussehen.
DeviceFollowCharacter()<suspends>:void= if: # Hole den Agent (KI-Charakter), mit dem dieses Verhalten assoziiert ist. Agent := GetAgent[] # Hole das fort_character-Interface des Agents, um Zugang zu Fortnite-spezifischen Verhaltensweisen, Events, Funktionen und Interfaces zu erhalten. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) Sleep(UpdateRateSeconds)
Ein In „OnBegin()“, nach der „if“-Anweisung, in der du deine Charakter-Interfaces speicherst, aber vor der Schleife, spawne eine Instanz von „DeviceFollowCharacter()“.
Deinen Charakter zum Level hinzufügen
-
Erstelle eine neue NPC-Charakterdefinition mit dem Namen Medic. Klicke auf deine neue NPC-Charakterdefinition, um den Bildschirm NPC-Charakterdefinition zu öffnen.
-
Modifiziere im Bildschirm NPC-Charakterdefinition die folgenden Eigenschaften:
Ein Setze unter NPC-Charaktertyp Typ auf Wächter. Über das Wächter-Interface hast du Zugang zu wächterspezifischen Funktionalitäten, wie z. B. Events, wenn der Wächter alarmiert oder misstrauisch ist, und du kannst Wächter als Verbündete anheuern. Wächter können auch Waffen ausrüsten, während benutzerdefinierte und Wildtier-Typen dies derzeit nicht können. Du kannst auch den Namen deines Charakters unter dem Tab Name ändern.
-
Setze unter Verhalten des NPC-Charakters Verhalten auf Verse-Verhalten. Setze dann das NPC-Verhaltensskript auf „medic_example“. Dein Charakter hat weiterhin Zugang zu den Funktionalitäten des Wächter-Interfaces, entscheidet aber mithilfe deines Verse-Skriptes, was er während „OnBegin“ und „OnEnd“ tun soll.
-
Klicke im Tab Modifikatoren unter Wächter-Spawn-Modifikator auf den Tab Kosmetik, um das kosmetische Aussehen deines Charakters anzupassen. Du kannst aus einer bereits vorhandenen Kosmetik auswählen oder Charakterkosmetik-Retargeting aktivieren, um ein benutzerdefiniertes Modell zu verwenden. Beachte, dass nur Wächter und benutzerdefinierte Typen das Kosmetik-Retargeting nutzen können, Wildtiere hingegen nicht. Weitere Informationen über Modifikatoren und welche Modifikatoren auf verschiedene Typen angewendet werden, findest du auf der Seite Charakterdefinition.
-
-
Speichere deine NPC-Charakterdefinition. Ziehe im Inhaltsbrowser deine NPC-Charakterdefinition ins Level. Dadurch wird automatisch ein neuer Charakter-Spawner erstellt und deine NPC-Charakterdefinition diesem zugewiesen.

-
Ziehe eine Mutator-Zone und ein VFX-Spawner-Gerät ins Level.
-
Wähle deinen Charakter-Spawner aus. Im Outliner unter Benutzeroptionen:
-
Setze AIBehavior-Skript-Override auf dein Skript
medic_example
. Das Außerkraftsetzen desAIBehavior
-Skripts im Outliner erlaubt es dir, Geräte im Level zu referenzieren, und du brauchst diese Funktionalität, um dein HealVolume und deinen VFXSpawner zuzuweisen. -
Setze HealVolume auf die Mutator-Zone und VFXSpawner auf den VFX-Spawner, den du im Level platziert hast.
-

-
Wähle deine Mutatorzone. Setze im Outliner unter Benutzeroptionen die Option Zone während des Spiels sichtbar auf True. So kannst du visualisieren, wo sich das
HealVolume
befindet und wie es sich mit dem Sanitäter-Charakter bewegt. -
Wähle deinen VFX-Spawner. Setze im Outliner unter Benutzeroptionen Visueller Effekt auf einen Effekt deiner Wahl. In diesem Beispiel wird der Effekt Blasen verwendet, um Heilung zu vermitteln, aber du möchtest vielleicht etwas anderes verwenden, wie zum Beispiel ein Feuerwerk oder Funken. Ändere den visuellen Effekt, um ihn an die Bedürfnisse deines Charakters anzupassen.
-
Klicke auf Sitzung starten in der UEFN-Symbolleiste, um dein Level zu testen. Wenn du einen Spieltest machst, sollte dein Charakter verletzte Charaktere heilen, welche die Mutator-Zone betreten. Bei der Heilung eines Charakters sollten die VFX abgespielt werden und der Sanitäter sollte dem zu heilenden Charakter folgen und diesen fokussieren.

Vollständiges Script
Das folgende Skript ist ein vollständiges Skript für einen NPC-Charakter, der Charaktere heilt, deren Kondition unter einem bestimmten Schwellenwert liegt.
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 }
# Ein in Verse verfasstes NPC-Verhalten, das in einer NPC-Definition oder einem dem Verhaltensskript-Override eines Charakter-Spawner-Geräts verwendet werden kann.
medic_example<public> := class(npc_behavior):
# Der Kondition-Schwellenwert, den ein Charakter haben muss, bevor er geheilt werden kann.
@editable
HealingThreshold:float = 50.0
# Wie lange gewartet werden soll, bevor Charaktere geheilt werden
@editable
HealingDelay:float = 1.5
# Wie viel Charaktere pro Heilungsinstanz geheilt werden
@editable
HealingAmount:float = 5.0
# Das Volumen, das Charaktere betreten, um Heilung zu erhalten.
@editable
HealVolume:mutator_zone_device = mutator_zone_device{}
# Der VFX-Spawner, um VFX abzuspielen, während Charaktere geheilt werden.
@editable
VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
# Der Agent, dem gefolgt werden soll, während er geheilt wird
var AgentToFollow:?agent = false
# Die Warteschlange der Agents, die geheilt werden sollen, wenn mehrere Agents das Heilen-Volumen betreten.
var AgentsToHeal<public>:queue(agent) = queue(agent){}
# Wird verwendet, um festzulegen, wie schnell die Position von HealVolume und VFXSpawner aktualisiert werden soll
UpdateRateSeconds<private>:float = 0.1
OnBegin<override>()<suspends>:void=
VFXSpawner.Disable()
HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters)
HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
if:
# Hole den Agent (KI-Charakter), mit dem dieses Verhalten assoziiert ist.
Agent := GetAgent[]
# Hole das fort_character-Interface des Agents, um Zugang zu Fortnite-spezifischen Verhaltensweisen, Events, Funktionen und Interfaces zu erhalten.
Character := Agent.GetFortCharacter[]
# Hole das navigatable-Interface des Charakters, um bestimmte Ziele festzulegen, zu denen der Charakter reisen soll.
Navigatable := Character.GetNavigatable[]
# Hole das focus_interface des Charakters, um bestimmte Ziele festzulegen, auf die der Fokus gerichtet werden soll, nachdem du zu ihnen gereist bist.
Focusable := Character.GetFocusInterface[]
then:
# Lege das HealVolume und den VFXSpawner so fest, dass sie dem NPC-Charakter kontinuierlich folgen
spawn{DeviceFollowCharacter()}
loop:
# Hole den nächsten Agent in der Warteschlange zur Heilung. Wenn es einen Agent zu heilen gibt, heile ihn, indem du AgentToHeal aufrufst.
# Wenn es keine Agents zu heilen gibt, warte, bis ein Agent das HealVolume betritt
if:
DequeueResult := AgentsToHeal.Dequeue[]
set AgentsToHeal = DequeueResult(0)
AgentToHeal := DequeueResult(1)
then:
PrintNPCB("Nächster Agent zum Heilen aus der Warteschlange genommen")
HealCharacter(AgentToHeal, Navigatable, Focusable)
else:
PrintNPCB("AgentsToHeal ist leer!")
Sleep(HealingDelay)
HealVolume.AgentEntersEvent.Await()
else:
# Wenn der Code hier ausgeführt wird, ist etwas beim Sammeln des Agents und seiner Interfaces fehlgeschlagen
PrintNPCB("Fehler im NPC-Verhalten Script im NPC-Setup",
?Duration := 6.0,
?TextColor := NamedColors.Red )
# Heile den Charakter und warte dann eine Zeit lang (HealingDelayAmount).
# Endet, wenn die Kondition des Charakters den HealingThreshold erreicht
# oder der Charakter das HealVolume verlässt.
HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
# Heile den Charakter nur, wenn er sich innerhalb des HealVolume befindet
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
PrintNPCB("Character im Volumen, Heilung starten")
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("Character hat das HealingThreshold erreicht, Heilung stoppen")
break
else:
CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
Sleep(HealingDelay)
HealVolume.AgentExitsEvent.Await()
# Stellt das HealVolume und den VFXSpawner so ein, dass sie dem Charakter kontinuierlich in einer Schleife folgen
# MoveTo auf die Position des Charakters.
DeviceFollowCharacter()<suspends>:void=
if:
# Hole den Agent (KI-Charakter), mit dem dieses Verhalten assoziiert ist.
Agent := GetAgent[]
# Hole das fort_character-Interface des Agents, um Zugang zu Fortnite-spezifischen Verhaltensweisen, Events, Funktionen und Interfaces zu erhalten.
Character := Agent.GetFortCharacter[]
then:
# Schleife MoveTo auf dem HealVolume und dem VFXSpawner, um ihre Position mit der Position des
# NPC-Charakters abzugleichen
loop:
CharacterTransform := Character.GetTransform()
VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
Sleep(UpdateRateSeconds)
# Wenn ein Agent das HealVolume betritt, füge ihn zur
# AgentsToHeal-Warteschlange hinzu, wenn sie nicht der NPC-Charakter sind.
OnAgentEnters(EnteredAgent:agent):void=
PrintNPCB("Agent ist dem Heilungsvolumen beigetreten")
if (EnteredAgent <> GetAgent[]):
set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
# Wenn ein Agent das HealVolume verlässt, PrintNPCB in das Log
OnAgentExits(ExitAgent:agent):void=
PrintNPCB("Agent hat das Heilungsvolumen verlassen")
# Benutzerdefinierter Wrapper, der eine Default-Dauer und -Farbe bereitstellt.
PrintNPCB(Msg:string,?Duration:float = 3.0, ?TextColor:color = NamedColors.Green):void =
Print("[new_npc_behavior] {Msg}", ?Color := TextColor, ?Duration := Duration)
# Diese Funktion wird ausgeführt, wenn der NPC de-spawnt oder aus der Welt eliminiert wird.
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
Auf eigene Faust
Durch den Abschluss dieser Anleitung hast du gelernt, wie du einen Sanitäter-Charakter erstellst, der automatisch Charaktere unter einem bestimmten Schwellenwert heilt. Erstelle mit dem Gelernten deinen eigenen Sanitäter-Charakter mit seinen eigenen Verhaltensweisen.
-
Kannst du einen Sanitäter erstellen, der zwischen beschädigendem und heilendem Volumen tauscht, je nachdem, ob sich ein Feind in dem Volumen befindet?
-
Wie wäre es mit einem Sanitäter, der eine erschöpfbare Ressource nutzt, um Charaktere zu heilen? Wie würde der Sanitäter diese Ressource wiederherstellen? Könnte er es über die Zeit wiederherstellen oder könnte er es durch Angriffe auf Feinde wiederherstellen?