O Scene Graph oferece várias maneiras de criar uma jogabilidade flexível e dinâmica por meio das muitas interações diferentes entre entidades e componentes. Esse modelo mostra algumas das maneiras de manipular entidades em tempo de execução por meio da geração e remoção de entidades, e como você pode usar eventos de componentes e consultas para criar comunicação entre as entidades e construir elementos de jogabilidade reutilizáveis.
Como gerar e remover estruturas pré-fabricadas
Por exemplo, confira a plataforma interativa no segundo corredor. Quando o jogador pisa na plataforma, um lampião de estrutura pré-fabricada é adicionado à cena e removido quando o jogador sai da plataforma.
Abra SpawnPrefabDevice.verse para conferir como esse código funciona. Esse é um dispositivo do Modo Criativo que usa um dispositivo Volume para saber quando um jogador entra nele. Em seguida, gera uma estrutura pré-fabricada e a remove quando o jogador pisar no gatilho. Na parte superior da definição da classe SpawnPrefabDevice, um TriggerVolume editável faz referência ao volume de disparador no nível.
SpawnPrefabDevice := class(creative_device):
@editable
TriggerVolume:volume_device = volume_device{}Quando o jogo começa, o dispositivo define uma nova estrutura pré-fabricada de poste de luz para surgir, bem como a posição em que ela surgirá.
OnBegin<override>()<suspends>:void =
PrefabToSpawn:entity = LightPost.P_LightPost{}
SpawnTransform:transform = transform:
Translation := vector3:
Left := -9084.0
Up := -8.0
Forward := -919.0Em seguida, ele obtém a entidade de simulação e cria uma série de contextos de falha para trabalhar. Primeiro, ele usa loop para executar o código repetidamente, seguido por race entre duas instruções block.
if:
SimulationEntity := GetSimulationEntity[]
then:
loop:
race:
block:
TriggerVolume.AgentEntersEvent.Await()
SimulationEntity.AddEntities(array{ PrefabToSpawn })
PrefabToSpawn.SetGlobalTransform(SpawnTransform)
A primeira instrução block espera um agente entrar no dispositivo Volume. Ao fazer isso, ele adiciona a estrutura pré-fabricada de lampião à cena usando AddEntities() e a posiciona corretamente usando SetGlobalTransform(). O segundo bloco espera o agente sair do volume e remover a estrutura pré-fabricada de seu pai, neste caso, a entidade de simulação.
Como esses blocos estão correndo em loop, cada um deles está sempre em execução para que o jogador possa entrar e sair do volume para regenerar o poste qualquer número de vezes!
Script completo
Abaixo está o processo concluído para adicionar e remover uma entidade da cena.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { /Verse.org/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
SpawnPrefabDevice := class(creative_device):
Sobreposições de entidade
O segundo corredor também tem uma série de exemplos que mostram algumas das diferentes maneiras pelas quais as entidades podem interagir. Por exemplo, as entidades com um componente de malha podem ser alertadas quando se sobrepõem a outra entidade por meio de EntityEnteredEvent e EntityExitedEvent do componente de malha. Confira o exemplo de OVNI no segundo corredor para ver essa funcionalidade em ação.
Quando o jogo começa, a vaca reproduz uma animação para se elevar, como se estivesse sendo abduzida pelo OVNI. A vaca usa o EntityEnteredEvent do componente de malha para saber quando ela se sobrepõe ao OVNI e, em seguida, se define como invisível para parecer que foi abduzida.
O código para esse exemplo é definido em EntityEnteredExampleComponent.verse. Abra esse arquivo no Explorador do Verse.
A classe entity_entered_example_component começa definindo uma matriz de Quadros-chave editável para criar a animação de abdução da vaca. Ela também define uma variável StartTransform para indicar a posição inicial da vaca na cena.
entity_entered_example_component<public> := class<final_super>(component):
@editable
Keyframes:[]keyframed_movement_delta = array{}
var StartTransform:transform = transform{}Em OnBeginSimulation(), o script começa recuperando o fort_round_manager. Essa é uma interface para o gerenciador de rodadas do Fortnite que você pode usar para registrar eventos no início e no final de uma rodada. Nesse exemplo, os scripts inscrevem a função OnRoundStarted() no início da rodada usando SubscribeRoundStarted(), o que significa que a função será executada somente quando o jogo começar, e não quando a entidade iniciar a simulação.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
if:
FortRoundManager := Entity.GetFortRoundManager[]
then:
FortRoundManager.SubscribeRoundStarted(OnRoundStarted)Em seguida, em OnRoundStarted(), o script começa definindo StartTransform para a transformação global da entidade e, em seguida, inscrevendo EntityEnteredEvent do componente de malha em uma nova função OnEntityEntered(), que será acionada sempre que outra malha se sobrepor à entidade. Em seguida, gera uma função PlayCowAbductionAnimation() para começar a elevar a vaca.
OnRoundStarted():void =
set StartTransform = Entity.GetGlobalTransform()
if:
Mesh := Entity.GetComponent[mesh_component]
then:
Mesh.EntityEnteredEvent.Subscribe(OnEntityEntered)
spawn { PlayCowAbdcutionAnimation() }A função PlayCowAbductionAnimation() simplesmente define a transformação global da vaca como sua transformação inicial e espera um curto período de tempo. Em seguida, obtém os componentes de malha e de quadro-chave da entidade, habilita a malha para tornar a vaca visível, depois define a animação no componente de movimento de quadro-chave e a reproduz.
PlayCowAbdcutionAnimation()<suspends>:void =
Entity.SetGlobalTransform(StartTransform)
Sleep(2.0)
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
set Mesh.Visible = true
MovementComponent.SetKeyframes(Keyframes, oneshot_keyframed_movement_playback_mode{})
MovementComponent.Play()Por fim, a função OnEntityEntered() é usada para executar códigos sempre que a vaca se sobrepõe a outra entidade, nesse caso, o OVNI. Ao fazer isso, ele obtém novamente os componentes de malha e movimento em quadro-chave, mas, em vez disso, os usa para interromper a reprodução de todas as animações contínuas e tornar a vaca invisível para parecer que foi abduzida. Em seguida, gera uma nova instância de PlayCowAbductionAnimation() para recomeçar o processo.
OnEntityEntered(OtherEntity:entity):void =
if:
Mesh := Entity.GetComponent[mesh_component]
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Stop()
set Mesh.Visible = false
spawn { PlayCowAbdcutionAnimation() }Script completo
Abaixo está o código completo para abduzir uma malha de vaca com uma malha de OVNI.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
entity_entered_example_component<public> := class<final_super>(component):
Consulta de acertos de sobreposição
As entidades também podem consultar outras entidades usando consultas de sobreposição. Em vez de detectar quando uma entidade entra ou sai de uma malha específica, as consultas de sobreposição podem ser usadas para retornar todas as entidades e componentes sobrepostos em uma área específica.
Essa área pode ser a própria entidade, um determinado volume de colisão, como uma esfera ou caixa, ou uma posição para simular a entidade. Ela retornará uma lista de overlap_hit. Cada overlap_hit fornece informações sobre o componente ou a sobreposição de volume pelo volume de origem, e você pode consultar esses componentes para encontrar sua entidade associada.
Por exemplo, tente pisar na plataforma interativa na frente do exemplo FindOverlapHits. Quando você interage com a plataforma, o OVNI gera um volume de colisão invisível e o usa para realizar uma consulta de sobreposição. Se o volume de colisão sobrepuser a vaca, o OVNI a abduzirá!
O código para esse exemplo é definido em FindOverlapHitsExampleComponent.verse. Abra este arquivo no Explorador do Verse.
A classe começa definindo um dispositivo de volume editável chamado Gatilho para referenciar a plataforma interativa em que o jogador pisa, bem como uma variável lógica IsAbducting para saber se a vaca está sendo abduzida ou não. Quando o componente começa a simulação, ele inscreve a função OnRoundStarted() no início da rodada do gerenciador de rodadas. A função OnRoundStarted(), de forma semelhante, apenas inscreve AgentEntersEvent e AgentExitsEvent do Gatilho para as funções OnTriggerEntered() e OnTriggerExited(), respectivamente.
find_overlaphits_example_component<public> := class<final_super>(component):
@editable
Trigger:volume_device = volume_device{}
var IsAbducting:logic = false
OnBeginSimulation<override>():void =
Quando o jogador pisar na plataforma, se o OVNI não estiver abduzindo uma vaca no momento, a função OnTriggerEntered() será chamada para pausar qualquer movimento que o OVNI possa estar realizando, recuperando seu keyframed_movement_component e chamando Pause(). Em seguida, ela chama EnableAbductionBeam() e PerformOverlapCheck() para habilitar o feixe de abdução e verificar se a vaca está embaixo do OVNI.
OnTriggerEntered(Agent:agent):void=
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()
PerformOverlapCheck()A lógica real de verificar se há sobreposições é armazenada na função PerformOverlapCheck(). Para simular um feixe de abdução, essa função gera uma cápsula de colisão e define um CollisionTransform que é definido logo abaixo do OVNI.
PerformOverlapCheck():void =
CollisionCapsule := collision_capsule{Radius := 36.0, Length := 328.0}
var CollisionTransform:transform = Entity.GetGlobalTransform()
set CollisionTransform.Translation.Up = CollisionTransform.Translation.Up - 248.0Em seguida, em uma expressão for, a chamada de função FindOverlapHits() localiza e retorna quaisquer componentes ou volumes. Ele passa CollisionCapsule como o volume para verificar se há colisões e CollisionTransform como o local para simular essa colisão. Em seguida, ele percorre cada sobreposição e verifica se o componente sobreposto era um componente de malha, especificamente a malha SM_Toy_Cow da entidade Vaca. Se for, será gerada uma função AbductCow() passando a vaca a ser abduzida.
# Perform the overlap check from the entity that contains the mesh_component
for:
Overlap : Entity.FindOverlapHits(CollisionTransform, CollisionCapsule)
# Cast to see if what was overlapped was the Cow
CowMesh := Meshes.SM_Toy_Cow[Overlap.TargetComponent]
CowPrefab := CowMesh.Entity
do:
spawn { AbductCow(CowPrefab) }Para simular a abdução da vaca, a entidade cria uma animação e a reproduz na entidade Vaca de maneira semelhante ao exemplo de sobreposição de entidades acima. Como esse código está sendo chamado da entidade OVNI e não da entidade Vaca, o código precisa obter componentes da entidade Vaca e, em seguida, passar uma animação para que ela seja reproduzida. Ele começa obtendo os componentes de movimento de malha e em quadro-chave da vaca e, em seguida, definindo IsAbducting como verdadeiro.
AbductCow(CowEntity:entity)<suspends>:void =
# Get the components on the Cow Prefab
if:
CowMesh := CowEntity.GetComponent[mesh_component]
MovementComponent := CowEntity.GetComponent[keyframed_movement_component]
then:
set IsAbducting = trueComo os quadros-chave usados na animação de abdução da vaca não estão definidos no Organizador, o código precisa criá-los com base na diferença de posição entre o OVNI e a vaca. Isso é feito obtendo a diferença de translação entre a vaca e o OVNI e, em seguida, criando um novo keyframed_movement_delta a partir desses valores. Em seguida, ele define esse único quadro-chave como uma matriz no componente de movimento do quadro-chave e chama play para deixar a vaca animar entre sua posição inicial e o OVNI.
# Get the delta between Cow and UFO
DeltaTransform:transform = transform:
Translation:= Entity.GetGlobalTransform().Translation - CowEntity.GetGlobalTransform().Translation
Scale := vector3{Left:= 0.0, Up:= 0.0, Forward:= 0.0}
# Create a key frame
Keyframe := keyframed_movement_delta:
Transform := DeltaTransform
Duration := 2.0
Easing := ease_in_cubic_bezier_easing_function{}
A vaca pode animar para o OVNI quando é abduzida, mas o código também precisa fazer com que ela desapareça quando houver sobreposição com o próprio OVNI. Para fazer isso, o código espera o EntityEnteredEvent do componente de malha da vaca e então chama RemoveFromParent() para remover a entidade Vaca da cena. Como a vaca se foi, o OVNI pode começar a patrulhar novamente, então o código chama play no componente de movimento com quadro-chave do OVNI para fazê-lo se mover.
# Wait for Entity Entered Event
CowMesh.EntityEnteredEvent.Await()
# Remove Cow from world
CowEntity.RemoveFromParent()
# Resume UFO Patrol
set IsAbducting = false
if:
UFOMovementComponent := Entity.GetComponent[keyframed_movement_component]
Finalmente, as funções EnableAdbuctionBeam() e DisableAbductionBeam() atuam como auxiliares simples que acendem e apagam os componentes de luz de holofote e malha do feixe de abdução, respectivamente, sempre que são chamadas.
EnableAbductionBeam():void =
for:
Mesh : Entity.FindDescendantComponents(Meshes.S_EV_SimpleLightBeam_01)
do:
Mesh.Enable()
for:
Light : Entity.FindDescendantComponents(spot_light_component)
do:
Light.Enable()
Script completo
Abaixo está o script completo para consultar acertos de sobreposição.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }
Consulta de acertos de varredura
Acertos de varredura fornecem outra maneira importante de consultar sobreposições entre entidades. Varrer refere-se a mover um objeto ao longo de uma distância definida ao longo de um vetor específico. Por exemplo, mover um bloco através de uma plataforma para empurrar os jogadores para um espaço ou lançar um míssil para frente para destruir uma parede.
A função FindSweepHits() retorna uma lista de sweep_hits. Cada sweep_hit fornece as mesmas informações que um overlap_hit, como a incidência do componente ou volume e o volume ou componente de origem que faz a varredura. Além disso, fornece informações sobre a posição de contato, normal, normal da face e a distância ao longo da varredura onde a ocorrência foi detectada.
O modelo usa acertos de varredura para criar uma versão mais avançada do exemplo de acertos de sobreposição anterior. Tente pisar na plataforma interativa final no segundo corredor para conferir esse exemplo. Quando você pisa na plataforma, o OVNI gera um feixe de abdução. Em seguida, executa um acerto de varredura da malha do OVNI para baixo, verificando a primeira entidade com a qual a varredura se sobrepõe. Se a entidade for uma vaca, o OVNI a abduzirá normalmente. No entanto, se a vaca estiver protegida por uma entidade Globo, a varredura atingirá o globo primeiro e será bloqueada, impedindo que o OVNI abduza a vaca.
Abra FindOverlapHitsExampleComponent.verse no Explorador do Verse para examinar o código. A configuração aqui é muito semelhante ao exemplo de acertos de sobreposição acima, com a mesma lógica usada para abduzir a vaca e habilitar e desabilitar o feixe de abdução. A principal diferença está na função OnTriggerEntered() que é executada quando o jogador pisa na plataforma interativa na frente do exemplo.
O código para essa função começa de forma semelhante ao exemplo de acertos de sobreposição, recuperando o componente de movimento com quadro-chave da entidade e habilitando o feixe de abdução.
OnTriggerEntered(Agent:agent):void=
# When a cow is inside the abduction area, stop the ship moving and start the abduction beam.
if:
not IsAbducting?
MovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
MovementComponent.Pause()
EnableAbductionBeam()No entanto, como a função está usando mapeamentos em vez de sobreposições, a lógica para saber se a vaca está sob o feixe de abdução é um pouco diferente. Ele começa obtendo o primeiro filho da entidade OVNI, nesse caso, a entidade de malha do OVNI. Em seguida, cria um vetor de deslocamento a partir do qual fazer a verificação, apontando diretamente para baixo a partir do OVNI.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}Em seguida, ele usa esse vetor de deslocamento para chamar a função auxiliar FindFirstSweepHit(), passando a malha do OVNI e o vetor. Se o primeiro componente for o componente de malha da vaca, será gerada a função AbductCow() para simular a abdução da vaca.
# Perform the sweep from the UFO Mesh
if (Child := Entity.GetEntities()[0]):
DisplacementVector := vector3{Left:=0.0, Up:=-300.0, Forward:=0.0}
FirstSweepHitEntity := FindFirstSweepHit(Child, DisplacementVector)
# If the First Hit Entity is the Cow Mesh, then abduct the Cow
if (HitEntity := FirstSweepHitEntity?; HitEntity.GetComponent[Meshes.SM_Toy_Cow]):
spawn { AbductCow(HitEntity) }A função FindFirstSweepHit() recebe a entidade a ser varrida e o vetor de deslocamento ao longo do qual ela será varrida. Ele chama FindSweepHits() para simular uma faixa e, em seguida, itera em cada acerto de faixa retornado em uma expressão `for`. Como cada sweep_hit contém um componente ou um volume, você pode consultar TargetComponent ou TargetVolume para saber qual é o tipo. Nesse caso, o código obtém a entidade proprietária de TargetComponent e a retorna como uma opção, o que significa que retornará `true` se a varredura atingir um componente, caso contrário, retornará false.
# Returns the first Entity hit by FindSweepHits
FindFirstSweepHit(InEntity:entity, DisplacementVector:vector3):?entity =
for (SweepHit : InEntity.FindSweepHits(DisplacementVector)):
return option{ SweepHit.TargetComponent.Entity }
return falseScript completo
Abaixo está o código completo para consultar acertos de varredura.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/SpatialMath }
using { /Fortnite.com/Game }
using { /Fortnite.com/Devices }