게임에서는 애니메이션이 실행되는 도중에 적절한 타이밍에 맞춰서 무언가가 실행되어야 하는 경우가 종종 발생한다. 예를 들자면 캐릭터가 이동할 때 발 동작에 맞춰서 발소리가 나거나 먼지가 일어나야 하는 경우, 캐릭터가 무기를 휘두를 때 타이밍에 맞춰서 대미지가 들어가야 하는 경우등이 있을 수 있다.
이때 발 움직임과 다르게 소리가 나거나 공격하는 동작과 다르게 대미지가 들어간다면 플레이어는 굉장히 거슬리는 느낌을 받게 될 것이다.
이런 애니메이션과 리액션의 타이밍을 맞추기 위해서 존재하는 기능이 바로 유니티의 애니메이션 이벤트이다. 이 기능은 애니메이션 실행 도중에, 원하는 시점에 원하는 함수를 호출할 수 있게 해준다.
애니메이션 이벤트를 사용하는 방법은 FBX 모델과 함께 임포트된 애니메이션에서 사용하는 방법과 애니메이션 클립에서 사용하는 방법, 이렇게 2가지가 있다.
방법 1 : FBX 파일에서 모델과 함께 임포트된 애니메이션에서 애니메이션 이벤트 사용하기
프로젝트 뷰에서 임포트한 FBX 파일을 선택하면 인스펙터 뷰에 그 FBX 파일의 정보들이 보인다. 그 중에 상단의 Animations 탭을 선택하면 그 FBX에 포함된 애니메이션의 정보를 볼 수 있다.
애니메이션 이벤트 기능은 Events 항목에서 볼 수 있다.
접혀있는 Events 항목을 열면 이런 타임라인과 비활성화된 필드를 볼 수 있다.
애니메이션에 이벤트를 추가하기 위해서는 먼저 이벤트를 추가할 지점을 애니메이션에서 선택해야 하는데 인스펙터 뷰의 제일 아래를 보면 이렇게 지금 선택한 FBX 파일에 포함되어 있는 애니메이션을 미리보기로 재생해볼 수 있는 부분이 있다.
여기서 재생 버튼 옆의 타임라인을 클릭해보면 애니메이션의 특정 지점을 선택할 수 있게 된다.
이 타임라인에서 애니메이션 지점을 선택하면 위쪽의 전체 애니메이션 타임라인은 물론이고 Events의 타임라인도 함께 움직이는 것을 볼 수 있다.
애니메이션 중에 팔을 적절한 지점을 선택하고 Events 타임라인 앞에 있는 버튼을 클릭하면 그러면 Events 타임라인에 작은 표식이 생기며 아래 쪽에 있는 입력 가능한 필드들이 활성화되는 것을 볼 수 있다.
참고로 파란색 표식을 잡고 드래그하면 언제든지 이벤트가 실행될 시점을 조절할 수 있다.
필드 이름
내용
Function
이벤트 시점에 호출할 함수의 이름
Float
함수에 float형 매개변수가 있을 때 들어갈 값
Int
함수에 int형 매개변수가 있을 때 들어갈 값
String
함수에 string형 매개변수가 있을 때 들어갈 값
Object
함수에 Object형 매개변수가 있을 때 들어갈 값
필드에 입력되는 내용은 위와 같다.
참고로 애니메이션 이벤트가 함수를 호출하는 방식은 여기에 넣어준 함수 이름으로 애니메이터 컴포넌트가 붙어있는 게임 오브젝트에 부착되어 있는 모든 컴포넌트에서 같은 이름을 가진 함수를 찾아서 실행하는 것이다.
만약 게임 오브젝트에 붙어있는 A라는 컴포넌트와 B라는 컴포넌트가 둘 다 Attack이라는 함수를 가지고 있다면 이 두 함수가 모두 호출된다.
Attack 애니메이션이니 이벤트에서 호출할 함수의 이름은 Attack으로 설정하고 매개변수에는 스크립트에서 여기에 입력된 값들이 넘어오는 것을 체크하기 위해서 Float에는 3.14, Int에는 10, String에는 hello, Object에는 씬에서 큐브 게임 오브젝트를 하나 생성한 뒤 프리팹으로 만들어서 넣어주자.
그리고 Apply 버튼을 누르면 추가한 애니메이션 이벤트가 완전히 적용된다.
그리고 Attack 애니메이션을 재생할 애니메이터 컨트롤러를 만들어서 애니메이터 뷰를 열고 Attack 트리거를 만들어준 다음 Stand와 Attack 애니메이션을 스테이트로 넣어주고, Attack 트리거가 들어오면 Attack 애니메이션이 재생된 다음 Stand로 돌아가게 구성해준다.
그리고 BoxMan을 씬에 배치하여 애니메이터에 애니메이터 컨트롤러를 넣어준다.
using UnityEngine;
public class AttackComponent : MonoBehaviour
{
private void Attack()
{
Debug.Log("Damage");
}
}
애니메이션 이벤트가 실행할 함수를 만들어주기 위해서 AttackComponent C# 스크립트를 생성하고 코드를 작성한다.
애니메이션 이벤트는 앞에서 이야기한 것과 같이 애니메이션 이벤트가 포함된 애니메이터 컨트롤러를 가진 컴포넌트과 같은 게임 오브젝트에 부착된 컴포넌트 중에서 이벤트의 Function에 넣어준 이름과 같은 함수를 찾아서 실행해준다.
애니메이션의 기초에 대해서 설명하는 튜토리얼을 통해서 애니메이터 파라미터로 애니메이터 컨트롤러의 흐름을 조절할 수 있다는 것을 이야기했었다.
우선 애니메이터 컨트롤러의 파라미터와 트랜지션에 대해서 간단하게 복습해보자.
애니메이터 컨트롤러의 파라미터와 트랜지션
유니티 애니메이션에 대한 기초를 다루었던 글에서 이야기 했듯이 애니메이터 컨트롤러의 트랜지션은 스테이트와 스테이트 사이를 이어주고, 어느 방향으로 애니메이션이 흘러갈지 결졍하는 것이고, 파라미터는 이 트랜지션이 실행될 조건을 결정하는 변수이다.
애니메이터 컨트롤러
우선 간단한 애니메이터 컨트롤러를 만들기 위해서 씬에 게임 오브젝트를 하나 생성해보자.
그리고 추가한 게임 오브젝트에 애니메이터 컴포넌트를 부착해준다.
그 다음 프로젝트 뷰에 우클릭해서 [Create > Animator Controller] 항목을 선택하여 새 애니메이터 컨트롤러를 생성해서 게임 오브젝트에 부착된 Animator 컴포넌트의 Controller 프로퍼티에 할당해주면 된다.
Controller 프로퍼티를 할당한 다음에는 게임 오브젝트를 선택한 상태에서 상단 메뉴바의 [Window > Animation > Animator] 항목을 선택해서 애니메이터 뷰를 열어보면 비어있는 게임 오브젝트의 애니메이터 컨트롤러를 볼 수 있다.
애니메이션 파라미터
애니메이터 뷰를 열었다면 뷰 제목 바로 아래 있는 파라미터 탭을 클릭한 다음, 플러스 모양의 버튼을 눌러서 위 이미지처럼 각 파라미터들을 하나씩 만들어 보자.
이 파라미터들은 앞서 언급했듯이 한 애니메이션에서 다른 애니메이션으로 전환되는 트랜지션이 실행되는 조건의 역할을 한다. 파라미터의 종류는 여기서 볼 수 있듯이 Float은 소수점을 나타내는 실수, Int는 정수, Bool은 참/거짓을 표현하는 논리 변수, Trigget는 신호가 들어오면 트랜지션을 통과시킨 다음에 자동으로 꺼지는 타입의 변수다.
애니메이터 트랜지션
각 파라미터 타입을 테스트하기 위해서 위 이미지와 같이 애니메이터 컨트롤러를 세팅해보자. 스테이트를 모두 만들고 트랜지션을 연결했다면, 트랜지션의 조건을 설정할 차례다.
Float in
먼저 Idle 스테이트에서 Float 스테이트로 들어가는 트랜지션의 조건이다. 트랜지션 화살표를 클릭하면 인스펙터 뷰에서 해당 트랜지션을 수정할 수 있는데, 컨디션(Conditions)의 플러스 버튼을 누르면 이 트랜지션이 동작할 조건을 추가할 수 있다. 우선 조건으로 사용될 변수는 New Float으로 하고 3보다 클 때(Greater), 동작하도록 설정했다.
Float out
그 다음은 Float에서 Idle로 빠져나오는 트랜지션의 조건이다. 컨디션을 추가한 다음, 원래 Greater라고 적혀있는 드롭다운 메뉴를 눌러보면 Less가 있는 것을 볼 수 있다. Less는 정해진 숫자보다 작을 때를 뜻한다. 즉, 3보다 작을 때 Float에서 빠져나가서 Idle로 향하게 된다.
참고로, 여기에 왜 같다를 의미하는 Equal이 없는지 궁금할 수도 있다. 이것은 컴퓨터의 고질적인 부동소수점 오차라는 문제 때문이다. 예를 들어 현실적으로는 0.0001을 만 번 더하면 정확히 1이 되어야 하는데, 컴퓨터는 소수점 계산에 문제가 있어서 정확히 1이 되지 않고 1.00001이나 다른 숫자가 되는 경우가 종종 발생한다. 이 때문에 소수점 계산에 정확히 같다라는 것을 사용하기 어렵기 때문에 Equal이 빠져있는 것이다.
Int in
Idle에서 Int로 들어가는 조건을 설정해보자. 컨디션을 추가한 다음 New Float이라는 변수 이름이 적혀있는 드롭다운 메뉴를 선택하면 애니메이터 컨트롤러에 만들어져 있는 파라미터를 찾아서 선택할 수 있다. 그리고 Int형 파라미터는 Float과 다르게 "같다(Equal)"라는 조건을 사용할 수 있다. 파라미터의 값이 1과 같을 때 Idle에서 Int로 들어가게 설정해보자.
Int out
New Int의 값이 1이 아닐 때, Int에서 Idle로 빠져나오게 만든다.
Bool in
Idle에서 Bool로 들어가는 조건은 New Bool이 true일 때로 설정한다. Bool형 파라미터는 true 혹은 false만 설정할 수 있다.
Bool out
New Bool이 false가 되면 Bool에서 Idle로 빠져나오게 만들어 준다.
Trigger in
Idle에서 Trigger로 들어가는 조건은 New Trigger로 넣어준다. 트리거 형식은 아까 이야기했듯이 트리거가 호출되는 순간에 한 번 켜지고, 트리거 조건이 있는 트랜지션을 통과하면 자동으로 꺼지기 때문에 별다른 세부 조건이 없다.
Trigger out
Trigger에서 Idle로 빠져나오는 조건은 따로 만들지 않는다. 참고로 트랜지션을 선택한 다음 볼 수 있는 인스펙터 뷰의 내용 중에서 Has Exit Timer이라는 옵션이 있는데 이 옵션이 켜져있으면 트랜지션의 조건 만족되더라도 지금 실행하는 애니메이션을 끝까지 재생하기 전에는 다음 스테이트로 넘어가지 않는다. 반대로 이 옵션을 꺼두면 지금 실행되는 애니메이션이 아직 재생이 끝나지 않았더라도 트랜지션의 조건이 만족되면 그 애니메이션을 끝내고 곧바로 다음 스테이트로 넘어가게 된다.
테스트 해보기
애니메이터 컨트롤러 설정이 모두 끝났다면 이제 플레이 버튼을 누르고 게임을 실행해보자.
그리고 애니메이터 뷰에서 파라미터의 값을 하나씩 변경하면서 테스트 해보자. 파라미터의 값에 따라서 애니메이션의 흐름이 통제되는 것을 확인할 수 있다.
스크립트로 애니메이터 파라미터 변경하기
다른 섹션에서 트랜스폼 컴포넌트를 다루면서도 이야기 했지만, 이렇게 에디터에서 값을 바꾸는 방법은 실제 게임 내에선 사용할 수 없으며, 게임에서 이 애니메이터 파라미터의 값을 바꿔서 애니메이션의 흐름을 통제하기 위해서는 스크립트에서 이 애니메이터 컨트롤러의 파라미터를 바꿀 수 있어야 한다.
public class AnimatorParameterPractice : MonoBehaviour
{
private Animator animator;
private void Awake()
{
animator = GetComponent<Animator>();
}
}
우선 AnimatorParameterPractice라는 이름의 C# 스크립트를 하나 생성하고, animator 멤버 변수를 하나 만든 다음, 게임이 시작되자마자 애니메이터 컨트롤러를 가져올 수 있게 Awake() 함수에서 GetComponent() 함수를 사용해서 게임 오브젝트에 부착된 Animator 컴포넌트를 가져오자.
참고로 GetComponent() 함수를 사용하면 지금 이 컴포넌트가 부착되어 있는 게임 오브젝트에 부착된 다른 컴포넌트를 가져올 수 있다. 지금은 Animator 컴포넌트를 가져오기 위해서 뾰족 괄호 안에<> Animator 컴포넌트 클래스 이름을 넣었습니다.
애니메이터의 각 파라미터 값 변경 함수
애니메이터 클래스 내부에는 애니메이터 안에 설정한 파라미터 값을 변경할 수 있는 함수를 제공한다.
Float형 변경하기
animator.SetFloat("New Float", 3.1f);
애니메이터의 Float형 파라미터의 값을 변경하려면 SetFloat() 함수를 사용하면 된다. 첫 번째 매개변수로 변경하고자 하는 파라미터의 이름을 넣고, 두 번째 매개변수에 값을 넣어준다.
Int형 파라미터 값 변경하기
animator.SetInteger("New Int", 1);
Int형 파라미터의 값은 SetInteger() 함수로 변경할 수 있다.
Bool형 파라미터 값 변경하기
animator.SetBool("New Bool", true);
Bool형 파라미터 값은 SetBool() 함수로 변경할 수 있다.
Trigger형 파라미터 신호 주기
animator.SetTrigger("New Trigger");
Trigger형 파라미터는 SetTrigger() 함수를 사용하면 해당 파라미터의 신호가 켜진다.
키보드를 누르면 각 파라미터 값 바뀌게 하기
void Update()
{
if (Input.GetKeyDown(KeyCode.F))
{
animator.SetFloat("New Float", 3.1f);
}
if (Input.GetKeyUp(KeyCode.F))
{
animator.SetFloat("New Float", 2.9f);
}
if (Input.GetKeyDown(KeyCode.I))
{
animator.SetInteger("New Int"
, 1);
}
if (Input.GetKeyUp(KeyCode.I))
{
animator.SetInteger("New Int"
, 0);
}
if (Input.GetKeyDown(KeyCode.B))
{
animator.SetBool("New Bool"
,
true);
}
if (Input.GetKeyUp(KeyCode.B))
{
animator.SetBool("New Bool"
, false
);
}
if (Input.GetKeyDown(KeyCode.T))
{
animator.SetTrigger("New Trigger");
}
}
AnimatorParameterPractice 클래스의 업데이트 함수에 위와 같은 코드를 작성해서 키보드를 눌렀다 뗄 때, 각 파라미터 값이 바뀌도록 해보자.
코드를 모두 작성했으면 코드를 저장하고 에디터로 돌아가서 애니메이터 컨트롤러가 붙어있는 게임 오브젝트에 AnimatorParameterPractice 컴포넌트를 부착한 뒤, 애니메이터 뷰를 켜고 플레이 버튼을 누른다.
그리고 게임이 실행되면 지정한 키인 F, I, B T를 눌러보면 키보드를 눌렀다 뗄 때마다 파라미터의 값이 바뀌고 그에 따라 애니메이션의 흐름이 통제되는 것을 볼 수 있다.
[유니티 어필리에이트 프로그램]
아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.
게임 제작에서 일반적으로 버그라고 하면, 프로그래머의 전유물 되는 경우가 많다. 물론 대다수의 게임 버그는 프로그래머의 실수나 설계 미스로 인해서 발생하는 것이지만, 잘못된 리소스로 인해서 발생하는 경우도 꽤나 있다. 대표적인 리소스로 인한 버그를 예로 들자면 3D 모델을 3ds 맥스나 마야같은 3D 툴에서 작업할 때, 실수로 폴리곤을 뒤집어서 내보내는 것과 같은 것이 있다. 이렇게 폴리곤이 뒤집힌 모델은 게임 엔진에 올릴 경우, 해당 부위가 뻥 뚫려서 보이는 문제가 발생한다. 이렇게 눈에 바로 보이는 버그라면 다행이지만 눈에 보이지 않는 리소스로 인한 버그라면 찾아내기가 매우 어려울 것이다.
찾아내기 어려운 리소스 버그 중에 하나가 바로 잘못된 애니메이션 리소스로 인해서 발생하게 되는 버그이다. 단지 애니메이션이 이상하게 움직이는 버그라면 상관이 없지만, 애니메이션은 정상적으로 동작하지만 어느 순간부터인가 캐릭터의 로테이션이 돌아가있다던지 정상적인 위치를 조금씩 벗어나게 되는 문제가 발생한다면 어떨까? 프로그래머나 엔진 작업자는 우선 캐릭터의 로테이션이나 포지션에 관여하는 모든 코드를 검토해야할 것이다. 이상한 현상을 보일 수 있는 코드가 있는지 모든 부분을 확인해야 한다. 만약 규모가 있는 프로젝트라면 이 작업만으로도 엄청난 시간을 소모해야 하지만, 이 과정을 거친 이후에 코드에 문제가 없음을 확인했다면 어떨까? 만약 이러한 문제에 대한 경험이 없다면 큰 혼란에 빠질 것이다.
나는 최근에 이렇게 코드에 문제가 없음에도 불구하고 캐릭터가 회전하는 문제와 원래 위치를 조금씩 벗어나서 움직이는 버그를 모두 겪어본 적이 있다. 코드를 검토했음에도 불구하고 코드에는 문제가 없음을 확인했지만 여전히 캐릭터가 이상한 회전이나 이동을 하는 문제가 발생했는데, 그때가 되어서야 나는 코드가 아닌 리소스를 의심해보기로 했다. 우선은 어떤 상황에서 이런 문제가 발생하는지 명확하게 확인해야 했다. 여러 번 테스트해본 결과 캐릭터의 로테이션이 이상한 각도로 변하는 문제는 캐릭터가 사망하고 난 이후에, 오브젝트 풀링으로 재활용해서 가져왔을 때 발생했고, 캐릭터가 원래 위치를 벗어나는 문제는 캐릭터가 공격 모션을 취한 다음에 조금씩 이동하는 것을 확인할 수 있었다. 그렇게 해서 우선 문제가 발생하는 원인이 사망 애니메이션과 공격 애니메이션이라는 것을 알 수 있었고, 그 이후에는 유니티에서 캐릭터의 본들의 트랜스폼을 분석해서 특정한 본이 애니메이션 이후에 원래 상태로 돌아가지 않고 이상한 각도를 유지하거나 이상한 위치를 유지하는 것을 확인할 수 있었다. 이러한 문제를 애니메이터에게 알려주고 수정된 애니메이션 리소스를 받아서 적용한 이 후에는 이 버그는 해결되었다.
버그는 코드에만 있지 않다. 스크립트에 캐릭터의 회전이나 이동에 이산한 동작을 하는 코드가 없음에도 불구하고 이상한 회전을 보인다거나 원래 위치를 조금씩 벗어나서 이동하는 버그를 보게 된다면 애니메이션 리소스의 버그를 의심해보라.
[유니티 어필리에이트 프로그램]
아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.
애니메이션은 게임에 생동감을 불어넣어 준다. 그런 애니메이션은 2D와 3D 애니메이션으로 나누어 지는데 3D 모델에 본을 심어서 움직이는 3D 애니메이션과 달리 2D 애니메이션은 고전적으로 시퀀스 이미지를 사용해왔다(최근 들어서는 2D 이미지를 부위 별로 나누고 거기에 본을 심어서 움직이는 스파인이나 라이브 2D 같은 방식의 애니메이션도 사용된다).
시퀀스 이미지란 위의 이미지처럼 진행될 애니메이션을 이미지로 그려서 나열해 놓은 것을 의미한다. 일반적으로는 위의 이미지처럼 하나의 시퀀스 이미지를 뽑아서 가져온 뒤에 오프셋을 이동시키는 방법으로 시퀀스 애니메이션을 사용했지만 유니티에서는 시퀀스 이미지를 각 프레임별로 이미지를 잘라서 사용한다.
잘라서 가져온 스프라이트 이미지
유니티 엔진에서 스프라이트 시퀀스 애니메이션은 크게 2가지 용도로 사용이 가능하다. 하나는 2D게임을 제작할 때, 2D 오브젝트에 애니메이션을 넣는 것이고, 다른 하나는 UI에서 2D 이펙트를 주거나, 기존의 키 애니메이션이 아닌 방식으로 UI에 애니메이션을 넣고 싶을때 사용하는 것이다.
이 2D 시퀀스 애니메이션을 사용하는 방법은 매우 간단하다.
우선은 간단하게 시퀀스 애니메이션을 적용할 2D 스프라이트 오브젝트를 생성하자.
그리고 Hierarchy 창에서 생성한 스프라이트 오브젝트를 선택하고 애니메이션 창을 열면 해당 오브젝트에는 아직 애니메이션이 없음을 볼 수 있다. 여기서 Create 버튼을 누르면 새로운 애니메이션을 만들 수 있다.
새로운 애니메이션을 생성한 이후에 한 애니메이션에 사용될 시퀀스 이미지들을 애니메이션 창에 드래그&드롭하면 그 생성한 애니메이션 클립에 시퀀스 이미지들이 들어가면서 스프라이트 시퀀스 애니메이션이 완성된다. 참고로 1프레임당 시퀀스 이미지 1장이 들어가기 때문에 적당한 속도로 애니메이션을 출력하기 위해서는 애니메이션의 속도 조절을 해야할 것이다. 다음 이미지가 0.5배속으로 만든 스프라이트 애니메이션이 적용된 모습이다.
2D 스프라이트 시퀀스 애니메이션을 사용할 때,시퀀스 이미지의 프레임이 늘어나면 애니메이션이 부드러워 보이고 퀄리티가 높아지지만 그만큼 더 많은 리소스를 사용하게 된다는 점을 기억해야 한다.
또한 시퀀스 애니메이션 작업을 할 때는 애니메이션에 사용되는 시퀀스 이미지들을 한꺼번에 선택해야 하는 경우가 많은데, 만약 시퀀스 이미지들의 해상도가 높거나 이미지들의 개수가 너무 많은 경우, 이미지들을 선택하는 것만으로도 유니티 엔진이 버벅대는 사태가 발생할 수 있다.
유니티 엔진의 Project 뷰에서 다량 혹은 고해상도의 이미지를 선택해야하는 작업의 경우에는 Inspector 뷰 우측 상단의 자물쇠 표시를 눌러서 잠궈주고, 하단의 프리뷰 창을 내려주는 것이 많은 도움이 될 것이다.
[유니티 어필리에이트 프로그램]
아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.
이전까지 게임을 제작을 시작할 때, 5.3.5 버전의 유니티를 사용하다가, 변경된 파티클 시스템을 사용하기 위해 유니티를 5.6.0으로 업데이트를 진행했었다. 엔진을 업데이트하면서 변경점으로 인해서 프로젝트에 어느 정도 문제가 발생할 것이라고 생각은 하고 있었지만, 예상외로 별다른 문제없이 유니티 업데이트가 진행되었었다.
문제는 애니메이션 쪽에서 발생했다. 제작 중인 게임은 최적화를 위해서 오브젝트 풀링(Object pooling) 기법을 사용하고 있었는데, 이 기법을 간단하게 설명하자면 유니티 상에서 게임 오브젝트를 생성(Instantiate)하고 삭제(Destory)하는 과정에서 발생하는 부하를 줄이기 위해, 게임에 필요한 게임 오브젝트를 미리 생성해서 컨테이너에 저장해두고, 필요하면 게임 오브젝트의 활성화해서 사용하고 죽음이나 파괴 등으로 필요없어진 게임 오브젝트의 비활성화 시키는 방법으로 비활성화 시킨 오브젝트라도 같은 종류의 오브젝트가 필요하면 다시 활성화 시켜서 재활용할 수 있다. 이렇게 하면 빠르게 작동해야하는 게임 도중에 무거운 Instantiate() 함수의 호출이나 이후에 가비지 컬렉터(Gabage Collector) 호출로 이어지는 Destory() 함수의 호출을 최소화할 수 있다.
이 오브젝트 풀링 기법과 유니티가 버전업되면서 변경된 애니메이터 컨트롤러의 동작이 만나면서 문제가 발생했다. 이전에 사용하던 5.3.5의 애니메이터 컨트롤러의 경우에는 동작중이던 게임오브젝트를 비활성화 시키고 난 이후에 해당 오브젝트를 재사용하기 위해서 다시 활성화 시키면 애니메이터 컨트롤러의 상태가 초기로 자동으로 돌아갔다.
그렇기 때문에 애니메이터를 위의 그림과 같이 구성해두면 캐릭터가 사망하면서 Die 트리거가 작동해서 Die 애니메이션이 작동한 이후에 죽은 캐릭터의 게임 오브젝트를 비활성화 시키고, 나중에 이 오브젝트를 재사용하기 위해 다시 활성화 시키면 애니메이터 컨트롤러의 상태는 자동으로 Idle 상태로 돌아오는 방식으로 동작했었다.
하지만 유니티를 5.6.0으로 업데이트한 이후에는 게임 오브젝트를 비활성화 시켰다가 다시 활성화 시키면 애니메이터 컨트롤러의 상태가 처음으로 돌아오지 않고 비활성화 시키던 당시의 상태를 유지하는 것으로 애니메이터 컨트롤러의 동작이 변경되었다(5.3.5와 5.6.0 사이의 어느 버전에서 동작이 변경되었을 것이다. 정확한 릴리즈 노트를 찾아보지는 않아서 변경된 정확한 버전은 알지 못한다).
그렇기 때문에 죽은 캐릭터의 게임 오브젝트를 재활용하려고 게임 오브젝트를 다시 활성화 시키면 Die 애니메이션이 끝난 상태로 나타나는 버그가 발생하게 되었다.
이러한 문제해결하기 위해서는 애니메이터 컨트롤러에서 상태 노드들의 연결을 약간 변경해 주어야 했다.
애니메이션이 종료된 이후에 제일 처음의 상태로 돌아가야 한다면 위의 그림처럼 그 상태는 반드시 Exit 노드로 연결되어야 한다. 애니메이션의 상태를 Exit 노드와 연결해 주면 그 애니메이션이 끝난 이후에 Exit 노드로 이동한 후 다시 Entry로 돌아가서 제일 처음 애니메이션을 다시 출력한다. 하지만 Die 같은 애니메이션의 경우 아무런 제약 없이 Exit 노드와 연결해주면 캐릭터가 죽어서 넘어진 다음에 다시 벌떡 일어나는 불상사가 발생할 수도 있다. 그런 종류의 불상사를 막아야 하는 경우에는 Exit 노드로 가는 트랜지션(Transition)에 하나의 트리거를 추가해 준 다음, 다시 돌아갈 상황이 되었을 때, 스크립트에서 트리거를 호출해서 애니메이션 상태를 처음으로 돌릴 수 있다.
결론적으로 최신 버전의 유니티에서는 애니메이터 컨트롤러가 끊임없이 흘러가게 하기 위해서는 마지막 애니메이션을 절대로 고립된 상태로 두지말고 Exit 노드에 연결해 주어야 한다.
[유니티 어필리에이트 프로그램]
아래의 링크를 통해 에셋을 구매하시거나 유니티를 구독하시면 수익의 일부가 베르에게 수수료로 지급되어 채널의 운영에 도움이 됩니다.