Ten samouczek jest zaawansowaną wersją samouczka Animowanie ruchu rekwizytów właściwą dla Scene Graph. Jeśli chcesz dowiedzieć się więcej o ruchu w UEFN opartym na animacji poza Scene Graph, zapoznaj się z tym samouczkiem, a następnie wróć do tego!
Ruchome platformy są powszechnie stosowane w większości platformówek i wymuszają na graczu wykonywanie precyzyjnych skoków między celami w celu dotarcia do mety.
Rekwizytami w UEFN można poruszać na kilka sposobów. Możesz użyć funkcji, takich jak TeleportTo[] lub MoveTo(), aby bezpośrednio modyfikować przekształcenie, lub skorzystać z innego urządzenia, takiego jak przenośnik rekwizytów, aby poruszać rekwizytem wzdłuż zaprogramowanej ścieżki. Istnieje też inna przydatna opcja, czyli animacje.
W porównaniu z ruchem przekształcenia rekwizytu animacje mają kilka zalet. Animacje zwykle charakteryzują się płynniejszym ruchem niż w przypadku poruszania obiektami za pomocą MoveTo() lub TeleportTo(), ponieważ unikają problemu z opóźnieniem sygnału sieciowego występującego przy wywoływaniu tych funkcji co każde tyknięcie gry.
Ponadto animacje mają bardziej spójne kolizje z graczami i innymi obiektami, a twórca ma większą kontrolę nad tym, gdzie i w jaki sposób obiekt się porusza, w porównaniu z urządzeniem przenośnika rekwizytów. Możesz odtwarzać animacje w pętli lub odtwarzać je do przodu i wstecz przy użyciu trybu ping-pongowego.
Animacje umożliwiają też wybranie typu interpolacji. Typ interpolacji decyduje o typie łagodzenia lub krzywej animacji, według której podąża animacja. Na przykład typ interpolacji liniowej odtwarza animację ze stałą szybkością, natomiast typ łagodzenia na początku rozpoczyna animację powoli i przyspiesza pod jej koniec.
Wybierając dla animacji odpowiedni typ interpolacji, możesz określić konkretne punkty, w których rekwizyt ma zwolnić, przyspieszyć lub poruszać się liniowo. Stosując komponent w celu zaimplementowania tych zachowań do jednostek w poziomie, możesz tworzyć ruchome platformy, po których gracze będą mogli się poruszać.
Najpierw zastanów się, jakie zachowania powinna być w stanie wykonać ruchoma platforma. Animacja powinna rozpoczynać się od określonej pozycji początkowej, a następnie wykonywać przesunięcie do wielu punktów. Po zakończeniu ruchu platforma powinna być w stanie wrócić do pozycji początkowej lub pozostać w miejscu.
Powinien wykonywać te ruchy przez określony czas, mieć możliwość odpowiedniego obracania i skalowania w każdym punkcie swojej drogi. Każde z tych zachowań będzie wymagało określonego kodu, jednak rozpoczęcie od prostej klasy, którą następnie rozbudujesz, pozwala szybko nadawać kształt różnym pomysłom. Wykonaj poniższe kroki, aby utworzyć komponent ruchu oparty na animacji.
Utwórz nowy komponent Verse o nazwie
animate_to_targets_componenti otwórz go w Visual Studio Code. Aby dowiedzieć się więcej na temat tworzenia własnych komponentów Verse, patrz: Tworzenie własnego komponentu Verse. Dodaj polecenieusingdla/Verse.org/SpatialMath,/Verse.org/SceneGraph/KeyframedMovement, i/UnrealEngine.com/Temporary/SpatialMath. Funkcje z każdego z nich będą potrzebne później.Verseusing { /Verse.org } using { /Verse.org/Native } using { /Verse.org/SceneGraph } using { /Verse.org/Simulation } using { /Verse.org/SpatialMath } using { /Verse.org/SceneGraph/KeyframedMovement } using { /UnrealEngine.com/Temporary/SpatialMath } # Place this component to an entity to move between preset targets. animate_to_targets_component<public> := class<final_super>(component):Podpowiedzi do narzędzi wykorzystanych w tej sekcji samouczka zamieszczono poniżej. Można je skopiować i wkleić nad definicją klasy
animate_to_targets_component.Verse# Editor Tool Tips DefaultSpeedTip<localizes><public>:message = "Default speed simulation entity moves during any segment that does not specify a speed." SpeedTip<localizes><public>:message = "Speed simulation entity moves during this segment." AnimationDurationTip<localizes><public>:message = "The duration of the animation segment in seconds." EasingFunctionTip<localizes><public>:message = "Movement cubic-bezier easing function during this segment." DefaultEasingFunctionTip<localizes><public>:message = "Default movement cubic-bezier easing function during any segment that does not specify an easing function." PlaybackModeTip<localizes><public>:message = "Animation playback mode\n\tOneshot: Animation plays once.\n\tPingPong: Animation plays in order and reverse order repeating.\n\tLoop: Animation repeats in a loop. Tip: To construct an animation in a closed loop, make the last segment starting position the same as the first segment starting position." TargetsTip<localizes><public>:message = "Entities that are targets for the parent entity." RandomizeTargetsTip<localizes><public>:message = "Randomize the order of the segments.\n\tNever: Always use the order specified.\n\tOnBegin: Only randomize the order on begin simulation.\n\tEveryIteration: Randomize the order of the segments every loop iteration." PauseTip<localizes><public>:message = "Duration simulation entity pauses at the beginning of this segment."Dodaj następujące pola do definicji klasy
animate_to_targets_component:Edytowalna zmienna o wartości zmiennopozycyjnej o nazwie
InitialPauseSeconds. Jest to ilość czasu do rozpoczęcia animacji jednostki. Ustaw tę wartość na10.0, aby jednostka odczekała dziesięć sekund, zanim rozpocznie się animacja.
Verse# Amount of time to pause before the animation starts. @editable_number(float): ToolTip := SpeedTip MinValue := option{0.0} var InitialPauseSeconds<public>:float = 10.0Edytowalny obiekt
keyframed_movement_playback_modeo nazwieAnimationPlaybackMode. To jest tryb animacji dla animacji jednostki. Ustaw to naloop_keyframed_movement_playback_mode. Oznacza to, że domyślnie, gdy animacja zostanie ukończona, jednostka wykona pętlę i ponownie rozpocznie animację od początku.
Verse# The playback mode used by this animation. @editable: ToolTip := PlaybackModeTip var PlaybackMode<public>:keyframed_movement_playback_mode = loop_keyframed_movement_playback_mode{}Zapisz kod i go skompiluj.
Dzielenie animacji na segmenty
Aby zbudować animacje w kodzie, użyjesz klatek kluczowych. Animacje tworzy się z jednej lub wielu klatek kluczowych. Każda klatka kluczowa określa wartości obiektu w konkretnych punktach animacji. Tworząc animację przy użyciu klatek kluczowych, możesz określić wiele punktów, do których rekwizyt ma się poruszać, obracać, a także skalować.
Keyframed_movement_component używa keyframed_movement_delta jako typu klatki kluczowej. Te delty ruchu mają trzy wartości:
Przekształć: Określa zmiany w przekształceniu względem poprzedniej klatki kluczowej.Czas trwania: Czas trwania klatki kluczowej w sekundach.Dynamika: funkcja dynamiki używana podczas odtwarzania tej klatki kluczowej.
Keyframed_movement_component pobiera tablicę klatek kluczowych, a następnie je odtwarza, dlatego musisz podać wszystkie klatki kluczowe za jednym razem dla każdej animacji, która ma być odtwarzana. Można to zrobić na dwa sposoby:
Można utworzyć w kodzie tablicę złożoną z wielu klatek kluczowych, a następnie przekazać ją do komponentu ruchu w klatce kluczowej i odtworzyć pojedynczą animację.
Możesz utworzyć wiele tablic zawierających pojedynczą klatkę kluczową i przekazywać je indywidualnie do komponentu ruchu w klatce kluczowej, aby odtworzyć kolejno wiele animacji.
Obie te opcje wymagają kompromisów. Tablice pojedynczych klatek kluczowych ułatwiają wykonywanie operacji między klatkami kluczowymi, ale wymagają więcej kodu do przetworzenia. Utworzenie wszystkich klatek kluczowych jednocześnie ułatwia zarządzanie, ale trudniej jest wykonywać operacje podczas odtwarzania animacji. Zarówno samouczek Animacja ruchu rekwizytów, jak i ten samouczek, dotyczą pierwszego podejścia, ale dostarczona zostanie również implementacja drugiego podejścia.
Aby utworzyć indywidualne klatki kluczowe w kodzie, zdefiniujesz klasę segment. Każdy segment będzie reprezentował klatkę kluczową używaną przez komponent keyframed_movement_component, który możesz utworzyć w edytorze. Możesz też uwzględnić dodatkowe dane, takie jak ilość czasu oczekiwania między każdą klatką kluczową. Wykonaj poniższe kroki, aby utworzyć klasę segmentu.
Dodaj nową klasę o nazwie
segmentdo plikuanimate_to_targets_component.verse. Dodaj specyfikator<concrete>, aby umożliwić używanie tej klasy jako wartości@editable.Verse# Defines a segment of animation, which includes a starting position, animation speed and duration, and easing function. # Each segment acts as a single animation, and multiple segments make up an animation sequence. segment<public> := class<concrete>:Do swojej definicji klasy segmentu dodaj następujące pola:
Opcja edytowalnej
jednostkio nazwieSegmentStartPosition. Jednostka ta będzie pełniła funkcję odwołania do pozycji w świecie, od której jednostka ma rozpocząć animację.
Verse# An entity that represents the starting position of this entity during the animation segement. @editable: ToolTip := SourceTip SegmentStartPosition:?entity = falseEdytowalna
wartość zmiennopozycyjnao nazwieAnimationDuration. Jest to czas potrzebny do odtworzenia tego segmentu animacji. Ustaw tę wartość na2.0, aby odtworzenie każdego segmentu animacji trwało dwie sekundy.
Verse# The duration of the animation segment. @editable: ToolTip := AnimationDurationTip AnimationDuration:float = 2.0Edytowalna opcja kontroli dynamiki o nazwie
EasingFunction. Jest to funkcja łagodzenia używana w tym segmencie animacji.
Verse# The easing function to use during this segment of animation. @editable: ToolTip := EasingFunctionTip EasingFunction:?easing_function = falseKażda funkcja łagodzenia jest zdefiniowana za pomocą krzywej Béziera trzeciego stopnia, która składa się z czterech liczb tworzących typ funkcji łagodzenia stosowany przez animację. Np. parametry krzywej łagodzenia na początku powodują, że animacja zwalnia na początku, a następnie przyspiesza.
Parametry krzywej liniowej sprawiają, że animacja jest odtwarzana w stałym tempie. Możesz samodzielnie definiowanie te wartości, aby utworzyć własne niestandardowe krzywe animacji, ale nie musisz w tym przykładzie, ponieważ będziesz używać wartości zdefiniowanych w moduł
KeyframedMovement.Edytowalna opcja wartości zmiennopozycyjnej o nazwie
PauseSeconds. jest to czas w sekundach do pauzy przed rozpoczęciem tego segmentu animacji. Można to sobie wyobrazić jako ilość czasu, w którym jednostka się zatrzymuje, zanim przejdzie z każdego punktu na swojej ścieżce.
Verse# The number of seconds to pause before starting this animation segment. @editable: ToolTip := PauseTip PauseSeconds:?float = falseW definicji klasy
animate_to_targets_componentdodaj następujące pola:Edytowalna tablica segmentów o nazwie
Segments(Segmenty). Będzie to odwołanie do każdego segmentu animacji, który składa się na ogólną animację, przez którą przechodzi jednostka.
Verse# Segments of the keyframed movement animation. @editable: ToolTip := SegmentsTip var Segments<private>:[]segment = array{}Edytowalna
funkcja kontroli dynamikio nazwieDefaultEasingFunction. Jeśli segment animacji nie określa funkcji dynamiki, będzie używana domyślnie. Ustaw to naease_in_out_cubic_bezier_easing_function.
Verse# Movement easing function between two targets @editable: ToolTip := DefaultEasingFunctionTip var DefaultEasingFunction<public>:easing_function = ease_in_out_cubic_bezier_easing_function{}Zapisz kod i go skompiluj. W edytorze przy jednostkach powinna pojawić się tablica
Segmentyprzy tych jednostkach, które mają dołączony animate_to_targets_component.
Tworzenie klatek kluczowych za pomocą kodu
Gdy klasa segmentu będzie gotowa, czas utworzyć klatki kluczowe definiowane przez segmenty. Zbudujesz klatki kluczowe pojedynczo i dodasz je do tablicy, a następnie przekażesz tablicę do swojego keyframed_movement_component. Będzie to wymagało odrobiny matematyki, którą zdefiniujemy właśnie teraz.
Operacje matematyczne są przydatne w różnych scenariuszach, dlatego dobrze jest umieścić tę logikę w pliku narzędziowym, aby mieć do niej dostęp z dowolnego komponentu Verse. Aby dowiedzieć się więcej na temat dobrych praktyk Verse podczas pracy z jednostkami, patrz: Tworzenie własnego komponentu Verse. Aby utworzyć plik narzędziowy, wykonaj następującą procedurę:
Utwórz w pliku
animate_to_targets.versenowy moduł o nazwieUtilities. przechowa wspólną logikę, której będziesz używać w swoim projekcie.Verse# Module containing utility functions. Utilities<public> := module:Dodaj nowy alias typu
vector3o nazwieVectorOnesdo modułu Narzędzia, który będzie tworzyłvector3, gdzie ustawieniaLewo,PrawoiGóramają wartość1.0. Będziesz używać tego wektora później, aby usprawnić pewne obliczenia matematyczne, dlatego zdefiniowanie aliasu typu oznacza, że nie musisz wielokrotnie pisaćvector3{Left := 1.0, Up := 1.0, Forward := 1.0}. Ponieważ zaimportowano zarównoi/UnrealEngine.com/Temporary/SpatialMathmoduły, musisz określić, że jest to plik/Verse.org/SpatialMathvector3, ponieważ oba moduły zawierają jego definicję.Verse# Utility function for the identity of component-wise vector multiplication. VectorOnes<public>()<transacts>:(/Verse.org/SpatialMath:)vector3 = (/Verse.org/SpatialMath:)vector3{Left := 1.0, Up := 1.0, Forward := 1.0}Dodaj nową funkcję o nazwie
GetDeltaTransform()do modułuNarzędzia. Funkcja ta obliczy różnicę między dwoma przekształceniami i zwróci wartość delta. Dodaj modyfikator<transacts>do tej funkcji, aby zezwolić na jej wycofywanie do poprzedniej wersji. Określ/Verse.org/SpatialMathjako moduł dla każdegoPrzekształć, ponieważ będziesz obliczać różnicę między przekształceniami jednostek.Verse# Get the delta transform between two given transforms. GetDeltaTransform<public>(TransformOne:(/Verse.org/SpatialMath:)transform, TransformTwo:(/Verse.org/SpatialMath:)transform)<transacts>:(/Verse.org/SpatialMath:)transform=W
GetDeltaTransformzainicjuj nowe/Verse.org/SpatialMathbędąceprzekształceniem. Ustawtranslacjęna różnicę między translacjami każdego przekształcenia. Ustawobrótna wynik wywołaniaMakeComponentWiseDeltaRotation(). Funkcja ta znajduje się w module/UnrealEngine.com/Temporary/SpatialMath, więc musisz dokonać konwersji z obrotów/Verse.org/SpatialMathna obroty/UnrealEngine.com/Temporary/SpatialMath. Można to zrobić za pomocą funkcjiFromRotation(). WywołajMakeComponentWiseDeltaRotation(), przekazując obrót każdego przekształcenia po skonwertowaniu go za pomocąFromRotation(). Następnie skonwertuj wynik tego wywołania funkcji ponownie przy użyciuFromRotation(), aby przekonwertować z powrotem na/Verse.org/SpatialMathobrót. Na końcu ustaw skalę na wynik sumy wartościVectorOnesi różnicy między pierwszą a drugą skalą podzielonej przez pierwszą skalę. Gwarantuje to prawidłowe skalowanie jednostki podczas animacji. Gotowa funkcjaGetDeltaTransform()powinna wyglądać następująco:Verse# Get the delta transform between two given transforms. GetDeltaTransform<public>(TransformOne:(/Verse.org/SpatialMath:)transform, TransformTwo:(/Verse.org/SpatialMath:)transform)<transacts>:(/Verse.org/SpatialMath:)transform= (/Verse.org/SpatialMath:)transform: Translation := TransformTwo.Translation - TransformOne.Translation Rotation := FromRotation(MakeComponentWiseDeltaRotation( FromRotation(TransformTwo.Rotation), FromRotation(TransformOne.Rotation))) Scale := VectorOnes() + ((TransformTwo.Scale - TransformOne.Scale) / TransformOne.Scale)Na końcu dodaj funkcję o nazwie
TryGetvalueOrDefault()do modułuUtilitiesi dodaj do niej modyfikator<transacts>. Funkcja ta przyjmuje wartośćopcjiokreślonego typu oraz wartość domyślną tego samego typu i return wartość domyślną lub przedmiot w obrębieValue, jeśli taki istnieje. Jest to przydatne, gdy chcesz sprawdzić, czy wartość w klasie jest rzeczywiście zainicjowana, i gwarantuje, że zwrócisz jakąś wartość, jeśli nie jest. WTryGetValueOrDefault()sprawdź, czyValuezawiera wartość i zwraca ją. W przeciwnym razie zwróćDefault(wartość domyślną). Gotowy modułUtilitiesi funkcjaTryGetValurOrDefault()powinny wyglądać następująco:Verse# Module containing utility functions. Utilities<public> := module: # Utility function for the identity of component-wise vector multiplication. VectorOnes<public>()<transacts>:(/Verse.org/SpatialMath:)vector3 = (/Verse.org/SpatialMath:)vector3{Left := 1.0, Up := 1.0, Forward := 1.0} # Get the delta transform between two given transforms. GetDeltaTransform<public>(TransformOne:(/Verse.org/SpatialMath:)transform, TransformTwo:(/Verse.org/SpatialMath:)transform)<transacts>:(/Verse.org/SpatialMath:)transform=
Po zdefiniowaniu matematyki możesz tworzyć klatki kluczowe z kodu!
Aby utworzyć funkcje tworzenia klatek kluczowych, wykonaj poniższe kroki:
Dodaj nową funkcję o nazwie
ConstructKeyframe()do definicji klasyanimate_to_targets. W tej funkcji stosuje się jednostkę źródłową, jednostkę docelową, opcjonalną funkcję dynamiki jazdy oraz czas trwania. Zwraca nawet tablicękeyframed_movements_delta.Verse# Construct a single keyframe that animates between the Source and Destination entity using the given easing function over a set duration. ConstructKeyframe<private>(Source:entity, Destination:entity, Easing:?easing_function, Duration:float)<transacts><decides>:[]keyframed_movement_delta=W
ConstructKeyframe()najpierw pobierz przekształcenia jednostekSourceiDestination, wywołującGetGlobalTransform().Verse# Construct a single keyframe which animates between the Source and Destination entity using the given easing function over a set duration. ConstructKeyframe<private>(Source:entity, Destination:entity, EasingFunction:easing_function, Duration:float)<transacts><decides>:[]keyframed_movement_delta= var SourceTransform:(/Verse.org/SpatialMath:)transform = Source.GetGlobalTransform() var DestinationTransform:(/Verse.org/SpatialMath:)transform = Destination.GetGlobalTransform()Inicjowanie tablicy za pomocą jednego elementu
keyframed_movement_delta. Ustawprzekształceniena wynik wywołaniaGetDeltaTransform(), przekazując przekształcenie źródłowe i docelowe, a następnie ustawczas trwaniaiłagodzeniena wartości przekazane do tej funkcji. Gotowa funkcjaConstructKeyframe()powinna wyglądać następująco:Verse# Construct a single keyframe which animates between the Source and Destination entity using the given easing function over a set duration. ConstructKeyframe<private>(Source:entity, Destination:entity, EasingFunction:easing_function, Duration:float)<transacts><decides>:[]keyframed_movement_delta= var SourceTransform:(/Verse.org/SpatialMath:)transform = Source.GetGlobalTransform() var DestinationTransform:(/Verse.org/SpatialMath:)transform = Destination.GetGlobalTransform() array: keyframed_movement_delta: Transform := Utilities.GetDeltaTransform(SourceTransform, DestinationTransform) Duration := Duration Easing := EasingFunction
Funkcja ta buduje poszczególne klatki kluczowe, ale do zbudowania pełnych animacji potrzebujesz więcej logiki.
Dodaj nową funkcję o nazwie
ConstructAndPlayAnimations()do definicji klasyanimate_to_targets. Funkcja ta pobiera tablicę segmentów oraz tryb odtwarzania animacji, a następnie używa ich do tworzenia oraz odtwarzania pełnej animacji. Dodaj modyfikator<suspends>do tej funkcji, aby umożliwić uruchamianie jej w sposób asynchroniczny.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void=W
ConstructAndPlayAnimations()zdefiniuj nową zmiennąlogicznąo nazwieShouldBreakOuti zainicjuj ją nafalse. Biorąc pod uwagę trzy tryby odtwarzania ruchu z klatkami kluczowymi, należy traktować każdy z nich osobno. Wykorzystasz wyrażenieloopdo ciągłego budowania animacji w celu obsługi trybów pętli ping pongowej, ale tryb one-shot powinien zakończyć się po zakończeniu pierwszej iteracji. Sprawdź, czy tryb odtwarzania animacji jest trybem jednorazowym, a jeśli tak, ustaw dlaShouldBreakOutwartość true.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void= var ShouldBreakOut:logic = false # If this is a oneshot animation, break out of loop after it plays once. if (oneshot := oneshot_keyframed_movement_playback_mode[AnimationPlayback]): set ShouldBreakOut = trueNastępnie w wyrażeniu
ifpobierzkeyframed_movement_componentjednostki w zmiennejKeyframedMovementComponent. Następnie pobierz przekształcenie początkowe animacji w zmiennej o nazwieStartingTransform, pobierając pierwszy element tablicyInSegments, a następnie jego przekształcenie globalne.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform()Na koniec umieść jednostkę w jej lokalizacji początkowej, ustawiając dla niej transformację globalną na transformację początkową i uśpij przez okres
InitialPauseSecondsprzed odtworzeniem animacji.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform() then: Entity.SetGlobalTransform(StartingTransform) # Sleep for initial pause. Sleep(InitialPauseSeconds)Teraz czas utworzyć tablicę klatek kluczowych, z której zostanie zbudowana animacja. Najpierw zainicjuj nową tablicę zmiennych
keyframed_movement_deltao nazwieKeyframes. Następnie w wyrażeniuforwykonaj iterację po każdym segmencie w tablicyInSegments, pobierając zarówno segment, jak i jego indeks w zmiennej lokalnej o nazwieIndex.Verse# Build the array of keyframes to play the animation from. var Keyframes:[]keyframed_movement_delta = array{} for: Index -> Segment:InSegments SourceEntity := Segment.SegmentStartPosition? DestinationEntity := InSegments[Index + 1].SegmentStartPosition?Teraz pobierz funkcję dynamiki jazdy używaną dla tej klatki kluczowej w zmiennej lokalnej o nazwie
Dynamikadynamiki, wywołującTryGetValueOrDefault(), przekazującSegment.EasingFunctioniDefaultEasingFunction. Pobierz również czas trwania segmentu animacji w zmiennej lokalnejDurationzSegment.AnimationDuration. Po wprowadzeniu wszystkich wartości w wyrażeniuif utwórz klatkę kluczową,przekazując każdą wartość do ConstructKeyframe[],a następnie dodaj wynik do tablicyKlatekkluczowych. Po utworzeniu wszystkich klatek kluczowych Zestaw tablicę klatek kluczowych na komponent ruchu z klatkami kluczowymi, wywołującfunkcję SetKeyframes()przekazującątablicęklatek kluczowych oraz funkcję PlaybackMode.Verse# Build the array of keyframes to play the animation from. var Keyframes:[]keyframed_movement_delta = array{} for: Index -> Segment:InSegments SourceEntity := Segment.SegmentStartPosition? DestinationEntity := InSegments[Index + 1].SegmentStartPosition? do: Easing := Utilities.TryGetValueOrDefault(Segment.EasingFunction, DefaultEasingFunction) Duration := Segment.AnimationDuration # Construct each keyframe and add it to the array.Po ustawieniu tablicy klatek kluczowych czas zacząć je odtwarzać. Animacja musi być wykonywana w pętli, ale powinna zostać zatrzymana po pierwszej iteracji, jeśli tryb animacji jest ustawiony na jedno ujęcie. Musi też obsługiwać wstrzymywanie na każdą klatkę kluczową, jeśli segment zawiera jakieś
PauseSeconds. Aby to obsłużyć, skonfiguruj wyrażenieloopzawierające wyrażeniefor. W wyrażeniuforwykonaj iterację po każdej klatce kluczowej na tablicyklatek kluczowych, dodatkowo uzyskując indeks każdej klatki kluczowej w zmiennejKeyframeIndex.VerseKeyframedMovementComponent.SetKeyframes(Keyframes, PlaybackMode) # Loop playing the animation from the keyframed_movement_component, pausing at each # keyframe for a specified duration. Will break out of the loop if the animation mode # is set to oneshot. loop: for(KeyframeIndex -> Keyframe:Keyframes):Wewnątrz wyrażenia
forpobierz segment powiązany z tą klatką kluczową, indeksując w tablicyInSegmentsprzy użyciuKeyframeIndex. Następnie, jeśli segment zawiera jakiekolwiekPauseSeconds, wywołujSleep()na ten czas. Następnie wywołajKeyframedMovementComponent.Play(), po czym wyczekuj zdarzeniaKeyframeReachedEventi wywołajKeyframedMovementComponent.Pause(). Ta funkcja wstrzymuje animację przy każdej klatce kluczowej na czasPauseSeconds, po czym odtwarza i oczekuje na kolejną klatkę kluczową, a następnie ponownie ją zatrzyma. Na końcu wyrażenialoopsprawdź, czyShouldBreakOutma wartość true, a jeśli tak, przerwij pętlę.Verse# Loop playing the animation from the keyframed_movement_component, pausing at each # keyframe for a specified duration. Will break out of the loop if the animation mode # is set to oneshot. loop: for(KeyframeIndex -> Keyframe:Keyframes): if: SegmentReached := InSegments[KeyframeIndex] then: Sleep(SegmentReached.PauseSeconds) KeyframedMovementComponent.Play()Gotowa funkcja
ContstructAndPlayAnimations()powinna wyglądać następująco:Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void= var ShouldBreakOut:logic = false # If this is a oneshot animation, break out of loop after it plays once. if (oneshot := oneshot_keyframed_movement_playback_mode[AnimationPlayback]): set ShouldBreakOut = true # Position this entity in the correct starting position.
W kolejnym kroku utworzysz gotowiec animowanej jednostki, a następnie dodasz jego instancję w poziomie.