이 튜토리얼은 사물 이동 애니메이션 적용하기 튜토리얼의 고급 씬 그래프 버전입니다. UEFN에서 씬 그래프 외부의 애니메이션 기반 이동에 대해 자세히 알아보고 싶다면, 해당 튜토리얼을 확인한 후 여기로 돌아오세요!
이동 플랫폼은 대부분의 플랫폼 게임에서 흔히 등장하며, 플레이어가 타깃 사이에서 정확하게 점프하여 목표에 도달하는 데 도전하게 만듭니다.
UEFN에서 사물을 이동할 수 있는 몇 가지 방법이 있습니다. TeleportTo[] 또는 MoveTo()와 같은 함수를 사용하여 트랜스폼을 직접 수정하거나, 사물 이동 장치와 같은 다른 장치를 사용하여 프리셋 경로에서 사물을 이동할 수 있습니다. 하지만 애니메이션 형태의 다른 유용한 옵션도 존재합니다.
애니메이션은 사물의 트랜스폼 이동과 비교해 몇 가지 장점이 있습니다. 애니메이션은 보통 MoveTo()나 TeleportTo()로 오브젝트를 이동하는 것보다 이동이 매끄러운데, 이는 게임 틱마다 이러한 함수를 호출해야 하는 네트워크 지연시간을 방지하기 때문입니다.
또한 애니메이션은 플레이어 또는 다른 오브젝트와 더 일정하게 충돌하게 하여, 사물 이동 장치를 사용하는 것보다 뛰어난 수준으로 오브젝트가 어디에서 어떻게 움직일지를 제어합니다. 루프에서 애니메이션을 재생하거나, 왕복 모드를 통해 왕복 재생할 수 있습니다.
애니메이션에서는 보간 타입도 선택할 수 있습니다. 보간 타입은 이징 타입, 즉 애니메이션이 따르는 애니메이션 커브를 결정합니다. 예를 들어, 선형 보간 타입은 일정한 속도로 애니메이션을 재생하는 반면, 이즈 인 타입은 느리게 시작하여 끝으로 갈수록 빨라집니다.
애니메이션에 맞는 적절한 보간 타입을 선택함으로써 사물이 느려지거나, 빨라지거나, 선형으로 이동해야 하는 여러 지점을 지정할 수 있습니다. 이러한 비헤이비어를 구현하는 컴포넌트를 레벨의 엔티티에 적용하여 플레이어가 탐색할 수 있는 이동 플랫폼을 제작할 수 있습니다.
먼저 이동 플랫폼이 수행할 수 있어야 하는 비헤이비어의 종류를 고려합니다. 지정된 시작 위치에서 애니메이션을 시작한 후 여러 지점으로 이동해야 합니다. 이동의 끝 지점에 도달하면 원래 시작 위치로 돌아가거나 제자리에 있을 수 있어야 합니다.
이러한 이동은 특정 지속 시간 동안 수행되어야 하며, 이동하는 동안 각 지점에서 적절하게 회전하고 스케일 조절도 가능해야 합니다. 이러한 각 비헤이비어의 달성을 위해서는 특정 코드가 필요하지만, 단순한 클래스에서 시작하여 축적해 나가면 다양한 아이디어를 빠르게 반복작업할 수 있습니다. 애니메이션 기반 이동 컴포넌트를 만들려면 다음 단계를 따르세요.
animate_to_targets_component로 명명한 새 Verse 컴포넌트를 생성하여 Visual Studio Code에서 엽니다. 자신만의 Verse 컴포넌트를 만드는 방법에 대한 자세한 내용은 Verse를 사용하여 자신만의 컴포넌트 만들기를 참고하세요.using명령문을/Verse.org/SpatialMath,/Verse.org/SceneGraph/KeyframedMovement,/UnrealEngine.com/Temporary/SpatialMath모듈에 추가합니다. 나중에 이러한 각 모듈에서 함수가 필요합니다.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):튜토리얼의 이 섹션에서 사용되는 툴팁은 아래에 포함되어 있습니다. 툴팁을 복사하여
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."animate_to_targets_component클래스 정의에 다음 필드를 추가합니다.InitialPauseSeconds로 명명된 편집 가능한 숫자 float 변수를 추가합니다. 엔티티가 애니메이션을 시작하기까지 걸리는 시간입니다. 이를10.0으로 설정하면 엔티티가 애니메이션을 시작할 때까지 10초 동안 대기합니다.
Verse# Amount of time to pause before the animation starts. @editable_number(float): ToolTip := SpeedTip MinValue := option{0.0} var InitialPauseSeconds<public>:float = 10.0AnimationPlaybackMode로 명명된 편집 가능한keyframed_movement_playback_mode를 추가합니다. 엔티티의 애니메이션을 위한 애니메이션 모드입니다. 이 모드를loop_keyframed_movement_playback_mode로 설정합니다. 즉, 기본적으로 애니메이션이 완료되면 엔티티가 루프되고 애니메이션을 처음부터 다시 시작합니다.
Verse# The playback mode used by this animation. @editable: ToolTip := PlaybackModeTip var PlaybackMode<public>:keyframed_movement_playback_mode = loop_keyframed_movement_playback_mode{}코드를 저장하고 컴파일합니다.
애니메이션을 세그먼트로 분할하기
코드로 애니메이션을 만들기 위해 키프레임을 사용합니다. 애니메이션은 하나 이상의 키프레임으로 만들어지며, 각 키프레임은 애니메이션의 특정 지점에서 오브젝트의 값을 지정합니다. 키프레임을 사용하여 애니메이션을 제작하면 사물이 이동하고, 회전하고, 스케일 조절되는 여러 지점을 지정할 수 있습니다.
keyframed_movement_component는 키프레임 타입으로 keyframed_movement_delta를 사용합니다. 이러한 이동 델타에는 다음 세 가지 값이 있습니다.
트랜스폼(Transform): 이전 키프레임을 기준으로 트랜스폼의 변경을 지정합니다.Duration: 키프레임에 소요되는 시간(초)입니다.이징(Easing): 이 키프레임 재생 시 사용할 이징 함수입니다.
keyframed_movement_component가 키프레임의 배열을 받은 후 재생하기 때문에, 재생할 각 애니메이션을 위한 모든 키프레임을 한꺼번에 제공해야 합니다. 저장하는 방법은 두 가지가 있습니다.
코드로 여러 키프레임의 배열을 빌드한 다음, 키프레임이 지정된 이동 컴포넌트로 전달한 후 단일 애니메이션을 재생할 수 있습니다.
단일 키프레임이 포함된 여러 배열을 빌드한 다음, 키프레임이 지정된 이동 컴포넌트로 이를 개별적으로 전달하여 시퀀스에서 여러 애니메이션을 재생할 수 있습니다.
두 옵션 모두 절충점을 내포하고 있습니다. 단일 키프레임 배열을 사용하면 키프레임 간 작업을 더 쉽게 수행할 수 있지만, 처리해야 할 코드가 더 많아집니다. 키프레임을 모두 한 번에 구성하면 관리하기가 더 쉽지만, 애니메이션이 재생되는 동안 작업을 수행하기가 더 어렵습니다. 사물 이동 애니메이션 적용하기 튜토리얼과 이 튜토리얼 모두 첫 번째 접근 방법을 다루지만, 두 번째 접근 방법으로 구현하는 예시도 추후 제공될 예정입니다.
코드로 개별 키프레임을 빌드하기 위해 세그먼트 클래스를 정의해야 합니다. 각 세그먼트는 에디터에서 빌드할 수 있는 keyframed_movement_component에 의해 사용되는 키프레임을 나타냅니다. 또한 각 키프레임 간 대기할 시간과 같은 추가 데이터도 포함할 수 있습니다. 세그먼트 클래스를 빌드하려면 아래 단계를 따릅니다.
animate_to_targets_component.verse파일에 새 클래스를 추가하고segment로 명명합니다. 이 클래스가@editable값으로 사용될 수 있도록<concrete>지정자를 추가합니다.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>:세그먼트 클래스 정의에 다음 필드를 추가합니다.
SegmentStartPosition로 명명된 편집 가능한entity옵션을 추가합니다. 이 엔티티는 엔티티가 애니메이션 적용을 시작해야 하는 월드 포지션에 대한 레퍼런스 역할을 합니다.
Verse# An entity that represents the starting position of this entity during the animation segement. @editable: ToolTip := SourceTip SegmentStartPosition:?entity = falseAnimationDuration로 명명된 편집 가능한float를 추가합니다. 이 애니메이션 세그먼트 재생에 걸리는 시간입니다. 각 애니메이션 세그먼트가 2초씩 재생되도록 이 값을2.0으로 설정합니다.
Verse# The duration of the animation segment. @editable: ToolTip := AnimationDurationTip AnimationDuration:float = 2.0EasingFunction으로 명명된 편집 가능한 이징 함수 옵션을 추가합니다. 이 애니메이션 세그먼트에서 사용되는 이징 함수입니다.
Verse# The easing function to use during this segment of animation. @editable: ToolTip := EasingFunctionTip EasingFunction:?easing_function = false각 이징 함수는 큐빅 베지어 커브에 의해 정의되는데, 이는 애니메이션에서 사용되는 이징 함수의 타입을 생성하는 네 가지 숫자로 구성됩니다. 예를 들면 이즈 인 커브의 파라미터는 애니메이션이 시작할 때 속도를 늦추고 그 후에는 속도를 높입니다.
선형 커브의 파라미터는 애니메이션이 일정한 속도로 재생되도록 합니다. 이러한 값을 직접 정의하여 나만의 커스텀 애니메이션 커브를 만들 수 있지만, 이 예시에서는
KeyframedMovement모듈에 정의된 값을 사용하므로 직접 정의하지 않아도 됩니다.PauseSeconds로 명명된 편집 가능한 float 옵션을 추가합니다. 이 애니메이션 세그먼트를 시작하기 전에 일시정지할 초 단위 시간입니다. 엔티티가 경로상의 각 지점에서 이동하기 전에 일시정지하는 시간으로 생각해도 좋습니다.
Verse# The number of seconds to pause before starting this animation segment. @editable: ToolTip := PauseTip PauseSeconds:?float = falseanimate_to_targets_component클래스 정의로 돌아와 다음 필드를 추가합니다.Segments로 명명된
segment의 편집 가능한 배열을 추가합니다. 엔티티가 실행하는 전반적인 애니메이션을 구성하는 각 애니메이션 세그먼트를 참조합니다.
Verse# Segments of the keyframed movement animation. @editable: ToolTip := SegmentsTip var Segments<private>:[]segment = array{}DefaultEasingFunction으로 명명된 편집 가능한이징 함수를 추가합니다. 애니메이션 세그먼트가 이징 함수를 지정하지 않는 경우, 이를 기본값으로 사용합니다.ease_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{}코드를 저장하고 컴파일합니다. 그러면 에디터에서 animate_to_targets_component가 어태치된 엔티티에
Segments배열이 나타납니다.
코드로 키프레임 빌드하기
세그먼트 클래스가 완료되면 세그먼트가 정의하는 키프레임을 만들 차례입니다. 키프레임을 개별적으로 빌드하여 배열에 추가한 다음, 이 배열을 keyframed_movement_component로 전달합니다. 이를 위해서 약간의 수학적 연산이 필요한데, 이를 지금 정의해 보겠습니다.
수학 연산은 다양한 시나리오에서 유용하므로, 모든 Verse 컴포넌트에서 액세스할 수 있도록 해당 로직을 유틸리티 파일에 배치하면 도움이 됩니다. 엔티티 작업 시 참고할 수 있는 더 많은 Verse 모범 사례를 알아보려면 Verse를 사용하여 자신만의 컴포넌트 만들기를 참고하세요. 유틸리티 파일을 생성하려면 다음 단계를 따릅니다.
animate_to_targets.verse파일에Utilities로 명명한 새 모듈을 생성합니다. 이 모듈이 프로젝트 전반에서 사용할 공통 로직을 저장합니다.Verse# Module containing utility functions. Utilities<public> := module:VectorOnes로 명명한 새vector3타입 에일리어스를vector3를 만드는 Utilities 모듈에 추가합니다. 여기에서Left,Up,Forward는 모두1.0으로 설정됩니다. 나중에 이 벡터를 사용하면 몇 가지 수학 연산이 쉬워집니다. 따라서 타입 에일리어스를 정의해 두면vector3{Left := 1.0, Up := 1.0, Forward := 1.0}을 반복해서 쓰지 않아도 됩니다./Verse.org/SpatialMath와/UnrealEngine.com/Temporary/SpatialMath모듈을 둘 다 임포트했으므로, 이 타입 에일리어스가/Verse.org/SpatialMathvector3임을 명시해야 합니다. 두 모듈에 이에 대한 정의가 포함되어 있기 때문입니다.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}GetDeltaTransform()으로 명명된 새 함수를Utilities모듈에 추가합니다. 이 함수는 두 트랜스폼 간의 차이를 계산하여 델타를 반환합니다. 이 함수에<transacts>모디파이어를 추가하여 롤백되도록 할 수 있습니다. 엔티티 트랜스폼 간의 차를 계산할 것이므로/Verse.org/SpatialMath를 각transform에 대한 모듈로 지정합니다.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=GetDeltaTransform에서 새/Verse.org/SpatialMathtransform을 초기화합니다.Translation을 각 트랜스폼의 이동 간의 차로 설정합니다.Rotation을MakeComponentWiseDeltaRotation()호출의 결과로 설정합니다. 이 함수는/UnrealEngine.com/Temporary/SpatialMath모듈에 있으므로/Verse.org/SpatialMath회전에서/UnrealEngine.com/Temporary/SpatialMath회전으로 변환해야 합니다.FromRotation()함수를 사용하여 이를 수행할 수 있습니다.FromRotation()으로 회전을 변환한 후 각 트랜스폼의 회전을 전달하는MakeComponentWiseDeltaRotation()을 호출합니다. 그런 다음,FromRotation()을 다시 사용하여 이 함수 호출의 결과를/Verse.org/SpatialMath회전으로 다시 변환합니다. 마지막으로 스케일은 첫 번째와 두 번째 스케일의 차를 첫 번째 스케일로 나눈 뒤, 거기에VectorOnes를 더한 결과로 설정합니다. 이렇게 하면 애니메이션 적용 과정에서 엔티티가 올바르게 스케일 조절됩니다. 완성된GetDeltaTransform()함수는 다음과 같습니다.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)마지막으로,
TryGetvalueOrDefault()로 명명한 함수를Utilities모듈에 추가하고<transacts>모디파이어를 추가합니다. 이 함수는 일부 타입의option값과 동일한 타입의 기본값을 가져와 기본값 또는Value내부의 항목(존재하는 경우)을 반환합니다. 이는 클래스의 값이 실제로 초기화되는지 확인하고 싶을 때 유용하며, 초기화되지 않을 경우 어떤 값을 반환받을 수 있습니다.TryGetValueOrDefault()내부에서Value에 값이 포함되어 있는지 확인하고, 포함된 경우 해당 값을 반환합니다. 그 외에는Default를 반환합니다. 완성된Utilities모듈 및TryGetValurOrDefault()함수는 다음과 같습니다.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=
수학 연산이 정의되었으니 이제 코드로 키프레임을 빌드할 수 있습니다!
키프레임 생성 함수를 빌드하려면 다음 단계를 따릅니다.
ConstructKeyframe()으로 명명한 새 함수를animate_to_targets클래스 정의에 추가합니다. 이 함수는 소스 엔티티, 대상 엔티티, 선택적 이징 함수, 지속 시간을 가져오고,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=ConstructKeyframe()에서GetGlobalTransform()을 호출하여Source와Destination엔티티 모두의 트랜스폼을 먼저 구합니다.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()배열 하나를
keyframed_movement_delta멤버 하나로 초기화합니다.Transform을GetDeltaTransform()호출의 결과로 설정하여 소스 및 대상 트랜스폼을 전달하고,Duration과Easing을 이 함수에 전달된 값으로 설정합니다. 완성된ConstructKeyframe()함수는 다음과 같습니다.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
이 함수는 개별 키프레임을 빌드하지만, 전체 애니메이션을 만들려면 더 많은 로직이 필요합니다.
ConstructAndPlayAnimations()로 명명된 새 함수를animate_to_targets클래스 정의에 추가합니다. 이 함수는 세그먼트 배열과 애니메이션 재생 모드를 가져와 전체 애니메이션을 만들고 재생하는 데 사용합니다. 이 함수에<suspends>모디파이어를 추가하면 비동기적으로 실행할 수 있습니다.Verse# Construct and play an animation from an array of animation segments. ConstructAndPlayAnimations<private>(InSegments:[]segment, AnimationPlayback:keyframed_movement_playback_mode)<suspends>:void=ConstructAndPlayAnimations()에서ShouldBreakOut으로 명명된 새logic변수를 정의하고false로 초기화합니다. 키프레임이 있는 이동 재생 모드가 세 개인 점을 고려하여, 각각을 개별 처리해야 합니다. 핑퐁 루프 모드를 처리하려면loop표현식을 사용하여 애니메이션을 계속 만들어야 하지만, 원샷 모드의 경우 첫 번째 반복작업에서 루프를 중단해야 합니다. 애니메이션 재생 모드가 원샷 모드인지 확인하고, 이에 해당하는 경우ShouldBreakOut을 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 = true다음으로,
if표현식에서 변수KeyframedMovementComponent에 있는 엔티티의keyframed_movement_component를 구합니다. 그런 다음,StartingTransform으로 명명된 변수에서InSegments배열의 첫 번째 엘리먼트를 구해 애니메이션의 시작 트랜스폼을 구한 다음 글로벌 트랜스폼을 구합니다.Verse# Position this entity in the correct starting position. if: KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component] StartingTransform := FirstSegment := InSegments[0].SegmentStartPosition?.GetGlobalTransform()마지막으로, 글로벌 트랜스폼을 시작 트랜스폼으로 설정하여 엔티티를 시작 위치에 배치하고 애니메이션이 재생되기 전에
InitialPauseSeconds동안 슬립합니다.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)이제 애니메이션을 빌드할 키프레임의 배열을 빌드할 차례입니다. 먼저,
keyframed_movement_delta로 이루어진 새 변수 배열Keyframes를 초기화합니다. 다음으로,for표현식에서InSegments배열의 각 세그먼트를 반복작업하여Index라는 이름의 로컬 변수에서 세그먼트와 인덱스를 모두 가져옵니다.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?이제
Segment.EasingFunction과DefaultEasingFunction을 전달하는TryGetValueOrDefault()를 호출하여Easing으로 명명된 로컬 변수에서 이 키프레임에 사용된 이징 함수를 구합니다. 또한Segment.AnimationDuration의 로컬 변수Duration에서 애니메이션 세그먼트의 지속 시간을 구합니다. 모든 값이 준비되었으면if표현식에서 각 값을ConstructKeyframe[]에 전달하여 키프레임을 구성하고 결과를Keyframes배열에 추가합니다. 키프레임이 모두 만들어지면Keyframes배열과PlaybackMode를 전달하는SetKeyframes()를 호출하여 키프레임이 지정된 이동 컴포넌트에서 키프레임의 배열을 설정합니다.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.키프레임 배열이 설정되었으면 이제 자유롭게 사용해 볼 차례입니다. 애니메이션은 루프로 실행해야 하지만 애니메이션 모드가 원샷으로 설정된 경우 첫 번째 반복 후 멈춰야 합니다. 또한 세그먼트에
PauseSeconds가 있는 경우 각 키프레임에서 일시정지를 처리해야 합니다. 이를 위해for표현식이 안에 있는loop표현식을 구성합니다.for표현식에서Keyframes배열에 있는 각 키프레임을 반복작업하여 변수KeyframeIndex의 각 키프레임 인덱스를 추가로 구합니다.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):for표현식 내에서KeyframeIndex를 사용하여InSegments배열로 인덱싱함으로써 이 키프레임과 연결된 세그먼트를 구합니다. 그런 다음, 세그먼트에PauseSeconds가 있으면 해당 시간 동안Sleep()을 호출합니다. 이후KeyframedMovementComponent.Play()호출하고KeyframeReachedEvent를 대기한 후KeyframedMovementComponent.Pause()를 호출합니다. 이렇게 하면 다음 키프레임을 재생하고 대기했다가 다시 일시정지하기까지PauseSeconds시간 동안 각 키프레임에서 애니메이션이 일시정지됩니다. 마지막으로,loop표현식 끝에서ShouldBreakOut이 true인지 확인하고 true인 경우 루프를 중단합니다.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()완성된
ContstructAndPlayAnimations()함수는 다음과 같습니다.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.
다음 단계에서는 애니메이션이 적용된 엔티티의 프리팹을 만들고 이를 레벨에서 인스턴스화합니다.