El personaje de este ejemplo utiliza una definición de personaje de tipo personalizado, ya que solo necesita moverse y no necesita acceder a la API de guardias o animales salvajes. El comportamiento del personaje se rige por un comportamiento de Verse personalizado nombrado verse_commander_character.
Los guardias son personajes no jugables (PNJ) que pueden moverse a lo largo de una ruta designada y pueden volverse hostiles a los jugadores de ataque enemigo. Los animales salvajes son animales, como las gallinas y los jabalíes, que también pueden moverse por la ruta designada y por los jugadores de ataque enemigo.
Para empezar a crear el PNJ personalizado, crea un nuevo comportamiento de PNJ y nómbralo vese_commander_character en el explorador de Verse. Para obtener información sobre cómo crear tus propios comportamientos de PNJ personalizados, consulta Cómo crear un comportamiento personalizado de PNJ.
El personaje debe conocer y gestionar las siguientes propiedades:
CommandWaitTime: cuánto tiempo se debe esperar entre cada orden.
FocusTime: cuánto tiempo hay que forzar el enfoque en un objetivo. Girar al personaje a izquierda o derecha se controla con el parámetro
focus_interfacedel personaje para obligarle a mirar a un punto concreto a su izquierda o derecha. Como el enfoque en un objetivo no se completa a menos que se interrumpa, se establece en un número muy bajo (el tiempo justo para que el personaje se gire para mirar en una dirección).ReachRadius: indica lo cerca que tiene que estar el personaje de su objetivo de navegación para considerar que lo ha «alcanzado».
VerseCommanderMinigame: es una referencia al minijuego VerseCommanderMinigame del nivel y permite al personaje escuchar las órdenes que proceden de él.
Efectos visuales y referencias de flechas: hacen referencia a los distintos efectos visuales de teletransporte de entrada y salida, así como al elemento de flecha de avance que facilita la orientación del personaje.
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{}Ahora que hemos definido las propiedades del personaje, vamos a definir sus comportamientos y las funciones que lo dirigen.
Movimiento del personaje
El personaje de este juego tiene los siguientes comportamientos:
Moverse hacia delante: la orden Adelante hace avanzar al personaje 1 casilla en el tablero.
Girar a la derecha o Girar a la izquierda: las órdenes Girar a la derecha y Girar a la derecha hacen que el personaje gire 90 grados para mirar a su derecha o a su izquierda, respectivamente. Esto también tiene que ocurrir sin mover al personaje de la casilla en la que está.
Reiniciar: cuando se da la orden Reiniciar, el personaje se teletransporta a la posición inicial en el tablero.
Esperar órdenes: como el movimiento del personaje no se puede controlar directamente, tiene que escuchar las órdenes procedentes del dispositivo VerseCommanderMinigame del nivel. Tras ejecutar todas sus órdenes, se quedará quieto y esperará nuevas órdenes.
El PNJ de esta plantilla solo tiene pocas opciones de movimiento, pudiendo avanzar en la dirección hacia la que mira una casilla, girar a la derecha o girar a la izquierda. Cada una de estas opciones se realiza mediante la función GetNavTarget(), que crea un nuevo objetivo de desplazamiento a un TileDistance de distancia para que lo utilice el personaje. Este objetivo está a la derecha, a la izquierda o delante del personaje, dependiendo de si la orden dada es avanzar, ir hacia la derecha o ir hacia la izquierda.
# 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()
Cuando el PNJ recibe la señal Ejecutar, recorre la lista de órdenes que recibe y pasa cada una a la función ExecuteCommand(). En primer lugar, obtiene la interfaz focus_interface y navigatable del personaje, y después realiza distintas acciones en función de la orden. Para cada orden de movimiento hacia delante, a la derecha o a la izquierda, llama a GetNavTarget() para encontrar la nueva transformación que debe utilizar el PNJ. A continuación, se desplazan hacia delante hasta la nueva transformación con NavigateTo() desde la interfaz navigatable o utilizan la interfaz focus_interface para centrarse en el objetivo situado a su derecha o a su izquierda.
# 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[]
Efectos visuales del personaje
Cuando el personaje se mueve por el tablero de juego, un elemento en forma de flecha muestra su posición y orientación para facilitar la visualización del personaje desde la cámara cenital. Esta flecha tiene que seguir al personaje y actualizarse a medida que gira y se mueve. La función MoveArrow() actualiza la posición de la flecha para que coincida con la del personaje, copiando su posición y orientación. La función CreateArrow() genera el elemento de flecha y hace una llamada inicial a MoveArrow() para que puedas ver dónde está el personaje desde el principio.
# 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)?
Cuando el personaje se genera en el tablero, se mueve a un nuevo tablero o se reinicia al principio del tablero mediante la orden «Reiniciar», se reproduce una animación de teletransporte tanto para entrar como para salir. Para crear un efecto de teletransporte, primero llamamos a Hide() en el personaje y la flecha, y luego reproducimos TeleportOutVFX moviendo el generador de efectos visuales al punto en que está el personaje y activándolo. Una vez finalizado el efecto visual de teletransporte de salida, tenemos que teletransportar al personaje a su nueva posición y reproducir TeleportInVFX en dicha posición. Una vez hecho esto, podemos llamar a Show() en el personaje y en el elemento de flecha para mostrar al personaje en la nueva posición.
# 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()
El teletransporte del personaje se hace con una función de ayuda MoveToTile(), que toma la transformación a la que mover al personaje y lo teletransporta allí. Se añade un pequeño desfase al valor Z de la transformación para evitar que el personaje se recorte contra el suelo.
# 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
Cómo procesar las órdenes
Cuando el personaje está inactivo en el tablero, necesita sentarse y escuchar la señal Ejecutar para saber qué hacer a continuación. Esto ocurre en la función AwaitCommands(). Esta función tiene el especificador suspends para que pueda ejecutarse de forma asíncrona, ya que el personaje necesita Await() para el evento ExecuteCommandsEvent. Como las órdenes llegan como una tupla que contiene una matriz de órdenes y el valor de TileSize utilizado para estas órdenes, cada una de ellas debe procesarse en un bucle for llamando a ExecuteCommand(). A medida que se ejecuta cada orden, ocultamos la flecha de avance y solo la volvemos a mostrar cuando la orden termina de ejecutarse. Cuando todas las órdenes hayan terminado de ejecutarse, indicamos al minijuego Verse Commander que hemos terminado con las órdenes y que podemos recibir para más.
# 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.
En lugar de procesar nuevas órdenes, el personaje también puede volver al inicio del tablero actual con el botón Reiniciar. Como el reinicio es inmediato y no utiliza la cola de órdenes, el personaje debe escucharlo independientemente de la señal Ejecutar. Esto ocurre en la función AwaitReset(), que espera la señal BoardResetEvent del minijuego Verse Commander. Cuando lo hace, llama a PlayVFXAndMoveCharacter() para mover al personaje de vuelta a la posición inicial del tablero.
# 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)}Cómo ejecutar el bucle del juego del personaje
Ahora que ya están configuradas las distintas funciones que procesan las órdenes, es el momento de crear el bucle central del juego del personaje. El personaje necesita escuchar continuamente la señal Ejecutar para procesar una lista de órdenes, o la señal Reiniciar para volver al inicio del tablero. Como la espera de la señal Ejecutar y de la señal Reiniciar deben producirse de forma asíncrona, y pueden ocurrir varias veces por tablero, necesitas una función auxiliar independiente que se encargue del bucle de ambas. De esto se encarga la función CharacterCommandLoop(), que ejecuta el bucle principal del juego para el personaje. En una expresión `race`, hay una carrera entre la función AwaitReset() y un bucle que llama continuamente a AwaitCommands() para asegurarse de que el personaje esté siempre a la escucha de órdenes.
# Race between resetting the character to start of the board and awaiting commands for that character.
CharacterCommandLoop()<suspends>:void=
race:
AwaitReset()
loop:
AwaitCommands()Cuando comience el juego, el personaje no estará presente en el nivel hasta que aparezca en el generador de PNJ. Esto significa que cuando aparece, tiene que encontrar el minijuego Verse Commander en el nivel, ya que no tiene ninguna referencia a él. Lo consigue utilizando GetCreativeObjectsWithTag() para encontrar el objeto con la etiqueta de jugabilidad verse_commander_minigame_tag y ajustarlo como VerseCommanderMinigame. Cuando crees tu propia experiencia de minijuego, asegúrate de configurar correctamente las etiquetas para que los personajes del nivel puedan encontrar los objetos con los que necesitan comunicarse.
Después de encontrar el minijuego Verse Commander, el personaje tiene que crear la flecha hacia delante que le sigue con CreateArrow(). Para ejecutar el bucle del juego, necesita repetir continuamente la función CharacterCommandLoop() para reiniciar al personaje si se recibe una señal Reiniciar. Esto también tiene que ocurrir en una expresión race en GameEndedEvent desde el minijuego Verse Commander, ya que si el juego termina, el personaje debe dejar inmediatamente lo que esté haciendo.
# 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:
Siguiente paso
Hemos definido un PNJ personalizado que toma datos de órdenes de un dispositivo de Verse y los utiliza para moverse por un tablero de juego. Puedes encontrar la lista completa de código para crear el personaje personalizado en el paso final 7. Resultado final.
En el siguiente paso, aprenderás a crear un tablero para que el personaje pueda moverse por él y resolver el puzle.