O personagem neste exemplo usa uma definição de tipo de personagem personalizada, pois só precisa se mover um pouco e não necessita de acesso à API de guardas ou de animais selvagens. O comportamento do personagem é orientado por um comportamento Verse personalizado chamado verse_commander_character.
Guardas são personagens não jogáveis (PNJs) que podem se mover ao longo do caminho designado e se tornar hostis para atacar jogadores inimigos. Animais selvagens são animais, como galinhas e javalis, que também podem se mover ao longo do caminho designado e atacar jogadores inimigos.
Para começar a criar o PNJ personalizado, crie um novo comportamento de PNJ chamado vese_commander_character usando o Explorador do Verse. Para obter informações sobre como criar seus próprios comportamentos de PNJ personalizados, consulte Como criar comportamento de PNJ personalizado.
O personagem precisa conhecer e gerenciar a seguintes propriedades:
CommandWaitTime: quanto tempo esperar entre cada comando.
FocusTime: por quanto tempo forçar o foco em um destino. Para virar o personagem para a esquerda ou para a direita, é utilizado o
focus_interfacedele para forçá-lo a ficar de frente para um ponto específico à direita ou esquerda. Como focar em um alvo é um processo contínuo, a menos que seja interrompido, essa propriedade é definida para um número muito baixo, apenas tempo suficiente para o personagem se virar e se voltar para uma direção.ReachRadius: é o quão próximo o personagem precisa estar para chegar ao destino de navegação para que seja considerado que "chegou".
VerseCommanderMinigame: é a referência para o VerseCommanderMinigame no nível e permite que o personagem escute os comandos provenientes dele.
Efeitos visuais e referências de setas: faz referência ao efeito visual de teletransporte de chegada/saída e ao adereço de seta para frente, facilitando a visualização da orientação do personagem.
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{}Agora que as propriedades dos personagens foram definidas, vamos definir os comportamentos deles e as funções que os orientam.
Movimento de personagem
O personagem deste jogo tem os seguintes comportamentos:
Mover para a frente: o comando Forward move o personagem para a frente 1 ladrilho no tabuleiro.
Virar à direita ou Virar à esquerda: os comandos Turn Right e Turn Left fazem o personagem virar 90 graus para ficar de frente para o lado direito ou esquerdo dele, respectivamente. Essa ação precisa acontecer sem mover o personagem do quadrado em que ele está.
Redefinir: quando o comando Reset é emitido, o personagem se teletransporta de volta para a posição inicial no tabuleiro.
Aguardar comandos: como não é possível controlar diretamente o movimento do personagem, ele precisa escutar os comandos provenientes do dispositivo VerseCommanderMinigame no nível. Depois de executar todos os comandos, ele permanece parado aguardando mais comandos.
O PNJ deste modelo tem apenas algumas poucas opções de movimento: mover-se um quadrado na direção para a qual está voltado, virar para direita ou virar para a esquerda. Cada uma dessas opções é realizada por meio da função GetNavTarget(), que cria um novo destino de navegação a um TileDistance de distância para o personagem usar. O destino é a frente, direita ou esquerda do local do personagem, de acordo com o comando dado: "Forward", "Right" ou "Left".
# 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()
Quando o PNJ receber o sinal "Execute", ele iterará pela lista de comandos recebidos e passará cada um deles para a função ExecuteCommand(). Primeiro, ele obtém o focus_interface e a interface navigatable para o personagem; em seguida, realiza ações distintas conforme o comando. Para cada "Forward", "Right" e "Left", é chamado a GetNavTarget() para encontrar a nova transformação para o PNJ usar. Em seguida, ele navega para frente até a nova transformação, usando NavigateTo(), na interface navigatable, ou usa focus_interface para focar no destino à esquerda ou à direita.
# 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[]
Efeitos visuais de personagem
Quando o personagem se move pelo tabuleiro, um adereço de seta mostrará a posição e a orientação dele para facilitar sua visualização por meio da câmera de cima para baixo. Esta seta precisa acompanhar o personagem à medida que se vira e se move. A função MoveArrow() atualiza a posição da seta para corresponder à do personagem, copiando a posição e a orientação dele. A função CreateArrow() gera um adereço de seta e realiza uma chamada inicial de MoveArrow() para que você veja onde o personagem está desde o início.
# 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)?
Quando o personagem for gerado no tabuleiro, ele se move para um novo tabuleiro ou é redefinido para o início do tabuleiro por meio do comando "Reset", uma animação de teletransporte é reproduzida quando ele se teletransportar para entrar e para sair. Para criar um efeito de teletransporte, primeiro chamamos Hide() no personagem e na seta. Em seguida, reproduzimos "TeleportOutVFX" movendo o gerador de efeito visual para onde o personagem está e o habilitamos. Quando o efeito visual do teletransporte para sair terminar, será preciso teletransportar o personagem para sua nova posição e reproduzir o "TeleportInVFX" naquele local. Depois de fazer tudo isso, podemos chamar Show() no personagem e o adereço de seta para mostrá-lo na nova posição.
# 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()
Para teletransportar o personagem, é utilizada a função auxiliar MoveToTile(), que recebe a transformação para onde mover o personagem e o teletransporta para lá. Um pequeno deslocamento é adicionado ao valor Z da transformação para impedir que o personagem fique entrando no chão.
# 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
Como processar comandos
Quando o personagem estiver ocioso no tabuleiro, ele precisa se sentar e escutar o sinal de "Execute" para saber o que fazer em seguida. Isso ocorre na função AwaitCommands(). Esta função tem o especificador suspends para que possa ser executada assincronamente, pois o personagem precisa aguardar, Await(), o ExecuteCommandsEvent. Os comandos vêm em forma de uma tupla que contém uma matriz de comandos e o "TileSize" usado para eles, por isso precisam ser processados em um loop for chamando ExecuteCommand(). À medida que cada comando for executado, a seta para frente será ocultada e mostrada novamente apenas quando terminar de ser executado. Ao fim da execução de todos os comandos, sinalizaremos ao minijogo Comandante Verse que não usaremos mais nenhum comando e podemos prosseguir.
# 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.
Em vez de processar comandos novos, o personagem também pode ser redefinido e voltar para o início do tabuleiro atual com o botão "Reset". Já que a redefinição é imediata e não utiliza a fila de comandos, o personagem precisa escutá-lo separadamente a partir do sinal de "Execute". Isso ocorre na função AwaitReset(), que aguarda o BoardResetEvent sinalizar a partir do minijogo Comandante Verse. Ao fazer isso,PlayVFXAndMoveCharacter() é chamado para mover o personagem de volta para a posição inicial do tabuleiro.
# 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)}Como executar o loop de jogo do personagem
Agora que as diversas funções para processar comandos foram configuradas, é hora de criar o loop de jogo principal do personagem. O personagem precisa escutar continuamente o sinal "Execute" para processar uma lista de comandos, ou o sinal "Reset" para ser redefinido e voltar para o início do tabuleiro. Como a espera pelo sinal "Execute" e a espera pelo sinal "Reset" precisam acontecer assincronamente, e podem ocorrer várias vezes por tabuleiro, é preciso ter uma função auxiliar separada que trata do loop que será percorrido por ambos. Isso é feito por meio da função CharacterCommandLoop(), que executa o loop do jogo principal para o personagem. A expressão "race" inicia uma corrida entre a função AwaitReset() e o loop que continuamente chama AwaitCommands(), para garantir que o personagem está sempre escutando os comandos.
# Race between resetting the character to start of the board and awaiting commands for that character.
CharacterCommandLoop()<suspends>:void=
race:
AwaitReset()
loop:
AwaitCommands()Quando o jogo começar, o personagem não estará presente no nível até ser gerado a partir do Gerador de PNJs. Ou seja, quando for gerado, ele precisa encontrar o minijogo Comandante Verse no nível porque não tem nenhuma referência ao jogo. Isso é feito usando GetCreativeObjectsWithTag() para encontrar o objeto com a Tag de Jogabilidade verse_commander_minigame_tag e configurando-o como VerseCommanderMinigame. Ao criar sua própria experiência de minijogo, verifique se suas tags estão definidas corretamente para que os personagens no nível encontrem os objetos com os quais precisam se comunicar.
Depois de encontrar o minijogo Comandante Verse, o personagem precisa gerar a seta para frente que o acompanhará usando CreateArrow(). Para executar o loop de jogo, é preciso executar a função CharacterCommandLoop() em loop para que seja redefinida se um sinal "Redefinir" ocorrer. Isso também precisa acontecer em uma expressão race em relação ao GameEndedEvent do minijogo Comandante Verse, pois, se o jogo terminar, o personagem deverá parar imediatamente o que estiver fazendo.
# 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:
Próxima etapa
Definimos um PNJ personalizado que recebe dados de comandos de um dispositivo Verse e os usa para se mover em um tabuleiro. Confira a lista completa de códigos para criar o personagem na etapa final 7. Resultado final.
Na próxima etapa, você aprenderá a criar um tabuleiro para o personagem poder se mover e solucionar o quebra-cabeça que está nele.