[UE5] 추출 슈터 2-6. 애니메이션 시스템
카테고리: Portfolio
🚨 완성된 포스트가 아니므로, 지속적으로 수정됩니다!
👾 깃허브
📋 기획
개요
이 포스팅에서 다루는 것:
- Lyra 애니메이션을 MetaHuman 스켈레톤에 리타게팅하는 방법
- Lyra 스타일 Linked Anim Layer 구조로 무기별 애니메이션 교체
- FABRIK 왼손 IK + 크로스헤어 HUD
왜 이 구조인가:
- Lyra: Epic 공식 샘플, 라이선스 문제 없음, 퀄리티 높은 Rifle/Pistol/Shotgun/UnArmed 애니메이션 보유
- Linked Anim Layer: 무기 추가 시 새 무기 AnimBP만 만들면 됨. 메인 AnimBP 수정 불필요
구현 내용
1. Lyra 애니메이션 리타게팅
IK Retargeter 2단계:
- 메타휴먼 IK Rig 생성
- 메타휴먼의 스켈레탈 메시를 통해 IK Rig를 생성했다.
- 이후, 리타깃 체인을 자동 생성해주었다.
- IK Retargeter
- 기존 Lyra 프로젝트에 존재하던, RTG_Mannequin을 사용했다.
- 해당 리타기터를 통해, 애니메이션 시퀀스들을 메타휴먼에 맞게 익스포트를 진행하였다.
BlendSpace / AimOffset
- AimOffset
- AimOffset의 경우는 리타기터에서 뜨지 않았다.
에셋 우클릭 → 리타깃 애니메이션을 통해 리타깃을 진행하였다.
- BlendSpace
- 현재로서는 간단하게 구현하기 위해 블렌드 스페이스를 이용하였다.
- 이후, Lyra와 동일하게 진행할 것이다.
2. Linked Anim Layer 전체 구조
┌─────────────────────────────────────────────┐
│ ABP_EPCharacter (메인 AnimBP) │
│ Parent C++: UEPAnimInstance │
│ Implements: ALI_EPWeapon │
│ │
│ ┌─ Locomotion StateMachine ─────────────┐ │
│ │ IdleWalkJog ←→ Crouch │ │
│ │ ↕ JumpSources / Jump │ │
│ └───────────────────────────────────────┘ │
│ │
│ ┌─ Linked Anim Layer 슬롯 (빈 슬롯) ───┐ │
│ │ FullBody_IdleWalkRun ← 빈 슬롯 │ │
│ │ FullBody_Crouch ← 빈 슬롯 │ │
│ │ FullBody_Jump ← 빈 슬롯 │ │
│ └──────────────────────────────────────┘ │
│ │
│ AimOffset: AimPitch / AimYaw │
│ AdditiveHitReact 슬롯 (HitReact 몽타주) │
└─────────────────────────────────────────────┘
↑ LinkAnimClassLayers()
┌─────────────────────────────────────────────┐
│ ABP_EPWeaponAnimLayersBase │
│ Parent C++: UEPWeaponAnimInstance │
│ Implements: ALI_EPWeapon │
│ │
│ 각 레이어 함수에 Sequence/BS Player 노드 │
│ → 임시적으로 각 레이어에 맞는 |
| 애니메이션 시퀀스 변수 할당 │
└─────────────────────────────────────────────┘
↑ 상속
┌─────────────────────────────────────────────┐
│ ABP_RifleAnimLayers (무기 AnimBP) │
│ │
│ AnimGraphOverrides (에셋 오버라이드 에디터) │
│ FullBody_IdleWalkRun → BS_IdleWalkJog │
│ FullBody_Crouch → Anim_Crouch │
│ FullBody_Jump → Anim_Jump │
└─────────────────────────────────────────────┘
3계층 설계의 핵심:
ABP_EPCharacter: 슬롯만 정의, 에셋 모름ABP_EPWeaponAnimLayersBase: 레이어 구현 구조 정의ABP_RifleAnimLayers: 실제 에셋만 꽂음- 새 무기 추가 시 이 파일만 새로 만들면 됨
무기 교체 한줄로 가능:
// OnRep_EquippedWeapon 함수
Owner->GetMesh()->LinkAnimClassLayers(NewWeapon->WeaponDef->WeaponAnimLayer);
3. Locomotion 상태 머신
상태 4개:
| State | 호출 레이어 | 전환 조건 |
|---|---|---|
IdleWalkJog |
FullBody_IdleWalkRun |
기본 상태 |
Crouch |
FullBody_Crouch |
bIsCrouching |
JumpSources |
— | 점프 입력 |
Jump |
FullBody_Jump |
공중 상태 |
- 레이어 내부 BlendSpace가 Speed로 Idle→Walk→Jog 블렌딩
4. AnimGraph 노드 흐름
[Locomotion State Machine]
↓
[Cache Pose: "Locomotion"]
↓
[Layered Blend Per Bone: spine_01] ← 상체에만 AimOffset 적용
Base: Cached Locomotion
Blend 0: AimOffset (AimPitch / AimYaw)
↓
[FABRIK: 왼손 IK]
↓
[AdditiveHitReact 슬롯] ← 피격 몽타주 (임시)
↓
[Output Pose]
- 현재는 임시적으로 배치
5. Property Access (Thread-Safe)
- ABP_RifleAnimLayers AnimGraph:
- Get Main Anim Blueprint Thread Safe
→ (ABP_EPCharacter로 캐스트)
→ .Speed → BlendSpace X축
→ .Direction → BlendSpace Y축
- Get Main Anim Blueprint Thread Safe
→ (ABP_EPCharacter로 캐스트)
- 중복 변수 선언 없이 워커 스레드 안전하게 값 복사 가능
6. FABRIK 왼손 IK
- 현재는 왼손만 FABRIK으로 무기 소켓 위치로 보정
NativeUpdateAnimation():
...
FTransform WorldLeftHandIK = WeaponMesh->GetSocketTransform(FName("LeftHandIK"));
FTransform HandR_World = Character->GetMesh()->GetBoneTransform(FName("hand_r"));
LeftHandIKTransform = WorldLeftHandIK.GetRelativeTransform(HandR_World);
...
- 팁 본은 폴리싱 단계에서 수정할 예정이다.
7. 크로스헤어 HUD
// EPPlayerController.cpp — IsLocalController() 체크 필수
// PlayerController는 서버에도 존재 → 서버에서 HUD 생성 방지
if (IsLocalController())
{
CrosshairWidget = CreateWidget<UEPCrosshairWidget>(this, CrosshairWidgetClass);
CrosshairWidget->AddToViewport();
}
- 손이 총 안으로 파고들어가 있는 이유:
- 팁 본이 왼손 중지로 설정됨
- 무기별로 오프셋 값이 설정되어야 하지만, 아직 없음
- 현재는 당장 수정할 필요가 없음
결과
- 이동 방향에 맞는 애니메이션
- 점프 애니메이션
- 웅크리기 애니메이션
- 상하 시야에 따라 상체(팔/총)만 AimOffset 적용
- 무기 장착 시 왼손이 LeftHandIK 소켓으로 이동
- 화면 중앙 크로스헤어 표시
댓글남기기