ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [UE5] C++로 애니메이션 시퀀스 변경, 커브 추가
    언리얼엔진/그 외 2024. 7. 3. 16:12

     

    가끔씩 애니메이션을 수정해야하거나 커브를 추가해야할 상황이 있는데 반복작업이 너무 많거나 정확한 계산이 필요한 경우가 있을때 사용하면 좋을듯함.

    애니메이션 트랜스폼 정보를 커브로 변환해서 다른곳에 사용할수도 있을듯.

    카메라에 있는 애니메이션을 추출해서 커브로 만든뒤에 카메라 쉐이크로 사용한다던가.

    아니면 커브를 애니메이션에 베이크하는 경우도 있을듯.

     

     

    1. 커브 추가

    애니메이션 시퀀스에는 커브를 추가할수 있음.

    벡터 커브를 통해 본에 애디티브 트랜스폼을 추가해서 애니메이션을 수정하는것도 가능함.

    // 애니메이션 시퀀스
    UAnimSequence* AnimSeq = Cast<UAnimSequence>(Object);
    
    // 애니메이션 데이터에 접근가능한 모델
    IAnimationDataModel* AnimDataModel = AnimSeq->GetDataModel();
    // 애니메이션 데이터를 수정할수 있는 컨트롤러
    IAnimationDataController& AnimDataController = AnimSeq->GetController();
    
    // 추가할 커브 Identifier. RCT_Float 커브와 RCT_Vector 커브를 지정할수도 있음.
    // 본의 애디티브로 추가하려는 경우엔 커브 이름을 본의 이름으로.
    FAnimationCurveIdentifier CurveId(BONE_NAME, ERawCurveTrackTypes::RCT_Transform);
    // 애니메이션 시퀀스에 커브 추가
    AnimDataController.AddCurve(CurveId);
    
    // 애니메이션 시퀀스의 키 개수
    int32 KeyNum = AnimDataModel->GetNumberOfKeys();
    // 커브의 x축은 시간이므로 시간 계산에 사용할 인터벌.
    double Interval = AnimDataModel->GetFrameRate().AsInterval();
    
    TArray<FTransform> Transforms;
    TArray<float> Times;
    float CurrentTime = 0.f;
    for (int32 i = 0; i < KeyNum; i++)
    {
        Transforms.Add(FTransform()); // 커브에 추가할 값 지정
        Times.Add(CurrentTime);
        CurrentTime += Interval;
    }
    
    // 애니메이션 시퀀스에 커브 추가
    AnimDataController.SetTransformCurveKeys(CurveId, Transforms, Times);
    
    AnimSeq->PostEditChange();
    AnimSeq->MarkPackageDirty();

     

     

     

    2. 애니메이션 수정

    기존 애니메이션을 가져와서 수정할 수도 있음.

    언리얼 5.4 기준으로는 이전 버전에 폐기된 함수들이 아직 많이 남아있으므로 제대로 알아보고 사용하는게 좋음.

    폐기된 함수는 작동하지 않음.

    // 애니메이션 시퀀스
    UAnimSequence* AnimSeq = Cast<UAnimSequence>(Object);
    
    // 애니메이션 데이터에 접근가능한 모델
    IAnimationDataModel* AnimDataModel = AnimSeq->GetDataModel();
    // 애니메이션 데이터를 수정할수 있는 컨트롤러
    IAnimationDataController& AnimDataController = AnimSeq->GetController();
    
    // 스켈레톤 레퍼런스 포즈 정보. 본 포즈와 Info를 가져올때 Raw가 붙지 않은 함수는 가상 본 정보도 포함.
    // deform 본을 수정하는 경우에는 레퍼런스 포즈를 기준으로 작업하는게 좋음
    USkeleton* Skeleton = AnimSeq->GetSkeleton();
    const TArray<FTransform>& RefBonePose = Skeleton->GetReferenceSkeleton().GetRawRefBonePose();
    const TArray<FMeshBoneInfo>& RefBoneInfo = Skeleton->GetReferenceSkeleton().GetRawRefBoneInfo();
    const int32 BoneNum = RefBonePose.Num();
    
    // 본 하나씩 작업
    for (int32 i = 0; i < BoneNum; i++)
    {
        const FName& BoneName = RefBoneInfo[i].Name;
        const FTransform& RefBoneTransform = RefBonePose[i];
    
        // 특정 본의 애니메이션 정보를 가져와서 수정할 수도 있음
        TArray<FTransform> BoneTrack;
        AnimDataModel->GetBoneTrackTransforms(BoneName, BoneTrack);
    
        TArray<FVector> PositionalKeys;
        TArray<FQuat> RotationalKeys;
        TArray<FVector> ScalingKeys;
    
        // 기존의 애니메이션 트랙을 대체할 정보들 추가
        for (const FTransform& Transform : BoneTrack)
        {
            PositionalKeys.Add();
            RotationalKeys.Add();
            ScalingKeys.Add();
        }
    
        // 본의 애니메이션 트랙 지정
        AnimDataController.SetBoneTrackKeys(BoneName, PositionalKeys, RotationalKeys, ScalingKeys
    }
    
    AnimSeq->PostEditChange();
    AnimSeq->MarkPackageDirty();

     

     

     

    3. 스페이스 변환

    UKismetMathLibrary::ComposeTransforms 함수 또는 FTransform의 * operator 사용.

    ChildTransformInOuterParentSpace = ChildLocalTransform * ParentTransform

    // KismetMathLibrary.h
    
    /**
     * Compose two transforms in order: A * B.
     *
     * Order matters when composing transforms:
     * A * B will yield a transform that logically first applies A then B to any subsequent transformation.
     *
     * Example: LocalToWorld = ComposeTransforms(DeltaRotation, LocalToWorld) will change rotation in local space by DeltaRotation.
     * Example: LocalToWorld = ComposeTransforms(LocalToWorld, DeltaRotation) will change rotation in world space by DeltaRotation.
     *
     * @return New transform: A * B
     */
    UFUNCTION(BlueprintPure, meta=(ScriptMethod = "Multiply", ScriptOperator = "*;*=", CompactNodeTitle = "*", Keywords="multiply *"), Category="Math|Transform")
    static ENGINE_API FTransform ComposeTransforms(const FTransform& A, const FTransform& B);
    
    
    
    // KismetMathLibrary.cpp
    
    KISMET_MATH_INLINE
    FTransform UKismetMathLibrary::ComposeTransforms(const FTransform& A, const FTransform& B)
    {
        return A * B;
    }

     

Designed by Tistory.