Animator Controller
Animator Controller는 Animation Clip을 관장하는 시각적 상태 머신입니다. Idle·Walk·Run·Jump·Attack의 전환과 블렌드를 Parameters·Transition으로 정하고, 역할 애니메이션 로직을 스크립트 하드컷 없이 다루는 실전 가이드입니다.
상상해 보세요...
Animator Controller는 캐릭터의 무대 감독입니다. 지금 어떤 상태인지(idle, run, jump)를 알고, 조건이 맞으면(점프 버튼, speed > 0) 다음 클립으로 부드럽게 전환합니다. State를 클립으로 두고 조건이 달린 「화살표」(Transition)를 그리면, 컨트롤러가 스스로 각 화살표를 발화합니다.
개념 자세히
Animator Controller는
(.controller)
에셋으로, 하나 이상의 Layers를 가지며 각 레이어는 독립 상태 머신입니다. Base
레이어는 보통 로코모션을, 상위 레이어는 상체를 덮어쓸 수 있습니다(달리면서 던지기). 각
레이어에는 Weight(0–1)와 Blending Mode
(Override/Additive)가 있습니다.
레이어 안에서 States(주황/초록 박스)는 Animation Clip이고,
Transitions(화살표)는 전환 시점을 정의합니다. 전환에는
Has Exit Time
(클립이 끝날 때까지 대기)과
Transition Duration
(블렌드 시간)이 있습니다. Parameters(Int, Float, Bool, Trigger)는 Animator가
전환을 고를 때 읽는 변수입니다.
Sub-State Machine은 복잡한 상태를 한 노드로 묶습니다 (예: 모든 공격 변형을 “Combat” 아래). Any State는 특별합니다——Any State에서의 전환은 현재 상태가 무엇이든 발화할 수 있어 Death나 Hit 인터럽트에 쓰입니다.
Unity 2022+에서는 Animation Rigging 패키지가 Animator 위에 IK를 더합니다: 전용 클립 없이 런타임 손/발 포즈(VR 컨트롤러, 클라이밍).
기본 캐릭터 상태 머신
실습 단계
Animator Controller 만들기
Project → Create → Animator Controller. 더블클릭해 Animator 창을 엽니다. Animation Clip을 끌어다 놓아 State를 만듭니다.
Parameters 추가
Animator 창 → Parameters 탭 (+) → Float "Speed", Bool "IsGrounded", Trigger "Jump"를 추가합니다. C#이 설정할 변수입니다.
조건이 있는 Transition 만들기
State 우클릭 → Make Transition → 대상을 클릭. Transition 선택 → Inspector → Conditions → (Speed > 0.1) 추가.
GameObject에 할당
Add Component → Animator → "Controller"에 Controller를 넣습니다. Avatar가 올바른지 확인합니다(Humanoid 설정).
스크립트로 구동
Update()에서 animator.SetFloat("Speed", velocity.magnitude). 문자열 대신 Animator.StringToHash()로 해시를 캐시하세요.
인터랙티브 시뮬레이터
상태를 클릭해 바로 이동하거나, 컨트롤 버튼으로 조건 전환을 발화해 보세요.
코드 예제
기본PlayerController에서 Animator 파라미터 구동——Speed와 Jump 트리거로 기본 로코모션.
using UnityEngine;
public class AnimatorDriver : MonoBehaviour
{
Animator anim;
// Cache hashes in Awake — faster than a string every frame
static readonly int SpeedHash = Animator.StringToHash("Speed");
static readonly int JumpHash = Animator.StringToHash("Jump");
static readonly int GroundHash = Animator.StringToHash("IsGrounded");
static readonly int DeathHash = Animator.StringToHash("Death");
void Awake() => anim = GetComponent<Animator>();
void Update()
{
Vector3 vel = GetComponent<Rigidbody>().velocity;
anim.SetFloat(SpeedHash, vel.magnitude, 0.1f, Time.deltaTime);
anim.SetBool(GroundHash, isGrounded);
if (Input.GetKeyDown(KeyCode.Space))
anim.SetTrigger(JumpHash); // Trigger auto-resets after 1 frame
}
public void Die()
{
anim.SetTrigger(DeathHash);
anim.SetLayerWeight(1, 0f); // Disable layer 1 on death
}
}코드 예제
고급PlayInFixedTime, CrossFade, 현재 상태 읽기——코드에서 세밀한 전환.
using UnityEngine;
public class AdvancedAnimator : MonoBehaviour
{
Animator anim;
static readonly int BaseLayer = 0;
void Awake() => anim = GetComponent<Animator>();
// CrossFade: switch to a named state with a custom blend duration
public void PlayAttack(string attackName)
{
anim.CrossFadeInFixedTime(attackName, 0.15f, BaseLayer);
}
// Is the character in a given state (name or tag)
bool IsInState(string stateName)
{
return anim.GetCurrentAnimatorStateInfo(BaseLayer)
.IsName(stateName);
}
// Scale animation playback from gameplay
public void SetAttackSpeed(float speedMultiplier)
{
anim.SetFloat("AttackSpeed", speedMultiplier);
}
// Read normalized time to know how far the clip has played
void Update()
{
var info = anim.GetCurrentAnimatorStateInfo(BaseLayer);
if (info.normalizedTime >= 0.9f && info.IsTag("Attack"))
{
Debug.Log("Attack almost done — combo window");
}
}
}📌 빠른 정리
- ▸Trigger는 1프레임 후 자동 리셋(Jump, Hit)
- ▸Animator.StringToHash() → Awake에서 캐시, 문자열 GC 회피
- ▸Any State → 현재 상태와 무관하게 인터럽트
- ▸SetFloat(hash, value, damping, dt)로 더 부드러운 블렌드
- ▸normalizedTime: 0 = 클립 시작, 1 = 클립 끝
⚠️ 흔한 실수
❌ Trigger가 동작하지 않음——상태가 바뀌지 않음
Has Exit Time이 true이고 클립이 끝나지 않아 트리거가 무시됨
✅ 언제든 발화해야 하는 전환은 Has Exit Time을 끕니다
❌ 애니메이션이 블렌드되지 않고 하드컷
Transition Duration = 0
✅ Transition Duration을 0.1–0.25초로 설정해 블렌드