Latarnie w samouczku dotyczącym narzędzia Scene Graph wykorzystują wiele opracowanych w Verse komponentów służących do tworzenia dynamicznych i interaktywnych gotowców. Jest to możliwe dzięki programowaniu ruchu i interaktywności w komponencie opracowanym w Verse.
Symulowanie prostego ruchu
Gotowiec słupa oświetleniowego wykorzystuje dwa różne komponenty do symulowania ruchu latarni. Oba są dołączone do jednostki Pivot, która zapewnia punkt odniesienia do poruszania latarnią. Po pierwsze komponent keyframed_movement_component umożliwia budowanie animacji z klatek kluczowych, a następnie ich odtwarzanie w celu animowania jednostki. Jednak ten komponent nie może działać samodzielnie i wymaga innego fragmentu kodu do dostarczania klatek kluczowych i rozpoczynania animacji. W tym miejscu do akcji wkracza niestandardowy komponent simple_movement_component. Ten skrypt jest ponownie wykorzystywany w projekcie do wskazywania sposobu poruszania się różnych obiektów gry w scenie.
Aby dowiedzieć się więcej o używaniu animacji do poruszania obiektami, zapoznaj się z dokumentem Animowanie ruchu rekwizytów.
Przyjrzyjmy się bliżej komponentowi simple_movement_component. Aby rozpocząć, otwórz skrypt SimpleMovementComponent.verse w Eksploratorze Verse w Visual Studio Code.
Animacje używają różnych trybów odtwarzania w celu zdefiniowania, co powinny robić po zakończeniu odtwarzania. Są one definiowane na początku pliku w typie wyliczeniowym (enum) movement_mode:
Jeden raz – obiekt przestaje się poruszać po zakończeniu animacji.
Pętla – obiekt ponownie rozpoczyna animację, gdy osiągnie koniec.
Ping-pong – obiekt odtwarza animację wstecz, przechodząc w przód i w tył od początku do końca.
movement_mode<public> := enum:
OneShot
Loop
PingPongPonadto klasa basic_movement_component definiuje zmienne potrzebne do budowania animacji. Każda z nich ma atrybut @editable, który umożliwia edytowanie w panelu Outliner:
Keyframes: tablica wartościkeyframed_movement_deltaużywana do budowania animacji. Każda z nich śledzi zmianę przekształcenia w tej konkretnej klatce kluczowej, czas trwania klatki kluczowej i typ zastosowanego łagodzenia ruchu.AutoPlay: zmiennalogic, która określa, czy jednostka powinna być animowana, gdy rozpoczyna symulację.MovementMode: zachowanie ruchu stosowane przez jednostkę.
# 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
Kod w OnSimulate() jest uruchamiany za każdym razem, gdy komponent jest dodawany do sceny i przy rozpoczynaniu gry. W tym przypadku kod najpierw używa krótkiego wywołania Sleep(), aby przed kontynuowaniem upewnić się, że wszystkie jednostki i komponenty są prawidłowo zainicjowane. Następnie w wyrażeniu if sprawdza, czy jednostka ma komponent keyframed_movement_component.
Jeśli tak jest, wywołuje funkcję InitializeKeyFramedMovementComponent(), aby skonfigurować komponent z użyciem odpowiednich wartości. Co się stanie, jeśli jednostka nie ma komponentu keyframed_movement_component? W takim przypadku można utworzyć jednostkę dynamicznie w czasie wykonywania programu i dodać ją do jednostki za pomocą funkcji AddComponents(). Dzięki temu, gdy rozpoczyna się gra, masz gwarancję, że jednostka jest prawidłowo skonfigurowana.
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)Funkcja InitializeKeyframedMovementComponent() ustawia wartości komponentu ruchu z klatką kluczową na podstawie wartości przypisanych w edytorze. Najpierw tworzy nową zmienną keyframed_movement_playback_mode, aby określić tryb odtwarzania używany podczas tej animacji. Jest ona inicjowana z wartością oneshot_keyframed_movement_playback_mode, co oznacza, że animacja zostanie odtworzona tylko raz.
InitializeKeyframedMovementComponent(InKeyframedMovementComponent:keyframed_movement_component):void =
var PlaybackMode:keyframed_movement_playback_mode = oneshot_keyframed_movement_playback_mode{}Następnie używa instrukcji case w celu ustawienia właściwości PlaybackMode w oparciu o wartość MovementMode ustawioną w edytorze. Każda wartość w typie wyliczeniowym (enum) MovementMode odpowiada innemu trybowi odtwarzania zdefiniowanemu w module 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{}Na koniec funkcja ustawia klatki kluczowe i tryb ruchu w komponencie ruchu z klatką kluczową. Teraz animacja jest gotowa do odtworzenia. Jeśli zmienna AutoPlay ma wartość true, animacja rozpoczyna się natychmiast od wywołania funkcji Play() komponentu ruchu z klatką kluczową.
InKeyframedMovementComponent.SetKeyframes(Keyframes, PlaybackMode)
if:
AutoPlay?
then:
InKeyframedMovementComponent.Play()W edytorze komponent simple_movement_component jest dodawany do jednostki Pivot i ma ustawiane poniższe wartości. Teraz po rozpoczęciu gry latarnia zacznie się kołysać w przód i w tył.
Kompletny skrypt
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
Interakcja z latarnią
Latarnia może kołysać się w przód i w tył, ale potrzebujesz innego elementu, aby umożliwić graczowi interakcję z nią. Odbywa się to poprzez dołączenie komponentu Interactable do jednostki Lantern i niestandardowego komponentu Verse lantern_interaction_component do słupa oświetleniowego, co umożliwia włączanie i wyłączanie latarni.
Otwórz skrypt LanternInteractionComponent.verse w Eksploratorze Verse. Ten skrypt rozpoczyna działanie od zaimportowania modułu LightPost zdefiniowanego w Assets.digest.verse. To tutaj przechowywane są wszystkie zasoby związane ze słupem oświetleniowym (np. gotowiec). Ponadto importuje moduł LightPost.Materials, aby uzyskać dostęp do materiałów oraz siatek słupa oświetleniowego i latarni.
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
LightPost := module:
Materials<public> := module:
Klasa lantern_interaction_component zaczyna działanie od zdefiniowania zmiennej MaterialInstance, która jest inicjowana z wartością LightPost.Materials.MI_Lantern_01. Jest to instancja materiału zawierająca wszystkie zmienne związane z materiałem latarni.
lantern_interaction_component<public> := class<final_super>(component):
var MaterialInstance:LightPost.Materials.MI_Lantern_01 = LightPost.Materials.MI_Lantern_01{}W funkcji OnBeginSimulation() skrypt zaczyna działanie od znalezienia każdego komponentu w jego jednostkach potomnych z dołączonym komponentem interactable_component. Komponent lantern_interaction_component jest dołączony do słupa oświetleniowego, więc to zwróci komponent interactable_component jednostki Lantern, ponieważ jest elementem potomnym słupa oświetleniowego.
OnBeginSimulation<override>():void =
(super:)OnBeginSimulation()
InteractabeleComponents := Entity.FindDescendantComponents(interactable_component)Następnie w wyrażeniu for skrypt iteruje po każdym znalezionym komponencie interaktywnym i subskrybuje jego zdarzenie SucceededEvent do funkcji OnInteractFinished() zdefiniowanej w dalszej części tego pliku. Teraz, gdy gracz zakończy interakcję z latarnią, zostanie uruchomiona funkcja OnInteractFinished().
InteractabeleComponents := Entity.FindDescendantComponents(interactable_component)
for (InteractableComponent : InteractabeleComponents):
InteractableComponent.SucceededEvent.Subscribe(OnInteractFinished)Następnie funkcja OnBeginSimulation() ponownie wywołuje funkcję FindDescendantComponents, aby znaleźć każdą jednostkę, która ma dołączony komponent LightPost.SM_Lightpost_Lantern_01. Jest to komponent siatki dołączony do latarni. Następnie ustawia komponent siatki na wcześniej zdefiniowany obiekt MaterialInstance i upewnia się, że siatka latarni jest odpowiednio zainicjowana.
MeshComponents := Entity.FindDescendantComponents(LightPost.SM_Lightpost_Lantern_01)
for (MeshComponent : MeshComponents):
set MeshComponent.M_Lantern = MaterialInstanceFunkcja OnInteractFinished() przyjmuje agenta, czyli gracza, jako inicjatora interakcji. Ta funkcja po prostu wywołuje funkcję ToggleLight() w celu włączenia lub wyłączenia latarni.
To funkcja ToggleLight() faktycznie zajmuje się włączaniem lub wyłączaniem latarni. Najpierw w wyrażeniu if sprawdza, czy poziom emisyjny obiektu MaterialInstance ma wartość 0.0, co oznacza, że światło jest wyłączone. Jeśli światło jest wyłączone, ustawia poziom emisyjny na wartość 1.0. Następnie w dwóch wyrażeniach for znajduje wszystkie komponenty light_component i particle_system_component w jednostkach potomnych i włącza je poprzez wywołanie odpowiednio funkcji Enable() i Play().
ToggleLight():void =
if (MaterialInstance.Emissive_Multiply = 0.0):
set MaterialInstance.Emissive_Multiply = 1.0
for:
Light : Entity.FindDescendantComponents(light_component)
do:
Light.Enable()
for:
Jeśli światło jest już włączone, funkcja wykonuje odwrotne działania w instrukcji else. Ustawia poziomy emisyjne materiału na wartość 0.0 oraz wyłącza komponenty systemu światła i systemu cząsteczkowego w jednostkach potomnych.
else:
set MaterialInstance.Emissive_Multiply = 0.0
for:
Light : Entity.FindDescendantComponents(light_component)
do:
Light.Disable()
for:
Particle : Entity.FindDescendantComponents(particle_system_component)
Funkcje w tym skrypcie działają razem, aby latarnia była dynamiczna. W tym celu włączają lub wyłączają wiele komponentów, gdy gracz wchodzi w interakcję z latarnią. Zwróć uwagę, że chociaż latarnia jest jednostką, która jest włączana lub wyłączana, to jej jednostka nadrzędna (słup oświetleniowy) dostarcza komponent lantern_interaction_component.
Zastanów się, co trzeba zrobić, aby utworzyć słup oświetleniowy z wieloma światłami, które można włączać lub wyłączać naciśnięciem jednego przycisku. Działanie tego kodu polega na znajdowaniu jednostek potomnych z określonym typem komponentu, więc nie trzeba tworzyć dodatkowego kodu Verse w celu zaimplementowania tej funkcjonalności. Jeśli każda jednostka podrzędna latarni ma komponent światła lub komponent efektu cząsteczkowego, można przejść dalej.
Kompletny skrypt
using { /Verse.org }
using { /Verse.org/Native }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
LightPost := module:
Materials<public> := module: