카테고리 없음

Unity6에서 AnimationClip.events 🔥 clip != null인데도 clip.events = null에서 예외가 나는 이유:

민트파워 2025. 4. 18. 11:33

🔥 clip != null인데도 clip.events = null에서 예외가 나는 이유:

Unity 6부터는 AnimationClip.events 프로퍼티의 setter가 더 엄격해졌습니다.

  • 읽기 전용 (ReadOnly) 상태의 AnimationClip에는 events를 설정할 수 없습니다.
  • 이는 다음과 같은 경우에 해당됩니다:
상태설명
🔒 AnimatorController에 바인딩된 AnimationClip Read-only 이기 때문에 .events 수정 불가
🔒 Resources 또는 Project에서 직접 참조한 Clip 런타임 수정 불가
✅ Instantiate()로 복사된 Clip 수정 가능

✅ 해결 방법 1: clip.events = null 코드 제거 또는 예외 방지 처리

🔸 방법 A: try-catch 방어

if (clip != null)
{
    try
    {
        clip.events = null;
    }
    catch (Exception e)
    {
        Debug.LogWarning($"clip.events = null failed on {clip.name} : {e.Message}");
    }
}

🔸 방법 B: 애초에 clip이 수정 가능한지 확인

csharp
복사
// Unity 6에서 ReadOnly일 가능성 있는 경우에는 clip을 복사해서 써야 함 if (clip != null && clip.events != null && clip.events.Length > 0) { // 이미 AddEvent를 통해 수정했는지 등 판단 }

 


✅ 해결 방법 2: 수정 가능한 AnimationClip을 사용하는 구조로 리팩토링

AnimatorController에서 가져온 clip은 수정할 수 없기 때문에, 실행 시점에 복사본을 생성해서 사용하는 방식을 써야 안전합니다.

csharp
복사
clip = Instantiate(anim[name].clip); // 복사본 생성 clip.name = $"{clip.name}_Instance"; anim.AddClip(clip, clip.name); // 애니메이션에 새 클립 추가

그 후에 이 clip에만 .events 설정을 수행하세요.


🧠 보너스: Unity 공식 문서에도 명시되어 있음

Unity 2023 이후 버전부터 일부 API는 런타임에서 리소스를 변경하려고 하면
NullReferenceException 또는 InvalidOperationException이 발생할 수 있습니다.
특히 AnimationClip.events는 내부적으로 C++ side가 null 상태일 경우 crash or exception 유발.


✅ 결론 정리

해결 방향설명
🔧 빠른 패치 clip.events = null; 코드를 try-catch로 감싸거나, 해당 줄 제거
🔒 Unity 6 대응 AnimationController에 종속된 clip은 수정 불가하므로, 복사본을 만들어서 .events 설정 필요
🧪 디버깅 확인 clip.name 출력해보고, anim.clip == clip 확인해보면 원본인지 판단 가능


AnimationClip을 복사해서 .events를 수정하면,
실제 AnimatorController에서 실행 중인 애니메이션이 아닌 복사본이 실행되는 것 아닌가?
→ 그럼 이벤트도 안 호출될 수 있는 것 아닌가?

👉 맞습니다. 그냥 복사해서 수정만 해서는 작동하지 않습니다.
복사한 Clip을 Animator 또는 Animation에 적용하지 않으면 실행되지 않습니다.


🔍 Unity 애니메이션 시스템 별 차이

시스템Clip 복사 후 적용 방식이벤트 적용 가능 여부비고
Animator (AnimatorController) 불가능 – 런타임 클립 교체 매우 복잡 ❌ .events 수정 불가 Unity의 메인 애니메이션 시스템
Animation (Legacy Animation) ✅ AddClip()으로 런타임 클립 교체 가능 ✅ 이벤트 설정 가능 Animation 컴포넌트 사용 시 가능

✅ Animation 컴포넌트일 경우 (지금 사용 중인 구조)

AnimationEndListener에서는 Animation 컴포넌트를 사용하고 있죠:

csharp
복사
[SerializeField] Animation anim = null;

즉, Legacy Animation 시스템을 쓰고 있으므로, 복사한 AnimationClip을 anim에 등록해 사용하면 전혀 문제 없습니다.

🔸 안전하게 작동하는 코드 예시

csharp
복사
var original = anim[name].clip; var cloned = Instantiate(original); cloned.name = $"{original.name}_Instance"; // 이벤트 추가 AnimationEventUtil.SafeAddEvent(cloned, "OnEnd", cloned.length); // 기존 이름으로 다시 등록해서 재생 가능하게 함 anim.AddClip(cloned, cloned.name); anim.Play(cloned.name);

✅ 이렇게 하면 복사본에 이벤트 등록 가능,
✅ Animation에서 해당 복사본이 실제로 실행되므로 OnEnd 이벤트 호출됨
✅ 기존 구조 그대로 유지 가능


❗ 단, Animator (Mecanim) 시스템이라면?

AnimatorController에서 사용하는 Animator는:

  • 클립을 runtime에 교체하거나 이벤트를 추가하는 건 거의 불가능에 가까움
  • AnimatorOverrideController로 대체하거나
  • 타임라인/애니메이션 상태 종료 체크 등 다른 방식으로 이벤트 처리 필요

✅ 결론

질문답변
복사한 Clip에 이벤트를 추가하면? 그 복사본이 실행되지 않으면 아무 효과 없음
Animation 컴포넌트 사용 중이라면? 복사 후 AddClip() 및 Play()로 실행하면 문제 없음
Animator 사용 중이라면? 런타임 클립 교체는 어렵고 다른 방식 추천됨

🎁 참고로: AnimatorController 쓰는 경우 대안

  1. AnimatorStateBehaviour.OnStateExit 사용해서 애니메이션 종료 감지
  2. AnimationClip.length + Invoke() 로 타이머 기반 처리
  3. AnimatorOverrideController 사용해 런타임에 클립 교체
  4. Playable API로 완전 커스텀 애니메이션 제어 (복잡함)

혹시 Animator와 Animation이 혼용되었거나 향후 Animator로 전환 계획이 있으시다면,
같은 이벤트 처리를 Animator 스타일로 바꾸는 방법도 함께 제안드릴 수 있어요!