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.
Każdy rekwizyt trybu kreatywnego ma kontroler play_animation_controller, którego można używać do odtwarzania animacji danego rekwizytu. 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.
Przełączając się między różnymi animacjami przeszkód, możesz, przy użyciu tych samych rekwizytów, utworzyć bogatą gamę różnych wyzwań stawianych graczom. W tej sekcji dowiesz się, jak wykorzystać to potężne narzędzie do tworzenia własnych animacji i wprawiania rekwizytów w ruch!
Konfigurowanie elementów sterujących animacji
Wykonaj poniższe kroki, aby skonfigurować elementy sterowania animacją dla rekwizytów:
Przy użyciu Eksploratora Verse utwórz nowy plik Verse o nazwie
movement_behaviors. Będą w nim przechowywane funkcje narzędziowe potrzebne do animacji rekwizytów.Dodaj instrukcje
using { /Fortnite.com/Devices },using { /Fortnite.com/Devices/CreativeAnimation }iusing { /UnrealEngine.com/Temporary/SpatialMath }na początku pliku, aby zaimportować te moduły. Są one potrzebne, aby animować rekwizyt.W
movement_behaviorsutwórz nowy typ wyliczeniowy (enum) o nazwiemove_to_ease_type. Wartości w tym enum odpowiadają różnym typom łagodzenia animacji. Z każdym typem łagodzenia możesz się zapoznać w moduleInterpolationTypes.Verse# This file stores functions common to animating creative props using keyframes. # It also defines the move_to_ease_type enum to help in building animations. using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/SpatialMath } using { /Fortnite.com/Devices/CreativeAnimation } # Represents the different movement easing types. move_to_ease_type<public> := enum {Linear, Ease, EaseIn, EaseOut, EaseInOut}Dodaj nowy alias typu
vector3o nazwieVectorOnestworzący wektorvector3, którego współrzędneX,YiZsą ustawione na 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{X:=1.0, Y:=1.0, Z:=1.0}.Verse# Initializes a vector3 with all values set to 1.0. VectorOnes<public>:vector3 = vector3{X:=1.0, Y:=1.0, Z:=1.0}Dodaj nową metodę
GetCubicBezierForEaseType(), która przyjmujemove_to_ease_typei zwracacubic_bezier_parameters.Verse# Return the cubic_bezier_parameters based on the given move_to_ease_type. GetCubicBezierForEaseType(EaseType:move_to_ease_type):cubic_bezier_parameters=Krzywa Beziera trzeciego stopnia składa się z czterech liczb, które definiują 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 zdefiniować te wartości samodzielnie, aby utworzyć własne niestandardowe krzywe animacji. W tym przykładzie nie jest to potrzebne, ponieważ użyjesz wartości zdefiniowanych w module
InterpolationTypes.W
GetCubicBezierForEaseType()w wyrażeniucase()pobierzcubic_bezier_parametersz modułuInterpolationTypesna podstawiemove_to_ease_type. Na przykładEaseOutpowinno zwracaćInterpolationTypes.EaseOut,Linearpowinno zwracaćInterpolationTypes.Linearitd. Ukończona funkcjaGetCubicBezierForEaseType()powinna wyglądać następująco:Verse# Return the cubic_bezier_parameters based on the given move_to_ease_type. GetCubicBezierForEaseType(EaseType:move_to_ease_type):cubic_bezier_parameters= case (EaseType): move_to_ease_type.Linear => InterpolationTypes.Linear move_to_ease_type.Ease => InterpolationTypes.Ease move_to_ease_type.EaseIn => InterpolationTypes.EaseIn move_to_ease_type.EaseOut => InterpolationTypes.EaseOut move_to_ease_type.EaseInOut => InterpolationTypes.EaseInOutWróć do klasy
movable_propi dodaj nowy edytowalny typ wyliczeniowymove_to_ease_typeo nazwieMoveEaseType. Jest to typ łagodzenia, który rekwizyt będzie stosował do swojej animacji.Verse# The type of animation easing to apply to the RootProp's movement. The easing type # changes the speed of the animation based on its animation curve. @editable {ToolTip := MoveEaseTypeTip} MoveEaseType:move_to_ease_type = move_to_ease_type.EaseInOut
Budowanie animacji przy użyciu klatek kluczowych
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 nawet skalować.
Klatki kluczowe mają pięć wartości. DeltaLocation, DeltaRotation i DeltaScale określają zmiany każdej wartości, które rekwizyt wykonuje od początku do końca klatki kluczowej. Istnieje też Time, czyli czas trwania animacji (w sekundach), oraz Interpolation, czyli tryb interpolacji dla klatki kluczowej. Przykładowa klatka kluczowa może wyglądać następująco:
# An example keyframe.
KeyFrame := keyframe_delta:
# The target position of the `creative_prop`. This is the difference between the starting and ending translation of the prop.
DeltaLocation := EndTransform.Translation - StartTransform.Translation,
# The target rotation for the `creative_prop` to rotate to.
DeltaRotation := EndTransform.Rotation,
# The target scale for the `creative_prop`. Scale is multiplicative to the starting Scale of the `creative_prop`
Wykonaj poniższe kroki, aby utworzyć animację przy użyciu klatek kluczowych:
Dodaj nową metodę rozszerzającą
creative_propo nazwieMoveToEase()do plikumovement_behaviors. Ta funkcja przyjmuje pozycję, obrót i skalę, do których ma się przemieścić rekwizyt, czas trwania animacji, typ łagodzenia ruchu i tryb animacji.Verse# Animate a creative_prop by constructing an animation from a single keyframe, and then playing that animation on the prop. # This method takes a Position, Rotation, and Scale for the prop to end at, the duration of the animation, # the type of easing to apply to the movement, and the animation mode of the animation. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Rotation:rotation, Scale:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void=W
MoveToEase()pobierz kontroler animacji rekwizytu trybu kreatywnego za pomocąGetAnimationController[]. Kontroler animacji umożliwia odtwarzanie animacji na rekwizycie i ujawnia zdarzenie, na które będziesz później czekać.Verse(CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Rotation:rotation, Scale:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= # Get the animation controller for the CreativeProp to move. if (AnimController := CreativeProp.GetAnimationController[]):Oblicz zmianę skali rekwizytu poprzez wywołanie
ScaleMultiplicative. W tej sytuacji kwestia wartościScalejest trochę bardziej skomplikowana. Skala jest proporcjonalna, dlatego skala przekształcenia końcowego musi być wielkością, o którą początkowe przekształcenie ma być przeskalowane, a nie wartością docelową skalowania. Jeśli na przykład skala przekształcenia początkowego to1.2, a chcesz wykonać skalowanie do1.5, musisz w praktyce wykonać skalowanie o1.25, ponieważ1.2 * 1.25 = 1.5. Końcowa wartość toVectorOnesplus różnica między nową skalą a starą skalą podzielona przez starą skalę. Potrzebujesz tej wartości, aby mieć pewność, że animacja będzie wykonywana poprawnie w przypadku zmiany jej rozmiaru w trakcie animacji.Verse# Calculate the multiplicative scale for the keyframe to scale to. ScaleMultiplicative:vector3 = VectorOnes + ((Scale - CreativeProp.GetTransform().Scale) / CreativeProp.GetTransform().Scale)Każda animacja potrzebuje do działania tablicy klatek kluczowych. W tej funkcji używasz tylko jednej klatki kluczowej i rzutujesz ją na tablicę. Tablica będzie miała tylko jedną klatkę kluczową, dlatego rekwizyt wykona jeden ruch w postaci płynnego animacyjnego przejścia do nowej lokalizacji. Funkcja
MoveToEase()będzie wywoływana w pętli, więc rekwizyt może kontynuować animację bez potrzeby określania wielu animacji jedna po drugiej.Zdefiniuj nową tablicę
keyframe_deltao nazwieKeyframes. Ustaw tę tablicę tak, aby była równa nowej tablicy, a wewnątrz tej tablicy utwórz nową tablicękeyfram_delta.Verse# Build the keyframe array from a single keyframe_delta of the given values. Keyframes:[]keyframe_delta = array: keyframe_delta:Wewnątrz definicji
keyframe_deltazainicjuj każdą wartość potrzebną do utworzenia klatki kluczowej. UstawDeltaLocationna różnicę między nową wartościąPositiona translacją rekwizytu trybu kreatywnego. UstawDeltaRotationnaRotation, aDeltaScalena obliczoną wcześniej wartośćScaleMultiplicative. UstawTimenaDurationi uzyskaj odpowiednią wartośćInterpolationdo ustawienia poprzez wywołanieGetCubicBezierForEaseType().Verse# Build the keyframe array from a single keyframe_delta of the given values. Keyframes:[]keyframe_delta = array: keyframe_delta: DeltaLocation := Position - CreativeProp.GetTransform().Translation, DeltaRotation := Rotation, DeltaScale := ScaleMultiplicative, Time := Duration, Interpolation := GetCubicBezierForEaseType(EaseType)Po utworzeniu tablicy
Keyframesustaw ją jako animację kontrolera animacji przy użyciuSetAnimation()i poprzez przekazanieAnimationMode. Odtwórz animację przy użyciuPlay(), a następnie za pomocąAwait()czekaj naMovementCompletedEvent. Ukończona metoda rozszerzającaMoveToEase()powinna wyglądać następująco:Verse# Animate a creative_prop by constructing an animation from a single keyframe, and then playing that animation on the prop. # This method takes a Position, Rotation, and Scale for the prop to end at, the duration of the animation, # the type of easing to apply to the movement, and the animation mode of the animation. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Rotation:rotation, Scale:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= # Get the animation controller for the CreativeProp to move. if (AnimController := CreativeProp.GetAnimationController[]): # Calculate the multiplicative scale for the keyframe to scale to. ScaleMultiplicative:vector3 = VectorOnes + ((Scale - CreativeProp.GetTransform().Scale) / CreativeProp.GetTransform().Scale)
Przeciążanie funkcji
Właśnie napisana funkcja MoveToEase() jest przydatna, ale przekazywanie do niej tak wielu zmiennych przy każdym wywołaniu może być skomplikowane. Mogą się zdarzyć sytuacje, w których chcesz zmienić tylko jedną część przekształcenia rekwizytu, np. translację lub obrót. W takim przypadku warto mieć do dyspozycji prostszą funkcję do wywołania.
Aby rozwiązać ten problem, możesz wykorzystać przeciążanie funkcji. Dzięki przeciążaniu funkcji MoveToEase() możesz skonfigurować wiele metod o tej samej nazwie, które umożliwią obsługiwanie różnych typów danych wejściowych. Wykonaj poniższe kroki, aby skonfigurować przeciążone funkcje.
W pliku
movement_behaviorsprzeciąż funkcjęMoveToEase()poprzez utworzenie kolejnej metody rozszerzającej o tej samej nazwie, ale z innymi danymi wejściowymi. Ta funkcjaMoveToEase()zaktualizuje tylko translację rekwizytu, a obrót i skalę pozostawi bez zmian. To oznacza, że potrzebujesz jedynie argumentówPosition,Duration,EaseTypeiAnimationMode.Verse# An overload of MoveToEase() that changes the position of the prop while keeping the rotation and scale the same. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void=W nowej funkcji
MoveToEase()wywołaj oryginalną funkcjęMoveToEase(), przekazując wszystkie dane wejściowe orazIdentityRotation()iVectorOnesjakoScale. FunkcjaIdentityRotation()zwraca obrót z każdą wartością ustawioną na0, czyli(0,0,0). W tym miejscu trzeba użyć funkcjiIdentityRotation(), ponieważ nie chcesz dodawać obrotu do animacji. Przeciążona funkcjaMoveToEase()powinna wyglądać następująco:Verse# An overload of MoveToEase() that changes the position of the prop while keeping the rotation and scale the same. (CreativeProp:creative_prop).MoveToEase<public>(Position:vector3, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= CreativeProp.MoveToEase(Position, IdentityRotation(), VectorOnes, Duration, EaseType, AnimationMode)Powtórz tę procedurę, aby utworzyć inną przeciążoną metodę, która zmienia tylko obrót (nie zmienia translacji ani skali). Nowa przeciążona metoda powinna wyglądać następująco:
Verse# An overload of MoveToEase() that changes the rotation of the prop while keeping the position and scale the same. (CreativeProp:creative_prop).MoveToEase<public>(Rotation:rotation, Duration:float, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= CreativeProp.MoveToEase(CreativeProp.GetTransform().Translation, Rotation, VectorOnes, Duration, EaseType, AnimationMode)Powtórz ponownie, aby utworzyć metodę, która zmienia tylko skalę (nie zmienia translacji ani obrotu). Skoro jednak
translationiscalesą wektoremvector3, wynikowa funkcja miałaby tę samą sygnaturę jak przeciążenie utworzone dla translacji, a to spowodowałoby wystąpienie błędu. Można to rozwiązać, zmieniając kolejność parametrów funkcji. Zamień miejscami pozycjęDurationiScale, aby uzyskać przeciążoną funkcję. Przeciążona metoda powinna wyglądać następująco:Verse# An overload of MoveToEase() that changes the position and scale of the prop while keeping the rotation the same. (CreativeProp:creative_prop).MoveToEase<public>(Duration:float, Scale:vector3, EaseType:move_to_ease_type, AnimationMode:animation_mode)<suspends>:void= CreativeProp.MoveToEase(CreativeProp.GetTransform().Translation, IdentityRotation(), Scale, Duration, EaseType, AnimationMode)Warto mieć wersję funkcji
MoveToEase(), która może obsługiwać wiele klatek kluczowych. Taki rezultat można uzyskać, konfigurując zawczasu klatki kluczowe i przekazując je wszystkie do funkcjiMoveToEase(). Dzięki temu funkcja musi jedynie ustawić i odtworzyć animację na rekwizycie Trybu kreatywnego. Dodaj nowe przeciążenie funkcjiMoveToEase(), która jako dane wejściowe przyjmuje tablicę klatek kluczowych i tryb animacji.Verse# An overload of MoveToEase() that takes a pre-built array of keyframes and plays an animation. (CreativeProp:creative_prop).MoveToEase<public>(Keyframes:[]keyframe_delta, AnimationMode:animation_mode)<suspends>:void= if (AnimController := CreativeProp.GetAnimationController[]): AnimController.SetAnimation(Keyframes, ?Mode:=AnimationMode) AnimController.Play() AnimController.MovementCompleteEvent.Await()Zapisz kod i go skompiluj.
Po skonfigurowaniu metod czas wprawić wszystko w ruch! W kolejnej sekcji wykonasz translację rekwizytów, aby utworzyć ruchome platformy!
Complete Code
Oto kompletny kod utworzony w tej sekcji:
movement_behaviors.verse
# This file stores functions common to animating creative props using keyframes.
# It also defines the move_to_ease_type enum to help in building animations.
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Characters}
using { /Fortnite.com/Devices/CreativeAnimation }
# Represents the different movement easing types.
move_to_ease_type<public> := enum {Linear, Ease, EaseIn, EaseOut, EaseInOut}