언리얼 애니메이션 시스템 정리 (AnimInstance, State Machine, Montage, Notify)

언리얼 Animation 학습 노트. 강의 6.1~6.5 개념만. 애님 데이터 흐름, 스테이트 머신 정리, 노티파이 헷갈릴 때 다시 보기.

애니메이션 시스템 구조

  • 애님 인스턴스(AnimInstance): 스켈레탈 메시를 소유한 폰의 정보를 받아 애님 그래프가 참조할 데이터를 제공. C++/블루프린트 둘 다 가능
  • 애님 그래프(Anim Graph): 변수 값에 따라 애니메이션을 재생하는 영역. 스테이트 머신 제공. 블루프린트로만 구현

애님 인스턴스 데이터 흐름 (중요 설계 패턴)

폰 속도에 따라 애니메이션을 바꾸려면 폰 정보와 애님 인스턴스 속성을 매 프레임 연동해야 함. 두 방식:

  1. 폰의 Tick에서 애님 인스턴스 속성을 업데이트(폰이 화분에 물 줌)
  2. 애님 인스턴스의 Tick에서 폰 정보를 가져와 업데이트(화분이 스스로 물 줌)

객체지향에서는 2번이 권장됨. 1번으로 하면 폰에 코드가 너무 몰림. 애님 인스턴스의 책임은 애님 인스턴스가 가지는 게 깔끔.

함수 치트시트:

  • NativeInitializeAnimation(): 애님 인스턴스 속성 초기화. 여기서 TryGetPawnOwner()로 소유 폰을 얻어 캐스팅
  • NativeUpdateAnimation(DeltaSeconds): 애님 인스턴스의 Tick. 여기서 폰의 Velocity, Speed, IsFalling 등을 매 프레임 받아옴
  • 폰에서 애님 인스턴스 얻을 땐 GetAnimInstance(). 애님 인스턴스에서 폰 얻을 땐 TryGetPawnOwner()(Try-가 붙는 건 폰이 없을 수도 있어서)

함정: C++에서 정의한 속성을 ABP의 VARIABLES에서 보려면 My Blueprint의 톱니바퀴 > Show Inherited Variables 체크해야 보임.

걷기 구현과 흔한 함정

bShouldMove 판단: 속도가 0보다 크고(KINDA_SMALL_NUMBER 비교) 가속도가 0이 아닐 때. 둘 다 봐야 정지 직전 미끄러짐을 안 잡음.

가장 흔한 함정: 걷기 애니메이션이 중간에 멈춤 → 시퀀스 플레이어 노드의 Loop Animation 체크 안 해서 그럼. FallLoop 등 반복돼야 하는 애니메이션은 전부 Loop Animation 체크 필수.

개선: 단순 Blend Poses by bool 대신 블렌드 스페이스(속도에 따라 걷기↔뛰기 자연 전환)를 Anim Graph에 연결.

State Machine

스테이트(반복 수행 동작 단위) + 룰(전환 조건)로 구성. 머신이 돌면 룰에 따라 한 스테이트가 지정돼 반복 수행. 다른 엔진에서 트랜지션이라 부르는 걸 언리얼은 룰이라 함.

정리 기법(노드 많아질 때 가독성 유지):

  • Cached Pose: Locomotion 결과를 캐싱해 여러 곳에서 재사용. 애님 그래프를 MainState 같은 상위 스테이트 머신으로 정리
  • Promote To Shared / Use Shared: 중복된 룰 로직을 하나로 만들어 공유(ToJump 등). 같은 조건을 여러 전환에 쓸 때
  • State Alias: 여러 스테이트에서 같은 곳으로 가는 전환을 하나로. "Jump, FallLoop로부터 Land로" 같은 걸 ToLand 앨리어스 하나로 묶음

치트시트:

  • Automatic Rule Based on Sequence Player: true면 애니메이션 끝나면 자동으로 스테이트 전환(점프 후 자동으로 다음 상태)
  • Priority Order: 한 스테이트에서 나가는 룰이 여러 개일 때 우선순위. 숫자 낮추면 그 룰 우선순위가 낮아지고 나머지가 상대적으로 높아짐. Locomotion에서 Jump와 FallLoop 둘 다 갈 수 있을 때 이걸로 우선순위 정리
  • Split Struct Pin: Velocity 같은 구조체 핀을 X/Y/Z로 쪼개서 룰 조건에 사용

무기 장착 (습득 패턴)

복합 충돌: 스태틱 메시 모양 그대로 콜리전 쓰려면 Convex Decomposition 적용.

습득 구조: PickupComponent(StaticMeshComponent 상속)에서 OnComponentBeginOverlap에 함수 바인드 → 플레이어가 겹치면 OnPickUp 델리게이트 Broadcast → 무기 액터가 받아서 캐릭터 손 소켓에 부착. 한 번 줍고 나면 OnComponentBeginOverlap.RemoveAll로 중복 방지.

부착: 스켈레톤에 Add Socket으로 hand_rSocket 만들고, AttachToComponent로 메시의 그 소켓에 SnapToTarget.

정오표 함정: 줍고 나서 PickupComponent->SetCollisionEnabled(NoCollision)은 PIE 종료 시 경고 유발. 대신 SetActorEnableCollision(false) 사용.

AnimMontage

스테이트 머신을 확장하지 않고 특정 상황에 원하는 애니메이션을 재생하는 기능. 여러 클립 일부를 떼고 붙여 새 애니메이션 생성. 섹션 단위로 자르고 붙임.

구조:

  • 슬롯 그룹/슬롯을 따로 만들어(SXGroup.SXUpperBody) 몽타주에 지정
  • ABP의 Anim Graph에 Slot 노드를 적절한 위치에 배치해야 몽타주가 그 자리에서 재생됨. Slot Name을 몽타주 슬롯과 맞춰야 함
  • 재생: AnimInstance의 Montage_Play. 이미 재생 중인지 Montage_IsPlaying으로 확인 후

함정: Anim Graph에 Slot 노드를 안 넣거나 Slot Name이 안 맞으면 몽타주가 재생돼도 화면에 안 나옴.

Animation Notify

애니메이션 재생 중 원하는 타이밍에 함수를 호출하는 기능. 시퀀스/몽타주 둘 다 가능. 공격 시 특정 프레임에 충돌 검사를 거는 식으로 활용.

두 방식:

  1. 애님 인스턴스 멤버 함수: AnimNotify_노티파이명() 형태로 선언 + UFUNCTION() 필수(UHT가 찾을 수 있게). 노티파이 이름을 타임라인에서 그 이름으로 만들면 자동 호출. 보통 여기서 델리게이트 Broadcast → 캐릭터가 받음
  2. AnimNotify C++ 클래스: UAnimNotify 상속, Notify() override. MeshComp->GetOwner()로 캐릭터 얻어 직접 호출. 재사용성 좋아서 이쪽으로 교체하는 경우 많음

연속 공격 (콤보) 구조

핵심 아이디어: 각 공격을 몽타주 섹션(Attack01/02/03)으로 분리, 섹션 간 자동 연결을 Remove Link로 끊음(안 끊으면 입력 없이 다음 콤보로 넘어감). 일정 타이밍에 입력이 있었으면 다음 섹션으로 점프.

흐름:

  • 노티파이 트랙을 2번 추가해서, 입력 체크용 노티파이(CheckInput_Attack)를 각 섹션에 배치
  • 첫 공격이면 BeginAttack(몽타주 재생 + Montage_SetEndDelegate로 종료 시 EndAttack 바인드), 이미 콤보 중이면 입력 플래그만 true
  • 입력 체크 노티파이 시점에 플래그가 true면 콤보 카운트 올리고 Montage_JumpToSection으로 다음 섹션 점프
  • EndAttack에서 콤보 카운트/플래그 리셋, 무브먼트 모드 복구, 델리게이트 Unbind

함정 1: 입력 체크 노티파이가 섹션 끝에 너무 가까우면, 다음 섹션 실행 전에 OnMontageEnded가 먼저 실행될 수 있음. 노티파이를 섹션 마지막에 너무 붙이지 말 것. 마지막 공격엔 입력 체크 노티파이가 불필요.

함정 2: 판정용 노티파이는 Montage Tick Type을 Branching Point로 지정. 기본 Queued는 한 박자 씹힐 수 있음. 이펙트/사운드는 씹혀도 되니 Queued 무방하지만, 콤보 판정 같은 건 Branching Point 필수.

이동하면서 공격 (상하체 분리)

Layered blend per bone 노드 사용. Branch Filters에 Bone Name으로 spine_01 지정하면 그 본 기준 상체는 공격 몽타주, 하체는 이동 애니메이션을 동시에 재생. 이때 BeginAttack에서 MovementMode를 None으로 막던 코드를 제거해야 이동 가능.

핵심 관통 패턴: 이번 챕터도 "동작의 특정 시점"을 직접 타이밍 재지 않고 노티파이/델리게이트(Montage_SetEndDelegate, OnComponentBeginOverlap)로 잡아 다음 동작을 잇는다. 걷기/점프 상태 전환, 무기 습득, 콤보 판정 전부 같은 구조. 이전 Collision/AI 챕터에서 반복된 그 패턴.

+ Recent posts