애니메이션은 게임 오브젝트에게 움직임을 부여하는 기능이다. 이를 사용해 미리 저장되어 있는 움직임을 적용하거나, 움직임을 만들어 낼 수 있다.
유니티에서 애니메이션을 다루기 위해 다음의 구성요소가 활용된다.
애니메이션(Animation)
단일의 애니메이션을 적용하기 위한 컴포넌트
애니메이션 클립(Animation Clip)
객체의 움직임을 미리 정의한(저장한) 파일
애니메이션 컨트롤러(Animation Controller)
애니메이션 클립을 등록해 두고 객체의 상태에 따라 애니메이션들을 관리하고 재생하는 일종의 작업 지시서
애니메이터(Animator)
애니메이션 컨트롤러를 객체에 적용하기 위한 컴포넌트이다.
블렌더(Blender)
애니메이션간의 전환이 일어날 때 두 애니메이션 클립을 결합하여 자연스러운 움직임을 만드는 도구
이 각각의 구성요소에 관해 유니티에서 작업해보면서 알아보자.
애니메이션을 만들어 보기 위해 간략하게 플레이어를 만들어보았다.
캡슐 모양에 팔만 단 간단한 형태지만 이런 모양으로도 애니메이션을 만들 수 있다.
먼저 Idle 상태의 애니메이션을 만들기 위해 Player를 누른 상태에서 Animation을 Create로 만들어보자.
이와 같이 다양한 옵션을 사용할 수 있을 뿐만 아니라, 자식 오브젝트의 상태도 변경할 수 있다.
평상시에는 팔이 위아래로 오가는 모션을 만들어보자.
이와 같이 변경할 부분을 등록한 다음 녹화 버튼을 누른다. 녹화가 시작되면 바를 움직여서 원하는 움직임대로 애니메이션을 구성해주면 된다.
0.2초에 Y축 포지션이 -0.2 내려가게 세팅하였다. 이 애니메이션이 제대로 작동하는지 보자.
위와 같이 장면을 따로 이어주지 않아도 유니티에서 자연스럽게 애니메이션이 만들어준 것을 확인할 수 있다.
애니메이션은 이와 같이 단순히 캐릭터의 Position, Rotation 등의 요소를 변경하는 것뿐만 아니라, 색을 바꾸거나 충돌체 변형 등 다양한 요소를 변경할 수 있다.
우리는 애니메이션을 통해 플레이어의 평상시 행동은 물론, 플레이어의 이동과 공격, 심지어 죽는 모션까지 구현할 것이다. 이런 애니메이션은 만드는 부분 말고, 애니메이션을 관리하는 애니메이터를 살펴보자.
애니메이터에는 파라미터라는 기능이 있다. 파라미터는 Float, Int, Bool, Trigger 등이 있다.
이런 파라미터의 용도는, 여러 애니메이션이 있을 경우 조건에 따라 애니메이션을 변환하기 위한 변수로서 활용된다. 예를 들어, 지금과 같은 상황에서는 이동이 감지될 경우(Float) Walk 애니메이션으로 젼환되게 한다.
오차를 감안하여 속도가 0.05 보다 빠를 때 Walk로 전환되고, 0.05보다 느릴 때 Idle로 전환되게 했다.
이렇게 설정하고 파라미터를 통해 변화를 주면서 애니메이션을 재생해보면 정상 재생되는 것을 확인할 수 있다.
다만 파라미터는 혼자서 작동하지 않는다. 이를 작동하기 위해서는 스크립트를 통해서 변수값을 집어넣어줘야 한다.
이전에 배웠던 MVC 패턴을 통해서 우선 플레이어의 움직임을 구현해보자. 플레이어는 Rigidbody를 통해 이동시키는 방법을 사용했다.
using UnityEngine;
public class PlayerModel : MonoBehaviour
{
[SerializeField] float maxSpeed;
public float MaxSpeed { get { return maxSpeed; } set { maxSpeed = value; } }
[SerializeField] float acceleration;
public float Acceleration { get { return acceleration; } set { acceleration = value; } }
}
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] Rigidbody rigid;
[SerializeField] PlayerModel model;
[SerializeField] Animator animator;
void Update()
{
Move();
}
private void Move()
{
float xInput = Input.GetAxisRaw("Horizontal");
float zInput = Input.GetAxisRaw("Vertical");
Vector3 dir = new Vector3(xInput, 0, zInput);
if(dir.sqrMagnitude > 1)
{
dir = dir.normalized;
}
rigid.velocity = Vector3.MoveTowards(rigid.velocity, dir * model.MaxSpeed, model.Acceleration * Time.deltaTime);
if(dir != Vector3.zero)
{
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation(dir), 5 * Time.deltaTime);
}
// 애니메이터의 파라미터의 값을 설정하여 애니메이션을 반영
animator.SetFloat("MoveSpeed", rigid.velocity.magnitude);
}
}
이와 같이 설정한 후 테스트를 해 보자.
Has Exit Time은 체크되어 있을 경우 조건이 충족되어도 기존 애니메이션이 전부 출력된 후 다음 애니메이션으로 넘어가게 한다. 체크를 해제할 경우 조건만 만족하면 바로 다음 애니메이션으로 넘어간다.
애니메이션이 바뀌는 조건을 설정하는 것
모든 애니메이션 종류를 가리키며, 다른 조건들이 충족되기 이전에 Any State에서 다른 애니메이션으로 이동하는 조건이 충족될 시 우선 실행된다.
예시를 들자면, Die와 같이 죽는 애니메이션이 있다고 했을 때, 모든 동작 애니메이션은 결과적으로 Die와 연결될 것이다. 이때 수많은 애니메이션 클립을 직접 Die에 연결하는 것이 아닌, Any State를 통해 한 번에 연결할 수 있게 된다.
여기서 Any State를 사용할 때 유의해야 할 점이 있다.
만약 죽는 조건이 활성화되어 Die라는 단방향 애니메이션으로 이동했을 때, Any State에 연결된 저 화살표 또한 계속 조건이 충족되기에 돌아갈 것이다. 이를 방지하기 위해서 Can Transition To Self를 체크 해제해 준다.
이와 같은 경우를 생각해보자. 캐릭터에게 공격기가 있고, 공격1, 공격2, 공격3의 연속기가 있어 버튼을 누르는 수에 따라 1타, 2타, 3타까지 공격할 수 있다고 해 보자.
이와 같은 복잡한 양상이 만들어질 것이다. 여기서 이걸 좀 더 간편하게 만들기 위해 이 과정 자체를 폴더처럼 만들 수 있다.
이와 같이 생성해준 후에 애니메이션을 Substate Machine 안에 넣어주자.
이와 같이 복잡한 과정을 나눠서 정리할 수 있다.
지금까지 배워 본 애니메이션에 관한 것이 FSM 모델을 활용한 예시이다.
유한 상태머신(Finite State Machine)은 상태를 기반으로 동작을 제어하는 시스템을 구현하기 위한 모델 중 하나이다. 상태 변화에 따른 객체의 동작이 복잡하고, 여러 가지 조건에 대응해 상태를 변경해야 하는 경우 사용한다. 유니티에는 애니메이션 컨트롤러가 유한상태머신을 이용해 구현되어 있으며, 부여된 객체의 상태변화에 따른 애니메이션을 유동적으로 관리할 수 있다.
하나의 파라미터에서는 한 가지의 상태만 가질 수 있고, 다수의 파라미터를 사용해 모델의 움직임과 표현을 사용할 수도 있다.
위에서 애니메이션을 만드는 방법과 애니메이터를 다루는 방법을 알아보았다. 이제는 실제 모델에 애니메이션을 넣어보는 실습을 해 보고자 한다.
여기서 우리는 유니티에서 무료로 제공하는 Unity-Chan 에셋과 Mixamo라는 사이트를 활용할 것이다.
유니티짱 같은 경우는 유니티에서 만든 캐릭터 에셋이다 보니, 애니메이션 구조가 어떻게 이루어져 있는지 파악하기에 좋은 용도로 활용할 수 있다.
이를 통해서 Mixamo와 같은 유니티가 아닌 외부 사이트에서 받은 파일을 유니티에 적용시키는 방법과, 애니메이션을 자연스럽게 만드는 방법에 대해 알아보고자 한다.
유니티 에셋을 활용하든, Mixamo와 같은 사이트를 활용하든, 인간(Humanoid) 애니메이션 파일을 찾을 때 모델의 T-Pose 애니메이션이 있는지 확인해보자.
T-Pose 모델을 기본으로 삼는 이유는, T-Pose로 되어 있는 모델이 Auto-Mapping에서 문제를 일으키지 않는 확률이 크기 때문이다.
Auto Mapping은 아래 사진처럼 모델의 모든 골격과 관절을 표현하여, 애니메이션으로 옮길 수 있는 상태로 만드는 단계라 생각하면 된다.
그러면 Mixamo에서 모델 및 애니메이션 파일을 다운 받아보자. Mixamo는 유니티 전용 애니메이션 파일로 형식을 정해서 다운받을 수 있으며, Shoot 애니메이션으로 실습을 진행해보자.
Mixamo에서 유니티에 적용 가능한 모델 및 애니메이션 파일을 받아 가져왔다. 하지만 Mixamo는 기본적으로 유니티 전용 모델 애니메이션만 만드는 곳은 아니기 때문에, 해당 사이트에서 받아온 파일은 유니티에서 적용할 수 있도록 설정해주어야 한다.
그러면 해야 할 설정을 알아보자.
먼저 아래와 같은 파일을 다운받는다
T-Pose 모델을 씬 화면에 꺼내놓고 세팅을 시작한다. Y bot의 T-Pose 모델을 살펴보자.
Y bot을 처음 받으면 이와 같이 초기설정이 되어 있으나, Generic으로 설정된 Animation Type의 캐릭터는 Humanoid 애니메이션을 적용할 수 없다. 유니티의 대부분의 캐릭터는 Humanoid 설정이 되어 있으니 해당 내용을 수정하도록 하자.
또한 받은 모든 애니메이션 또한 Humanoid로 전환해보자. 이렇게 한 후 애니메이션을 한 번 적용해보자.
걷는 모션이 잘 적용되는 듯 보이나 관절이 뭔가 이상하게 출력되는 것을 확인할 수 있다. 이는 애니메이션의 적용 과정 중 골격의 오차가 발생하여 생기는 문제이다. 이걸 방지하기 위해선 애니메이션의 세팅을 아래와 같이 한다.
이와 같이 설정하고 나면 아래와 같이 애니메이션이 정상출력되는 것을 확인할 수 있다.
이제 걷는 것은 제대로 세팅되었으나 조금 이상한 점이 있다. 캐릭터가 정면으로 이동해야 하는데 사선으로 이동하는 점이다. 이건 Mixamo에서 만든 애니메이션이다 보니 유니티의 설정과 맞지 않을 때 발생한다.
해당 애니메이션을 살펴 보면 골반 쪽이 기준이 되어 캐릭터가 움직이고 있다. 골반을 기준으로 다리가 틀어지며 이동을 하다 보니 사선으로 이동하는 것처럼 된 것이다.
유니티에서는 보통 캐릭터를 만들 때 발을 기준으로 캐릭터의 중심점을 설정한다. 왜냐하면 캐릭터가 점프를 하고, 이동을 하는 등의 애니메이션을 만들 때, 체형이나 키 등의 변수 사항을 줄이기 위해서이다. 하지만 이 캐릭터의 경우에는 중심점이 발이 아닌 골반에 있으므로, 행동이 의도치 않게 나타나는 것이다.
이를 위해서 애니메이션의 추가 설정이 필요하다.
애니메이션 세팅을 보면 Root Transform Rotation이라는 것이 있다.
이곳을 보면 Body Orientation으로 세팅되어 있는데, 이것 때문에 현재 걸을 때 골반 중심으로 움직이고 있다. 이 설정을 바꿔주면 원래 의도했던 대로 애니메이션면 원래 의도했던 대로 애니메이션이 출력된다.
각 기능의 상세한 설명은 유니티 메뉴얼을 참고하자.
https://docs.unity3d.com/kr/560/Manual/RootMotion.html
실제로는 이 기능들을 다 외우기보다는 써 보면은 기능을 대강 알 수 있다.
해당 옵션의 대표적인 사용처에 관해서는 아래와 같이 요약하고자 한다.
* Apply Root Motion
지금의 걷는 애니메이션을 보면 전방으로 나아가고 있는 것을 확인할 수 있다.
언뜻 보면 정상작동하고 있는 것으로 보이지만 이런 문제가 있을 수 있다.
캐릭터의 이동을 나중에 스크립트로 반영해야 하는데, 애니메이션 자체가 이미 이동하고 있으면 실제 이동하는 속도보다 더 많이 이동할 수도 있다. 또한 캐릭터 간 체형이 다르면 이동거리가 달라질 수도 있다.
이런 부분을 방지하기 위해 Apply Root Motion을 해제하면, 애니메이션이 아래와 같이 재생된다.
간혹 게임 중에 자세히 관찰해 보면, 실제로 발을 걷는 모션과 이동 거리가 다른 경우가 있는데 이런 식으로 작동하기 때문에 발생하는 문제이다. 이는 밸런스를 위한 이동속도 고정으로 인해 발생하는 어쩔 수 없는 문제이기도 하다.
예를 들어 플레이어가 공격을 해야 하는 상황이 있을 때, 공격 버튼을 한 번 누르고 다시 원래 행동으로 돌아가야 할 때가 있을 것이다. 이때 Attack을 따로 연결해서 쓰는 것 대신에 이와 같이 사용할 수 있다.
Attack은 트리거로 설정된 Parameter이고, Attack 트리거가 발생한 순간 공격을 수행하고 Exit으로 빠져나가 다시 원래 상태로 돌아간다.
언뜻 보면 이대로도 잘 작동할 것으로 보이지만 문제가 발생한다.
걷던 중에 공격을 하면, 걷던 애니메이션을 멈추고 공격을 하기 때문에 모션이 부자연스럽게 나온다. 또한 앞으로 걷기 말고도 다양한 모션이 추가될 것인데, 그 모든 상황에 대한 연결 애니메이션을 만들기도 어려운 상황이다.
이런 문제를 해결하기 위해 Avatar Mask라는 것을 활용할 것이다.
우선 아바타 마스크를 만들어보자.
아바타 마스크를 만들어서 인스펙터로 살펴보면, 이와 같이 나온다. 아바타 마스크는 애니메이션 중 일부분만 출력할 수 있도록 설정하는 것으로, 총을 쏘는 모션인 상반신만 사용하기 위해 하반신은 그대로 두고, 상반신만 조작하도록 할 것이다. 따라서, 아래와 같이 세팅한다.
그 다음 애니메이터에서 Layer를 만든 뒤 만들어 두었던 아바타 마스크를 넣는다.
Weight는 해당 애니메이션의 출력 여부를 결정하는 것으로 1로 맞춰준다.
애니메이션은 위에서부터 아래 순서로 출력되기 때문에, 이와 같이 세팅한다고 했을 때 아래와 같이 출력된다.
0번 레이어(위쪽 레이어)에서 걷기를 출력한다. 여기서 1번 레이어(아래쪽 레이어)에서 Attack이 발생했을 때 상반신만 총을 쏘는 애니메이션이 덮어씌워진다. 이렇게 하면 걸으면서 총을 쏘는 애니메이션을 구현할 수 있다.
0번 레이어
1번 레이어
이와 같이 세팅한 후 테스트 해 보면 걷는 중에도 자연스럽게 총 쏘는 애니메이션이 출력된다.
총 쏘는 모션이 잘 안 보이긴 하지만, 손가락을 까딱이고 있는 것을 볼 수 있다.
지금의 걷기 모션을 보면 조금만 걸어도 보폭이 크고 빨리 뛰어도 보폭이 작게 느껴지는 듯 걷는 모션이 부자연스럽다.
이런 부분을 좀 더 자연스럽게 하기 위해 Blend Tree를 활용할 것이다.
Blend Tree는 두 가지 이상의 애니메이션 모션을 블렌드 하는 기능을 말한다. 가장 대표적인 사용 예시로는 달리는 모션에서의 자연스러움을 주기 위해 Idle, Walk, Run 애니메이션을 Blend 하는 것이다.
Blend는 애니메이션을 리스트 형식으로 받아서 넣을 수 있다. 여기에 각각 Idle, Walk, Run 애니메이션을 넣고 세팅을 해 보자.
Automate Thresholds로 자동으로 설정하도록 해도 되지만, 세부 세팅을 했다. 이와 같이 만든 후 테스트를 해 보자.
보다 자연스러운 애니메이션이 된 것을 확인할 수 있다.