Os lampiões no tutorial do Scene Graph usam vários componentes criados no Verse para criar estruturas pré-fabricadas dinâmicas e interativas. Isso é possível pela programação de movimento e interatividade em um componente criado no Verse.
Simulação de movimentos simples
A estrutura pré-fabricada de poste de luz usa dois componentes diferentes para simular o movimento do lampião. Ambos são anexados à entidade Pivô, que fornece um ponto de pivô para mover o lampião. Primeiro, o keyframed_movement_component permite criar animações a partir de quadros-chave e depois reproduzi-las para animar uma entidade. No entanto, esse componente não pode agir sozinho e precisa de outro trecho de código para fornecer os quadros-chave e iniciar a animação. É aqui que entra o simple_movement_component personalizado. Esse código é reutilizado em todo o projeto para controlar como diferentes objetos de jogo se movem na cena.
Para saber mais sobre como usar animações para mover objetos, confira Como animar o movimento de adereços.
Vamos dar uma olhada mais de perto em simple_movement_component. Abra o script SimpleMovementComponent.verse no Explorador do Verse no Visual Studio Code para começar.
Animações usam diferentes modos de reprodução para definir o que elas devem fazer ao serem concluídas. Esses modos são definidos na parte superior do arquivo na enumeração movement_mode:
One-shot: o objeto para de se mover quando a animação é concluída.
Loop: o objeto reinicia a animação do início quando atinge o final.
Pingue-pongue: o objeto reproduz a animação ao contrário, indo e voltando do início ao fim.
movement_mode<public> := enum:
OneShot
Loop
PingPongA classe basic_movement_component também define as variáveis necessárias para criar animações. Cada um deles tem o atributo @editable para permitir que você os edite no Organizador:
Quadros-chave: a matriz dekeyframed_movement_deltausada para criar a animação. Cada uma dessas faixas rastreia a mudança na transformação nesse quadro-chave específico, a duração do quadro-chave e o tipo de suavização de movimento usado.AutoPlayautomática: umavariável lógicaque determina se a entidade deve ser animada ao iniciar a simulação.MovementMode: o comportamento de movimento que a entidade exibe.
# A Verse-authored component that can be added to entities
basic_movement_component<public> := class<final_super>(component):
@editable
var Keyframes<public>: []keyframed_movement_delta = array{}
@editable
var AutoPlay: logic = true
O código em OnSimulate() começa a ser executado sempre que o componente é adicionado à cena e quando o jogo começa. Aqui, o código usa primeiro uma chamada Sleep() curta para garantir que todas as entidades e componentes sejam inicializados corretamente antes de continuar. Em seguida, em uma expressão if, ele verifica se a entidade tem um keyframed_movement_component.
Nesse caso, a função InitializeKeyFramedMovementComponent() é chamada para configurar o componente com os valores adequados. E se a entidade não tiver um keyframed_movement_component? Nesse caso, você pode criar um dinamicamente em tempo de execução e adicioná-lo à entidade usando a função AddComponents(). Dessa forma, é possível garantir que a entidade esteja configurada corretamente quando o jogo começar.
OnSimulate<override>()<suspends>:void =
Sleep (0.1)
if:
KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component]
then:
InitializeKeyframedMovementComponent(KeyframedMovementComponent)
else:
NewKeyFramedMovementComponent := keyframed_movement_component { Entity := Entity }
Entity.AddComponents of array { NewKeyFramedMovementComponent }
InitializeKeyframedMovementComponent(NewKeyFramedMovementComponent)A função InitializeKeyframedMovementComponent() define o componente de movimento com quadros-chave com valores baseados naqueles que você atribui no editor. Primeiro, ele cria uma nova variável keyframed_movement_playback_mode para determinar o modo de reprodução usado durante essa animação. Ele é inicializado com o valor oneshot_keyframed_movement_playback_mode, o que significa que a animação será reproduzida apenas uma vez.
InitializeKeyframedMovementComponent(InKeyframedMovementComponent:keyframed_movement_component):void =
var PlaybackMode:keyframed_movement_playback_mode = oneshot_keyframed_movement_playback_mode{}Em seguida, usa uma instrução case para definir o PlaybackMode com base no valor de MovementMode definido no editor. Cada valor na enumeração MovementMode corresponde a um modo de reprodução diferente definido no módulo SceneGraph/KeyframedMovement.
case (MovementMode):
movement_mode.OneShot =>
set PlaybackMode = oneshot_keyframed_movement_playback_mode{}
movement_mode.Loop =>
set PlaybackMode = loop_keyframed_movement_playback_mode{}
movement_mode.PingPong =>
set PlaybackMode = pingpong_keyframed_movement_playback_mode{}Por fim, a função define os quadros-chave e o modo de movimento no componente de movimento com quadros-chave, e a animação está pronta para ser reproduzida. Se a variável AutoPlay for definida como verdadeira, a animação começará imediatamente com uma chamada da função Play() do componente de movimento em quadros-chave.
InKeyframedMovementComponent.SetKeyframes(Keyframes, PlaybackMode)
if:
AutoPlay?
then:
InKeyframedMovementComponent.Play()De volta ao editor, simple_movement_component é adicionado à entidade Pivô e definido com os seguintes valores. Agora, quando o jogo começar, o lampião começará a balançar para frente e para trás.
Script completo
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph/KeyframedMovement }
movement_mode<public> := enum:
OneShot
Loop
Como interagir com o lampião
O lampião pode balançar para frente e para trás, mas você precisará de outra peça para permitir que o jogador interaja com ele. Isso é feito anexando um Componente intratável à entidade Lampião e um lantern_interaction_component no Verse personalizado ao poste de luz que permite acender e apagar o lampião.
Abra o script LanternInteractionComponent.verse no Explorador do Verse. Esse script começa importando o módulo LightPost definido em Assets.digest.verse. É aqui que todos os ativos relacionados ao poste de luz, como a estrutura pré-fabricada, são armazenados. Também importa o módulo LightPost.Materials para acessar os materiais e malhas do poste de luz e do lampião.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
LightPost := module:
Materials<public> := module:
A classe lantern_interaction_component começa definindo uma variável chamada MaterialInstance, que é inicializada como LightPost.Materials.MI_Lantern_01. Essa é a instância de material que contém todas as variáveis relacionadas ao material do lampião.
lantern_interaction_component<public> := class<final_super>(component):
var MaterialInstance:LightPost.Materials.MI_Lantern_01 = LightPost.Materials.MI_Lantern_01{}Em OnBeginSimulation(), o script começa encontrando cada componente em suas entidades descendentes com um interactable_component anexado. Como lantern_interaction_component é anexado ao poste de luz, retornará o interactable_component da entidade Lantern, pois é um descendente do poste de luz.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
InteractabeleComponents := Entity.FindDescendantComponents(interactable_component)Em seguida, em uma expressão for, o código itera em cada componente interativo encontrado e registra seu SucceededEvent na função OnInteractFinished() definida posteriormente nesse arquivo. Agora, quando um jogador terminar de interagir com o lampião, OnInteractFinished() será disparado.
InteractabeleComponents := Entity.FindDescendantComponents(interactable_component)
for (InteractableComponent : InteractabeleComponents):
InteractableComponent.SucceededEvent.Subscribe(OnInteractFinished)A função OnBeginSimulation() chama FindDecedentComponents novamente para encontrar cada entidade que tenha um componente LightPost.SM_Lightpost_Lantern_01 anexado. Esse é o componente de malha anexado ao lampião. Em seguida, define o componente de malha para a MaterialInstance definida anteriormente, garantindo que a malha do lampião seja inicializada corretamente.
MeshComponents := Entity.FindDescendantComponents(LightPost.SM_Lightpost_Lantern_01)
for (MeshComponent : MeshComponents):
set MeshComponent.M_Lantern = MaterialInstanceA função OnInteractFinished() usa o Agente, ou jogador, como instigador da interação. Essa função simplesmente chama a função ToggleLight() para acender e apagar o lampião.
A função ToggleLight() faz o trabalho pesado em termos de acender e apagar o lampião. Primeiro, em uma expressão if, verifica se o nível emissivo de MaterialInstance é 0,0, indicando que a luz está apagada. Nesse caso, ele define o nível emissivo como 1.0. Em seguida, em duas expressões for, ele encontra cada light_component e particle_system_component em entidades descendentes e as ativa chamando Enable() e Play() respectivamente.
ToggleLight():void =
if (MaterialInstance.Emissive_Multiply = 0.0):
set MaterialInstance.Emissive_Multiply = 1.0
for:
Light : Entity.FindDescendantComponents(light_component)
do:
Light.Enable()
for:
Se a luz já estava acesa, a função fará o inverso dentro da instrução else. Ele define os níveis emissivos do material para 0,0 e desabilita quaisquer componentes de sistema de partículas e luz nas entidades descendentes.
else:
set MaterialInstance.Emissive_Multiply = 0.0
for:
Light : Entity.FindDescendantComponents(light_component)
do:
Light.Disable()
for:
Particle : Entity.FindDescendantComponents(particle_system_component)
As funções nesse código se combinam para tornar o lampião dinâmico, ativando e desativando vários componentes quando um jogador interage com ele. Observe que, embora o lampião seja ligado e desligado, sua entidade pai de poste de luz é aquela que fornece o lantern_interaction_component.
Considere o que você precisaria fazer para ter um poste de luz com várias luzes, acendendo e apagando cada uma delas com o pressionamento de um único botão. Como esse código funciona encontrando entidades descendentes com um tipo de componente específico, você não precisaria de um código extra do Verse para implementar essa funcionalidade. Desde que cada entidade de lampião filho tenha um componente de luz ou um componente de partícula, você está pronto.
Script completo
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
LightPost := module:
Materials<public> := module: