유니티 기본기 : 애니메이터(Animator) 2편

애니메이터 기초

Supercent.official
Supercent Blog 슈퍼센트 블로그
15 min readSep 13, 2023

--

애니메이터(Animator)란?

애니메이터(Animator)는 유니티에서 제공하는 애니메이션 시스템 중 하나인 메카님(Mecanim)이라고 불리는 시스템의 핵심 컴포넌트입니다.

1편에서는 애니메이터를 사용하기에 앞서 애니메이션 클립을 생성하거나 수정하는 방법에 대해 알아보았습니다. 이어서 2편에서는 애니메이터 컨트롤러를 편집하는 방법에 대해서 알아보도록 하겠습니다.

애니메이터 컨트롤러란?

Animator View

애니메이터 컨트롤러는 특정 캐릭터나 게임 오브젝트의 애니메이션을 각각의 상태(State)로 정의하고 상태(State) 간의 전환(Transition)을 설정할 수 있는 상태 머신(State Machine)을 이용해 구축된 정보들을 저장하고 있는 유니티 에셋입니다. 애니메이터 뷰를 통해 상태 및 전환을 편집할 수 있으며 애니메이터 컴포넌트를 사용하기 위해서는 반드시 필요합니다.

애니메이터 컨트롤러를 편집하는 방법은?

애니메이터 컨트롤러를 편집하기 위해서는 애니메이터 뷰를 열어야 합니다. 애니메이터 뷰를 여는 방법은 다음과 같습니다.

  • 애니메이터 컨트롤러를 더블 클릭해서 열기
  • Window → Animation → Animator를 통해 열기

상태(State)란?

기본적으로 캐릭터는 항상 특정 종류의 액션을 취하고 있다고 가정합니다. 이러한 액션은 게임에 따라 다르겠지만 일반적으로는 대기, 걷기, 달리기, 구르기, 점프 등이 포함됩니다. 애니메이터 컨트롤러에서는 이 각각의 액션들을 실행 중일 때를 특정 “상태(State)”라고 정의합니다.

상태를 생성하는 방법은 애니메이션 클립을 애니메이터 뷰로 드래그 앤 드롭하거나, 애니메이터 뷰에서 마우스 우클릭을 통해 생성할 수 있습니다.

일반적으로, 상태(State)는 애니메이터 뷰에서 긴 사각형의 형태로 표시되며, 특정 상태(State)를 클릭하면 해당 상태(State)에 대한 옵션들을 인스펙터 뷰에서 확인할 수 있습니다.

  • Motion
    해당 상태(State)일 때 실행될 애니메이션 클립 혹은 블렌드 트리를 의미합니다.
  • Speed
    Motion이 실행될 때 적용될 속도 배율을 의미합니다.
  • Speed Multiplier
    float 타입의 Parameter를 활용해서 코드를 통해 특정 상태(State)의 Speed 배율을 조절할 수 있습니다.
  • Motion Time
    float 타입의 Parameter를 활용해서 코드를 통해 특정 상태(State)의 진행 시간을 제어할 수 있습니다.
  • Transitions
    해당 상태(State)가 가지는 모든 전환(Transition)을 출력합니다.
  • Solo — 해당 전환(Transition)을 제외한 목록의 모든 전환(Transition)을 Mute합니다.
  • Mute — 해당 전환(Transition)을 Mute합니다.

상태(State)의 옵션에 대해 더 자세히 알고 싶다면 여기를 클릭해 주세요.

기본 상태(Default State)란?

기본 상태(Default State)해당 애니메이터 컨트롤러가 활성화되면 가장 먼저 실행되는 상태(State)입니다. 최초로 추가된 상태(State)가 기본 상태(Default State)로 지정되며, 원하는 상태(State)를 기본 상태(Default State)로 지정하려면 해당 상태를 마우스 우클릭하여 선택할 수 있습니다.

모든 상태(Any State)란?

모든 상태(Any State)는 특별한 상태(State)로, 현재 어떤 상태(State)에 있든지 관계 없이 조건이 충족되면 즉시 전환(Transition)이 발생하도록 설정할 수 있는 전환(Transition)의 시작점이라고 할 수 있습니다. 이 상태는 애니메이터 컨트롤러를 생성할 때 자동으로 생성되며 삭제할 수 없습니다. 또한, 특정 상태(State)에서 “모든 상태(Any State)”로의 전환(Transition)은 불가능합니다.

모든 상태(Any State)를 사용하면 이전의 상태(State)를 고려할 필요 없이 편리하게 상태의 전환(Transition)을 할 수 있지만, 이로 인해 상태(State)의 관리가 힘들어질 수 있고 의도치 않은 상황에서도 전환(Transition)이 발생할 수 있으므로 주의해서 사용해야 합니다.

파라미터(Parameter)란?

파라미터(Parameter)는 애니메이터 컨트롤러의 전환(Transition) 조건이나 상태(State)의 값들을 동적으로 조정하기 위해 사용되는 변수들을 의미합니다. 이러한 파라미터는 애니메이터 뷰의 좌측 상단에 있는 탭을 클릭하여 확인할 수 있습니다.

파라미터(Parameter)가 제공하는 변수의 타입은 다음과 같습니다.

  • float
  • int
  • bool
  • trigger

float, int, bool은 흔히 프로그래밍에서 사용되는 기본 타입과 동일하게 작동합니다. trigger는 bool과 유사하게 작동하지만, 해당 파라미터를 통해서 전환(Transition)이 발생할 경우 자동으로 초기화되는 특별한 파라미터입니다.

void AnimatorTest()
{
anim.SetInteger("attack_type", 1);
anim.SetFloat("move_speed", 0.2f);
anim.SetBool("is_alive", true);
anim.SetTrigger("jump");

var attackType = anim.GetInteger("attack_type");
var moveSpeed = anim.GetFloat("move_speed");
anim.ResetTrigger("jump");
}

전환(Transition)이란?

전환(Transition)은 현재 상태(State)에서 다른 상태(State)로 상태가 전환되는 과정을 나타냅니다. 애니메이터 뷰에서는 상태(State)와 상태(State) 사이를 이어주는 화살표로 표현됩니다.

전환(Transition)을 설정하려면 시작 상태(State)에서 우클릭을 통해 다음 상태(State)로 연결할 상태를 선택할 수 있으며, 시작 상태(State)와 다음 상태(State)가 동일할 지라도 여러 개의 전환(Transition)을 추가할 수 있습니다.

또한, 특정 전환(Transition)을 클릭하면 해당 전환(Transition)과 동일한 방향을 가지는 모든 전환(Transition)을 확인할 수 있으며, 각 전환(Transition)에 대한 옵션은 인스펙터 뷰에서 확인할 수 있습니다.

  • Transitions
    선택된 전환(Transition)과 동일한 방향을 가진 모든 전환(Transition)을 나열한 목록입니다. 해당 목록의 아이템을 클릭하면 해당 전환에 대한 옵션을 확인할 수 있습니다.
  • Solo — 해당 전환(Transition)을 제외한 목록의 모든 전환(Transition)을 Mute합니다.
  • Mute — 해당 전환(Transition)을 Mute합니다.
  • Has Exit Time
    현재 상태(State)의 애니메이션이 Exit Time 값만큼 진행된 후에만 해당 전환(Transition)이 발생하도록 설정합니다. Exit Time은 정규화된 시간으로 표현됩니다. 예를 들어, 0은 0%, 0.75는 75%를 나타냅니다.
  • Fixed Duration
    전환이 완료되기 위해 필요한 시간을 설정할 수 있는 Transition Duration을 정규화된 시간(%) 또는 초(s)로 사용할 것인지를 설정합니다.
  • Transition Duration
    이 값은 Fixed Duration 설정에 따라 시간(s) 또는 정규화된 시간(%)을 기반으로 현재 상태(State)와 다음 상태(State) 간의 애니메이션 전환을 적절하게 블렌딩합니다. 값이 작으면 블렌딩 시간이 짧아져 애니메이션 전환 시 부자연스러울 수 있습니다.
  • Transition Offset
    다음 상태(State)의 애니메이션 시작 지점을 정규화된 시간을 기준으로 설정합니다. 예를 들어, 0은 0%, 0.35는 35%를 나타냅니다.

전환(Transition)의 옵션에 대해 더 자세히 알고 싶다면 여기를 클릭해 주세요.

전환(Transition)에는 이름을 설정할 수 있지만 기본적으로 이 값은 비어 있습니다. 이름이 비어 있는 경우, 해당 전환(Transition)의 이름은 “현재 상태(State) → 다음 상태(State)” 형식으로 자동으로 설정됩니다.

예제

여태까지 애니메이터 시스템을 사용하기 위해 필요한 기본 사항을 모두 살펴보았습니다. 이제는 마우스를 클릭할 때마다 3D 캐릭터가 3개의 다른 춤을 번갈아가며 추는 예제를 만들어 볼 것입니다.

public class AnimatorTest : MonoBehaviour
{
const int DANCE_COUNT = 3;

[SerializeField]
Animator _anim = null;

int _danceIndex = 0;

void Update()
{
if (!Input.GetMouseButtonDown(0))
return;

++_danceIndex;
if (DANCE_COUNT <= _danceIndex)
_danceIndex = 0;

_anim.SetInteger("dance", _danceIndex);
}
}

자주 묻는 질문

Q. 애니메이션 클립을 반복하는 방법은?

애니메이션 클립을 선택하면 여타 에셋과 마찬가지로 관련 설정을 인스펙터 뷰에서 확인할 수 있습니다. 해당 화면에서 Loop Time을 true로 변경하는 것으로 해당 클립을 반복 재생되는 상태로 설정할 수 있습니다.

Q. 파라미터 호출 시 최적화 방법은?

애니메이터의 파라미터 호출 시에는 파라미터 이름을 문자열(string)로 넘겨주어야 하지만, Unity는 내부적으로 이 문자열을 해싱을 통해 정수(int)로 변환하여 사용합니다. 관련 메서드는 공개되었기 때문에, 미리 해싱 값을 캐싱 해두고 최적화할 수도 있습니다.

// Before
void Play()
{
_anim.SetTrigger("play");
}

// After
static readonly int ANIM_PLAY = Animator.StringToHash("play");

void Play()
{
_anim.SetTrigger(ANIM_PLAY);
}

Q. 현재 재생 중인 상태(State)를 확인하는 방법은?

에디터의 애니메이터 뷰를 사용하면 특정 게임 오브젝트의 현재 상태를 쉽게 확인할 수 있지만, 이를 코드로 확인하려면 조금 복잡한 과정을 거쳐야 합니다.

bool Check(string stateName)
{
// 여기서 0은 Base Layer의 Index를 의미합니다.
// 레이어와 관련된 사항은 2편에서 다룰 예정입니다.
var currentInfo = _anim.GetCurrentAnimatorStateInfo(0);

// 방법 1
// 해당 상태(State)의 이름을 통해 확인합니다.
// 상태의 이름은 동일한 레이어라면 유일성을 보장받습니다.
if (currentInfo.IsName(stateName))
return true;

// 방법 2
// 해당 상태(State)의 이름을 해싱한 값을 통해 확인합니다.
// 상태(State)의 해싱 값을 미리 캐싱해두고 사용하는 것을 권장드립니다.
if (currentInfo.shortNameHash == Animator.StringToHash(stateName))
return true;

return false;
}

GetCurrentAnimatorStateInfo를 사용하여 반환된 AnimatorStateInfo는 struct 형식이기 때문에 메서드를 호출한 프레임에서만 현재 상태가 정확하게 보장되며, 이후의 프레임에서는 정확성이 보장되지 않으므로 검사할 때마다 매번 호출해야 합니다.

Q. 파라미터와 무관하게 특정 상태(State)를 실행시키는 방법은?

간혹 애니메이터에서 제공하는 상태 머신을 사용할 필요가 없거나 특별한 경우에 상태(State)와 전환(Transition)의 관계를 무시하고 특정 상태(State)를 실행시키고 싶은 경우가 있을 수 있습니다.

void PlayState(string stateName)
{
// 위의 예시들과 동일하게 해싱된 값을 파라미터로 넘길 수도 있습니다.
// 해당 상태(State)를 재생합니다.
_anim.Play(stateName);

// 0번 레이어의 해당 상태(State)를 재생합니다.
_anim.Play(stateName, 0);

// 0번 레이어의 해당 상태(State)의 20% 지점부터 재생합니다.
_anim.Play(stateName, 0, 0.2f);
}

Q. 런타임에 애니메이터 컨트롤러를 교체하는 방법은?

게임을 만들다 보면 여러가지 이유에 의해서 특정 캐릭터의 애니메이터 컨트롤러를 변경해야 할 때가 있을 수 있습니다.

void ChangeAnimSet(RuntimeAnimatorController animSet)
{
_anim.runtimeAnimatorController = animSet;
}

다만, 애니메이터 컨트롤러 교체 시 현재 애니메이터가 가지고 있던 상태(State)가 사라지기 때문에 의도치 않은 동작이 발생할 수 있다는 점을 유의하셔야 합니다. 예를 들어, 달리기 상태에서 애니메이터 컨트롤러를 교체하면 달리기 상태는 사라지고 교체된 애니메이터 컨트롤러의 초기 상태로 전환됩니다.

Q. 캐릭터에 동적으로 생성된 게임 오브젝트가 캐릭터의 애니메이터 영향을 받지 않을 때의 해결 방법은?

캐릭터 A가 이미 존재하고, 캐릭터 A는 다양한 무기를 사용하며 A의 애니메이션은 이러한 무기를 제어하는 기능을 가지고 있다고 가정해봅시다. 그런데 A의 애니메이터가 초기화될 때 해당 무기를 가지고 있지 않았다면, 이후 스크립트를 통해 동적으로 무기를 생성하더라도 A의 애니메이터는 해당 무기를 인식하지 못하는 경우가 발생할 수 있습니다.

이러한 문제는 애니메이터가 초기화될 때 애니메이터의 제어를 받을 수 있는 게임 오브젝트들의 정보를 수집하는 데, 이때 해당 무기가 존재하지 않았기 때문에 애니메이터가 이를 인지하지 못해서 생기는 문제입니다.

이는 다시 한번 애니메이터에게 수집을 요청하면 해결할 수 있습니다. 이렇게 하면 애니메이터는 현재 게임 오브젝트의 상태를 갱신하고 새로 생성된 무기를 인식하게 됩니다.

void Rebind()
{
_anim.Rebind();
}

주의해야 할 점은 해당 메서드 호출 시 애니메이터가 제어할 수 있는 모든 게임 오브젝트에 대한 정보를 수집하기 때문에 프레임 드랍이 발생할 수 있으며, 이로 인해 애니메이터의 상태가 초기화됩니다.

따라서 이 메서드를 호출할 때는 게임의 성능에 주의해야 하며, 애니메이터의 현재 상태가 중요한 경우에만 사용하는 것이 좋습니다. 또한 앞서 언급했듯이 초기화된 애니메이터는 처음부터 다시 상태를 진행하므로 의도하지 않은 동작이 발생할 수 있습니다.

끝맺음

지금까지 애니메이터가 무엇이며 어떤 식으로 사용할 수 있는지 대해 알아보았습니다.

3편에서는 애니메이터의 레이어(Layer)와 휴머노이드(Humanoid)에 대해서 다룰 예정입니다.

이 글을 통해 애니메이터를 처음 접하신 분들도, 기존에 사용하고 계셨던 분들도 도움이 되었으면 좋겠습니다.

<유니티 기본기 시리즈 전체 보기>

더 많은 하이퍼 캐주얼 게임 개발 팁을 알고 싶으신가요?
지금 바로
여기를 클릭해서, 슈퍼센트의 파트너가 되어 보세요!

[Contact]
- E-mail: help@supercent.io
- Homepage:
corp.supercent.io

--

--

Supercent.official
Supercent Blog 슈퍼센트 블로그

Insights and tips to take your hyper-casual game to the top of the charts