Der Charakter in diesem Beispiel verwendet eine benutzerdefinierte Charakterdefinition, da er sich nur bewegen muss und keinen Zugang auf die Wächter- oder Wildtier-API benötigt. Das Verhalten des Charakters wird über ein benutzerdefiniertes Verse-Verhalten mit der Bezeichnung verse_commander_character gesteuert.
Wächter sind nicht spielbare Charaktere (Non-Playable Characters, NPCs), die sich auf bestimmten Pfaden bewegen und zu Gegnern für feindliche Spieler werden können. Wildtiere sind Tiere, wie Huhn und Wildschwein, die sich auch entlang bestimmter Pfade bewegen und feindliche Spieler angreifen können.
Um den benutzerdefinierten NPC zu erstellen, erstelle ein neues NPC-Verhalten namens verse_commander_character mit dem Verse Explorer. Informationen zum Erstellen deines benutzerdefinierten NPC-Verhaltens findest du unter Benutzerdefiniertes NPC-Verhalten erstellen.
Der Charakter muss die folgenden Eigenschaften kennen und verwalten:
CommandWaitTime: Dies gibt an, wie lang zwischen den jeweiligen Befehlen gewartet werden soll.
FocusTime: Wie lange der Fokus auf ein Ziel erzwungen werden soll. Das Drehen des Charakters nach links oder rechts wird über das
focus_interfacedes Charakters verarbeitet, um ihn zu zwingen, sich einem bestimmten Punkt auf seiner linken oder rechten Seite zuzuwenden. Da das Zuwenden zu einem Ziel nicht abgeschlossen wird, außer es wird unterbrochen, wird diese Eigenschaft auf eine sehr niedrige Zahl gesetzt, damit nur genug Zeit bleibt, damit sich der Charakter dreht, um in eine Richtung zu blicken.ReachRadius: Dies gibt an, wie nah sich der Charakter seinem Navigationsziel nähern muss, damit davon ausgegangen wird, dass er es „erreicht“ hat.
VerseCommanderMinigame: Dies ist eine Referenz auf das VerseCommanderMinigame im Level. Damit lauscht der Charakter auf Befehle, die von dort kommen.
VFX and Arrow References: Diese verweisen auf die unterschiedlichen Grafikeffekte für den Teleport hinein/hinaus sowie auf das Vorwärtspfeil-Prop, durch das es einfacher ist, die Ausrichtung des Charakters zu sehen.
Verse# A Verse-authored NPC Behavior that can be used within an NPC Definition or a Character Spawner device's Behavior Script Override. verse_commander_character<public> := class(npc_behavior): # The VFX that play when the NPC teleports out. @editable CharacterTeleportOutVFX:vfx_spawner_device = vfx_spawner_device{} # The VFX that play when the NPC teleports in. @editable CharacterTeleportInVFX:vfx_spawner_device = vfx_spawner_device{}Da wir nun die Eigenschaften des Charakters definiert haben, wollen wir sein Verhalten und die Funktionen definieren, die es steuern.
Character-Bewegung
Der Charakter in diesem Spiel weist die folgenden Verhaltensweisen auf:
Vorwärts bewegen: Der Befehl Vorwärts bewegt den Charakter 1 Kachel auf dem Spielplan vorwärts.
Nach rechts drehen oder Nach links drehen: Die Befehle Nach rechts drehen und Nach links drehen drehen den Charakter um 90 Grad nach rechts bzw. nach links. Das muss außerdem erfolgen, ohne dass sich der Charakter von der Kachel bewegt, auf der er steht.
Zurücksetzen: Wenn der Befehl Reset ausgegeben wird, teleportiert sich der Charakter zurück an die Startposition des Spielplans.
Befehle erwarten: Da die Bewegung des Charakters nicht direkt gesteuert werden kann, muss er auf Befehle lauschen, die vom VerseCommanderMinigame-Gerät im Level kommen. Nach der Ausführung aller seiner Befehle steht er still und erwartet weitere Befehle.
Der NPC hat in dieser Vorlage nur ein paar Bewegungsoptionen: sich nach vorn in die Richtung zu bewegen, in die er blickt, sich nach rechts oder links drehen. Jede dieser Optionen erfolgt über die Funktion GetNavTarget(), die ein neues Navigationsziel eine TileDistance entfernt erstellt, das der Charakter verwenden kann. Dieses Ziel befindet sich abhängig davon, ob der ausgegebene Befehl Vorwärts, Rechts oder Links ist, vom Charakter lokal aus gesehen vorwärts, rechts oder links.
# Gets a new navigation target for the NPC based on the current transform and the given command.
GetNavTarget(CurrentTransform:transform, Command:command, TileDistance:vector3):transform=
# Based on the command, get the character's local forward, right, or left (negative right).
Direction :=
if (Command = Commands.Forward):
CurrentTransform.Rotation.GetLocalForward()
else if (Command = Commands.TurnRight):
CurrentTransform.Rotation.GetLocalRight()
else if (Command = Commands.TurnLeft):
-CurrentTransform.Rotation.GetLocalRight()
Wenn der NPC das Ausführen-Signal erhält, iteriert er durch die Liste der Befehle, die er empfängt, und übergibt jeden einzelnen an die Funktion ExecuteCommand(). Zuerst holen sie das focus_interface und das navigatable-Interface für den Charakter und führen dann je nach Befehl verschiedene Aktionen aus. Für jeden Befehl Vorwärts, Rechts und Links rufen sie GetNavTarget() auf, um die neue Transformation für den NPC zu finden, die verwendet werden soll. Dann navigieren sie entweder vorwärts zur neuen Transformation mit NavigateTo() vom navigatable-Interface oder verwenden das focus_interface, um auf das Ziel zu seiner Rechten oder Linken zu fokussieren.
# Executes the given command, either moving the NPC forward one tile or turning them left
# or right.
ExecuteCommand(Command:command, TileSize:vector3)<suspends>:void=
if:
# Get the Agent (the NPC).
Agent := GetAgent[]
# Gets the Fortnite Character interface, which gets you access to its gameplay data
# including its AI module for navigation and focus.
Character := Agent.GetFortCharacter[]
Charakter VFX
Wenn sich der Charakter über den Spielplan bewegt, zeigt ein Pfeil-Prop seine Position und Ausrichtung, um die Visualisierung des Charakters gegenüber der von oben nach unten gerichteten Kamera einfacher zu machen. Dieser Pfeil muss dem Charakter umher folgen und aktualisiert werden, wenn sich der Charakter dreht und bewegt. Die Funktion MoveArrow() aktualisiert die Position des Pfeils entsprechend dem Charakter und kopiert seine Position und Ausrichtung. Die Funktion CreateArrow() spawnt das Pfeil-Prop und führt einen ersten Aufruf an MoveArrow() aus, damit du direkt von Anfang an sehen kannst, wo sich der Charakter befindet.
# Creates an arrow prop at the NPC's position that visually shows the orientation of the NPC.
CreateArrow(Agent:agent):void=
if :
Character := Agent.GetFortCharacter[]
then:
var Transform:transform = Character.GetTransform()
# Spawn the arrow prop, then set the mesh and material for the prop.
SpawnPropResult := SpawnProp(ForwardArrowAsset, Transform)
if:
SpawnedProp := SpawnPropResult(0)?
Wenn der Charakter auf dem Plan spawnt, sich zu einem neuen Plan bewegt oder über den Reset-Befehl zum Anfang des Plans zurückgesetzt wird, wird für das Herein- und Herausteleportieren eine Teleportanimation wiedergegeben. Um einen Teleporteffekt zu erstellen, rufst du zuerst Hide() auf dem Charakter und dem Pfeil auf und gibst dann TeleportOutVFX wieder, indem du den VFX-Spawner dorthin bewegst, wo sich der Charakter befindet, und ihn aktivierst. Wenn der Grafikeffekt für das Herausteleportieren abgeschlossen ist, müssen wir den Charakter an seine neue Position teleportieren und TeleportInVFX an dieser Position wiedergeben. Wenn das alles erledigt ist, können wir Show() auf den Charakter und das Pfeil-Prop aufrufen, um den Charakter an der neuen Position zu zeigen.
# Hides the NPC and the arrow prop, then teleports both to a new position,
# playing VFX for teleporting in and teleporting out.
PlayVFXAndMoveCharacter(StartPosition:transform)<suspends>:void=
if:
Agent := GetAgent[]
FortCharacter := Agent.GetFortCharacter[]
then:
# Hide the NPC and the arrow.
FortCharacter.Hide()
ForwardArrow.Hide()
Das Teleportieren des Charakters erfolgt mit der Helfer-Funktion MoveToTile(), die eine Transformation verwendet, zu welcher der Charakter bewegt wird, und ihn dorthin teleportiert. Dem Z-Wert der Transformation wird ein geringer Versatz hinzugefügt, um zu verhindern, dass sich der Charakter mit dem Boden überschneidet.
# Teleports the NPC to the given transform.
MoveToTile(Transform:transform)<transacts><decides>:void=
# Get the Agent (the NPC).
Agent := GetAgent[]
# Gets the Fortnite Character interface, which gets you access to its gameplay data
# including its AI module for navigation and focus.
Character := Agent.GetFortCharacter[]
var NewTransform:transform = Transform
Verarbeitungsbefehle
Wenn sich der Charakter untätig auf dem Plan befindet, muss er auf das Ausführen-Signal lauschen, damit er weiß, was er als Nächstes tun soll. Das erfolgt in der Funktion AwaitCommands(). Diese Funktion hat den suspends-Bezeichner, damit sie asynchron ausgeführt werden kann, da der Charakter mit Await() auf ExecuteCommandsEvent warten muss. Da Befehle als Tupel eingehen, das ein Befehlsarray und die TileSize enthält, die für diese Befehle verwendet wird, muss jeder in einer for-Schleife durch aufruf von ExecuteCommand() verarbeitet werden. Während der Ausführung jedes Befehls blenden wir den Vorwärtspfeil aus und zeigen ihn erst wieder, wenn die Ausführung des Befehls abgeschlossen ist. Sobald die Ausführung aller Befehle abgeschlossen ist, signalisieren wir dem Verse-Kommandant-Minispiel, dass wir die Befehle abgeschlossen haben und bereit für mehr sind.
# Waits for commands to be sent from the verse_commander_minigame, then
# executes each command.
AwaitCommands()<suspends>:void=
if:
Agent := GetAgent[]
then:
# Wait for commands to be sent from the verse commander minigame.
ExecuteResult := VerseCommanderMinigame.ExecuteCommandsEvent.Await()
# For each execute result tuple, execute the command and pass the tile size from the tuple.
Statt neue Befehle zu verarbeiten, kann der Charakter mit der Zurücksetzen-Schaltfläche auch an den Start des aktuellen Spielplans zurückgesetzt werden. Da das Zurücksetzen sofort erfolgt und nicht die Befehlswarteschlange verwendet, muss der Charakter separat vom Ausführen-Signal darauf lauschen. Das erfolgt in der Funktion AwaitReset(), die auf das BoardResetEvent-Signal vom Verse-Kommandant-Minispiel wartet. Wenn das erfolgt, ruft sie PlayVFXAndMoveCharacter() auf, um den Charakter wieder zum Startpunkt des Plans zu bewegen.
# Waits for the current board to be reset, then moves the
# NPC back to the starting position of the board along with VFX.
AwaitReset()<suspends>:void=
# Wait for the current board to be reset.
# The event payload is the starting position for the board.
StartPosition := VerseCommanderMinigame.BoardResetEvent.Await()
spawn{PlayVFXAndMoveCharacter(StartPosition)}Ausführen der Charakter-Spielschleife
Da die verschiedenen Funktionen, die Befehle verarbeiten, jetzt eingerichtet wurden, ist es an der Zeit, die Spiel-Hauptschleife für den Charakter zu erstellen. Der Charakter muss fortlaufend aus das Ausführen-Signal lauschen, um eine Liste mit Befehle zu verarbeiten, oder auf das Zurücksetzen-Signal, um zum Start des Plans zurückgesetzt zu werden. Da das Warten auf das Ausführen-Signal und das Zurücksetzen-Signal jeweils asynchron erfolgen müssen und pro Plan mehrfach auftreten können, benötigst du eine separate Helfer-Funktion, welche die Schleife für beide verarbeitet. Das erfolgt über die Funktion CharacterCommandLoop(), welche die Hauptspielschleife für den Charakter ausführt. Es wird ein Race-Ausdruck zwischen der Funktion AwaitReset() und einer Schleife ausgeführt, die fortlaufend AwaitCommands() aufruft, um sicherzustellen, dass der Charakter immer auf Befehle lauscht.
# Race between resetting the character to start of the board and awaiting commands for that character.
CharacterCommandLoop()<suspends>:void=
race:
AwaitReset()
loop:
AwaitCommands()Wenn das Spiel beginnt, ist der Charakter erst im Level vorhanden, wenn er über den NPC-Spawnpunkt spawnt. Das bedeutet, dass er das Verse-Kommandant-Minispiel im Level finden muss, wenn er spawnt, da er über keine Referenz darauf verfügt. Das erfolgt mit GetCreativeObjectsWithTag(), um das Objekt mit dem Gameplay-Tag verse_commander_minigame_tag zu finden, und Festlegen dieses Tags als VerseCommanderMinigame. Wenn du dein eigenes Minispielerlebnis erstellst, solltest du sicherstellen, dass du Tags ordnungsgemäß festlegst, damit Charaktere im Level die Objekte finden können, mit denen sie kommunizieren müssen.
Nachdem er das Verse-Kommandant-Minispiel gefunden hat, muss der Charakter mit CreateArrow() den Vorwärts-Pfeil spawnen, der ihm umherfolgt. Um die Spielschleife auszuführen, muss fortlaufend eine Schleife der Funktion CharacterCommandLoop() ausgeführt werden, damit neu gestartet werden kann, wenn ein Zurücksetzen-Signal auftritt. Das muss auch in einem race-Ausdruck gegen das GameEndedEvent vom Verse-Kommandant-Minispiel erfolgen, da der Charakter sofort das stoppen soll, was er tut, wenn das Spiel endet.
# This function runs when the NPC is spawned in the world and ready to follow a behavior.
OnBegin<override>()<suspends>:void=
# Get the Verse Commander Minigame Device.
# Assumption is that there is only one device in the level.
CreativeObjects := GetCreativeObjectsWithTag(verse_commander_minigame_tag{})
if:
CreativeObject := CreativeObjects[0]
MinigameManager := verse_commander_minigame[CreativeObject]
then:
Nächster Schritt
Wir haben einen benutzerdefinierten NPC definiert, der Befehlsdaten von einem Verse-Gerät übernimmt und sich mit diesen auf einem Spielplan bewegt. Die vollständige Codeliste zum Erstellen des benutzerdefinierten Charakters findest du im letzten Schritt 7. Endergebnis.
Im nächsten Schritt erfährst du, wie du einen Spielplan für den Charakter erstellst, auf dem er sich umherbewegen und sein Rätsel lösen kann.