애니메이션(Animation)
레거시 GUI

애니메이션(Animation) 스크립팅(레거시)

Unity의 애니메이션 시스템은 멋지게 애니메이션화된 캐릭터를 만들 수 있게 합니다. 이 시스템은 애니메이션 블렌딩, 믹싱, 애니메이션 더하기, 걷기 모션 순환 시간 동기화, 애니메이션 레이어, 모든 애니메이션 재생 제어(시간, 속도, 비중 혼합), 버텍스당 1, 2, 또는 4 개의 골격을 통한 메시 스키닝, 물리 기반 래그돌, 절차 애니메이션 등을 지원합니다. 최상의 결과를 위해서 Unity에서 최적화된 리깅된 캐릭터를 생성하는 베스트 프랙티스와 기법에 대해 최적화된 캐릭터 모델링 페이지를 참고하시기 바랍니다.

애니메이션화된 캐릭터 제작에는 월드에서의 움직임 과 그에 따른 애니메이션화 가 필요합니다. 캐릭터 이동에 대한 자세한 내용은 캐릭터 컨트롤러 페이지를 참조하십시오. 이 페이지는 애니메이션에 초점을 두고 있습니다. 실질적인 캐릭터 애니메이션화는 Unity의 스크립팅 인터페이스를 통해 이루어집니다.

데모 예제를 다운로드하여 미리 설정된 애니메이션되는 캐릭터 예제를 확인할 수 있습니다. 이 페이지에서 기본적인 내용을 익힌 이후에는 animation script interface를 참고하시기 바랍니다.

애니메이션 블렌딩(Animation Blending)

최신 게임에서 애니메이션 블렌딩은 캐릭터가 부드럽게 애니메이션되도록 하는 필수 기능입니다. 애니메이터는 걷기 사이클, 뛰기 사이클, 대기 또는 사격 애니메이션 등과 같은 서로 다른 애니메이션을 생성합니다. 게임 도중 어떤 시점이라도 대기 애니메이션에서 걷기 사이클로, 또는 그 반대로 전환할 수 있도록 해야 합니다. 당연하지만 전환 과정은 최대한 부드러워야 하며, 모션이 어색하게 보여서는 안됩니다.

이를 위해 애니메이션 블렌딩이 활용됩니다. Unity에서는 하나의 캐릭터에 다수의 애니메이션을 재생할 수 있습니다. 이는 전부 블렌드되거나 합쳐져서 최종 애니메이션을 생성합니다.

우선 캐릭터가 걷기와 대기 애니메이션을 전환하더라도 부드럽게 블렌드되도록 합니다. 스크립터가 작업을 쉽게 진행할 수 있도록, 애니메이션의 Wrap ModeLoop 로 설정합니다. 그런 다음 Play Automatically 를 비활성화해서 스크립트가 애니메이션을 한 번만 재생하도록 합니다.

캐릭터 애니메이션화를 위한 첫 스크립트는 간단합니다. 다만 캐릭터가 얼마나 빠르게 움직이는지 감지할 방법이 필요하며, 이를 통해 걷기와 대기 애니메이션을 페이드할 수 있습니다. 간단히 테스트하기 위해 아래와 같이 스탠다드 입력 축을 사용했습니다.

function Update () {
   if (Input.GetAxis("Vertical") > 0.2)
       animation.CrossFade ("walk");
   else
      animation.CrossFade ("idle");
}

이 스크립트를 프로젝트에서 사용하려면 다음 단계를 거쳐야 합니다.

  1. Assets > Create > Javascript 를 사용해서 Javascript 파일을 생성합니다.
  2. 파일에 위의 코드를 복사해서 붙여넣습니다.
  3. 캐릭터 위에 스크립트를 드래그합니다. 이때, 애니메이션이 있는 GameObject 에 스크립트를 부착해야 합니다.

Play 버튼을 누른 후, 위쪽 화살표 키를 누르면 캐릭터가 걷기 시작하고, 키에서 손을 떼면 대기 자세로 돌아갑니다.

애니메이션 레이어(Animation Layers)

레이어는 애니메이션을 그룹화하고 비중을 우선화할 수 있도록 하는 대단히 유용한 개념입니다.

Unity의 애니메이션 시스템은 원하는 만큼 애니메이션 클립을 블렌드할 수 있도록 지원합니다. 블렌드 가중치는 수동으로 설정하거나, 간단하게 animation.CrossFade() 를 사용하여 자동으로 애니메이션화 가중치를 조정하도록 할 수 있습니다.

블렌드 가중치는 적용되기 이전 항상 정규화됨

애니메이션에는 걷기 및 뛰기 사이클이 있고, 둘 다 비중이 1(100%)이라 가정해봅시다. Unity가 최종 애니메이션을 생성하면 이 비중을 정규화해서 걷기와 뛰기 사이클이 각각 50%의 비중을 차지합니다.

하지만 두 애니메이션이 재생되는 경우 한 애니메이션의 비중을 더 높이고 싶은 경우가 많습니다. 수동으로 합이 100%가 되도록 비중을 조정할 수도 있지만, 레이어를 사용하는 것이 더 쉽습니다.

레이어링의 예제

예를 들어, 사격, 대기, 걷기 애니메이션이 있다고 가정하겠습니다. 걷기와 대기 애니메이션은 플레이어의 속도에 따라 블렌드되지만, 플레이어가 사격을 하는 경우 해당 애니메이션만 보여주고 싶을 수 있습니다. 따라서 사격 애니메이션이 더 높은 우선 순위를 가져야 합니다.

이를 가장 쉽게 하는 방법은 단순히 걷기나 대기 애니메이션을 사격 도중에도 재생하는 것입니다. 이를 위해 사격 애니메이션이 나머지 애니메이션보다 더 높은 우선 순위에 있도록 하면 되는데, 이는 사격 애니메이션이 우선적으로 블렌드 가중치를 얻게 됨을 의미합니다. 걷기와 대기 애니메이션은 사격 애니메이션이 블렌드 가중치의 100%를 차지하지 않는 경우에만 가중치를 차지하게 될 것입니다. 따라서 사격 애니메이션을 CrossFading하는 경우, 가중치는 0에서 시작해서 짧은 시간에 100%가 될 것입니다. 초기에는 걷기와 대기 애니메이션은 어느 정도의 블렌드 가중치를 차지하지만, 사격 애니메이션이 완전히 페이드 된 경우, 가중치를 전부 잃게 됩니다. 이렇게 하면 의도한 대로 될 것입니다.

function Start () {
   // Set all animations to loop
   animation.wrapMode = WrapMode.Loop;
   // except shooting
   animation["shoot"].wrapMode = WrapMode.Once;

   // Put idle and walk into lower layers (The default layer is always 0)
   // This will do two things
   // - Since shoot and idle/walk are in different layers they will not affect
   // each other's playback when calling CrossFade.
   // - Since shoot is in a higher layer, the animation will replace idle/walk
   // animations when faded in.
   animation["shoot"].layer = 1;

   // Stop animations that are already playing
   //(In case user forgot to disable play automatically)
   animation.Stop();
}

function Update () {
   // Based on the key that is pressed,
   // play the walk animation or the idle animation
   if (Mathf.Abs(Input.GetAxis("Vertical")) > 0.1)
      animation.CrossFade("walk");
   else
      animation.CrossFade("idle");

   // Shoot
   if (Input.GetButtonDown ("Fire1"))
      animation.CrossFade("shoot");
}

기본적으로 animation.Play()animation.CrossFade() 가 동일한 레이어에 있는 애니메이션을 중단하거나 페이드 아웃합니다. 이는 대부분의 경우 의도한 사항입니다. 앞의 사격, 대기, 뛰기 예제에서 대기와 뛰기 애니메이션을 재생하는 것은 사격 애니메이션에 영향을 주지 않으며, 그 반대도 마찬가지입니다. 원한다면 animation.CrossFade에 선택 파라미터를 추가해서 이 동작을 변경할 수 있습니다.

애니메이션(Animation) 믹싱

애니메이션 믹싱을 통해 특정 애니메이션이 특정 신체 부위에만 적용되도록 하여 생성해야 하는 애니메이션의 수를 줄일 수 있습니다. 이는 이들 애니메이션이 다른 애니메이션과 동시에 사용되어 다양한 조합을 생성할 수 있다는 의미입니다.

애니메이션에 애니메이션 믹싱 변환을 더하려면, 해당 AnimationState에서 AddMixingTransform() 을 호출하면 됩니다.

믹싱의 예제

애니메이션 믹싱의 예제로는 손을 흔드는 애니메이션이 있습니다. 캐릭터가 대기 중이거나 걷는 동안 손을 흔들게 한다고 가정합시다. 애니메이션 믹싱 없이는 대기와 걷기 각각의 상태에 대해 손을 흔드는 애니메이션을 생성해야 합니다. 하지만, 어깨 트랜스폼을 믹싱 트랜스폼으로 두어 손을 흔드는 애니메이션에 추가하면, 이 애니메이션은 어깨 관절에서 손까지만 제어합니다. 따라서 나머지 부위는 이 애니메이션에 의해 영향을 받지 않으므로, 대기 또는 걷기 애니메이션을 계속 재생할 수 있습니다. 따라서 이 경우 손을 흔드는 애니메이션은 한 개만 있으면 되며, 나머지 신체 부위는 대기 또는 걷기 애니메이션을 재생할 수 있습니다.

/// Adds a mixing transform using a Transform variable
var shoulder : Transform;
animation["wave_hand"].AddMixingTransform(shoulder);

경로를 사용한 다른 예제입니다.

function Start () {
   // Adds a mixing transform using a path instead
   var mixTransform : Transform = transform.Find("root/upper_body/left_shoulder");
   animation["wave_hand"].AddMixingTransform(mixTransform);
}

애디티브 애니메이션(Animations)

애디티브 애니메이션과 애니메이션 믹싱을 활용하면 게임에 필요한 애니메이션의 수를 줄일 수 있으며, 얼굴 애니메이션을 생성하는 데 중요합니다.

걷거나 뛰는 동안 방향을 전환하면 옆으로 기우는 캐릭터를 만든다고 가정합시다. 이는 네 개의 조합(걷기-기울기-왼쪽, 걷기-기울기-오른쪽, 뛰기-기울기-왼쪽, 뛰기-기울기-오른쪽)이 필요하며, 각각의 조합은 애니메이션을 필요로 합니다. 이들 조합 각각마다 애니메이션을 생성하는 것은 이렇게 단순한 예제에서도 상당한 작업을 요하며, 행동이 하나씩 추가될 때마다 조합의 수는 급격하게 증가합니다. 다행히도 애디티브 애니메이션과 믹싱을 활용하면 각각의 조합마다 서로 다른 애니메이션을 제작할 필요가 없습니다.

애디티브 애니메이션(Animation)의 예제

애디티브 애니메이션을 사용하면 재생 중인 애니메이션 위에 다른 애니메이션 효과를 덮어씌울 수 있습니다. 애디티브 애니메이션을 생성하는 동안 Unity는 애니메이션의 첫 프레임과 현재 프레임 간 차이점을 계산합니다. 그런 다음 이 차이점을 다른 재생 중인 애니메이션 위에 전부 덮어씌웁니다.

이전 예제에서 볼 수 있듯, 왼쪽이나 오른쪽으로 기우는 애니메이션을 만들면 Unity가 이를 걷기, 대기, 뛰기 사이클 위에 덮어씌울 수 있습니다. 이는 아래의 코드를 사용하면 됩니다.

private var leanLeft : AnimationState;
private var leanRight : AnimationState;

function Start () {
   leanLeft = animation["leanLeft"];
   leanRight = animation["leanRight"];

   // Put the leaning animation in a separate layer
   // So that other calls to CrossFade won't affect it.
   leanLeft.layer = 10;
   leanRight.layer = 10;

   // Set the lean animation to be additive
   leanLeft.blendMode = AnimationBlendMode.Additive;
   leanRight.blendMode = AnimationBlendMode.Additive;

   // Set the lean animation ClampForever
   // With ClampForever animations will not stop 
   // automatically when reaching the end of the clip
   leanLeft.wrapMode = WrapMode.ClampForever;
   leanRight.wrapMode = WrapMode.ClampForever;

   // Enable the animation and fade it in completely
   // We don't use animation.Play here because we manually adjust the time
   // in the Update function.
   // Instead we just enable the animation and set it to full weight
   leanRight.enabled = true;
   leanLeft.enabled = true;
   leanRight.weight = 1.0;
   leanLeft.weight = 1.0;

   // For testing just play "walk" animation and loop it
   animation["walk"].wrapMode = WrapMode.Loop;
   animation.Play("walk");
}

// Every frame just set the normalized time
// based on how much lean we want to apply
function Update () {
   var lean = Input.GetAxis("Horizontal");
   // normalizedTime is 0 at the first frame and 1 at the last frame in the clip
   leanLeft.normalizedTime = -lean;
   leanRight.normalizedTime = lean;
}

팁: 애디티브 애니메이션을 사용하는 경우 비추가 애니메이션에서 사용되는 각각의 트랜스폼에 대해 다른 추가 애니메이션을 사용하는 것이 좋은데, 이는 애니메이션이 최종 프레임 결과 위에 덮여씌워지는 것을 방지합니다. 이는 최대한 피하는 것이 좋습니다.

순서대로 캐릭터 애니메이션화

간혹 캐릭터의 골격을 순서대로 애니메이션화해야 할 때가 있습니다. 예를 들어, 캐릭터의 머리가 3D 공간의 특정 지점을 보게 할 경우 타겟 지점을 트래킹하는 스크립트를 통해 가장 잘 처리할 수 있습니다. Unity에서는 이를 매우 쉽게 처리할 수 있는데, 골격 정보는 단지 스킨드 메시를 구성하는 변형들이기 때문입니다. 따라서 스크립트에서 캐릭터의 골격을 게임 오브젝트의 트랜스폼과 동일한 방법으로 제어할 수 있습니다.

한 가지 알아두어야 할 중요한 사항은, 애니메이션 시스템은 LateUpdate() 함수 이전, Update() 함수 이후 Transforms을 업데이트 한다는 사실입니다. 따라서 LookAt() 함수에 무언가를 하려면 LateUpdate() 에도 동일하게 진행하여 애니메이션 오버라이드가 확실하게 되도록 해야 합니다.

래그돌 역시 같은 방식으로 생성됩니다. 리지드바디, 캐릭터 조인트, 캡슐 콜라이더를 다른 골격에 부착하기만 하면 됩니다. 이렇게 하면 이후 스킨을 입힌 캐릭터를 물리적으로 애니메이션화합니다.

애니메이션(Animation) 재생 및 샘플링(Sampling)

이 섹션은 Unity의 애니메이션이 엔진에서 재생되는 경우 어떻게 샘플링 되는지 설명합니다.

애니메이션 클립은 일반적으로 고정 프레임 속도에서 제작됩니다. 예를 들어, 3ds Max 또는 Maya에서 애니메이션을 초당 60프레임으로 생성할 수 있습니다. 이러한 애니메이션을 Unity로 가져오는 동안 이 프레임 속도 역시 임포트 기능이 참조하여 임포트된 애니메이션의 데이터가 60fps로 샘플링됩니다.

하지만 게임은 보통 가변 프레임 속도에서 작동합니다. 프레임 속도는 컴퓨터에 따라 달라질 수 있으며, 주어진 시점에서 카메라가 보는 방향의 복잡도에 따라 매 초마다 달라질 수도 있습니다. 기본적으로 이는 게임이 작동하는 프레임수에 대한 가정을 내릴 수는 없다는 의미입니다. 따라서 애니메이션이 60 fps에서 제작되었다 하더라도 재생되는 경우에는 56.72 fps, 83.14 fps와 같이 다른 프레임 속도에서 재생될 수 있습니다.

따라서 Unity는 가변 프레임 속도에서 애니메이션을 샘플링해야 하며, 원래 의도된 프레임수를 보장할 수 없습니다. 다행히 3D 컴퓨터 그래픽스 애니메이션은 이산 프레임으로 구성된 것이 아니라 지속적인 커브로 구성되어 있습니다. 이들 커브는 원본 애니메이션의 프레임에 해당하는 시점에서만이 아닌, 어떤 시점에서도 샘플링될 수 있습니다. 사실, 게임이 애니메이션이 제작된 프레임수 보다 높은 프레임 속도에서 작동하는 경우, 애니메이션은 애니메이션 소프트웨어에서 보다 더욱 부드럽고 유동적으로 보입니다.

실용적 목적을 위해 Unity가 가변 프레임 속도에서 애니메이션을 샘플링하는 사실을 무시할 수 있습니다. 하지만 아주 특정적인 설정에 대해 트랜스폼이나 프로퍼티를 애니메이션화하는 애니메이션에 종속하는 게임플레이 로직인 경우, 보이지 않는 곳에서 재샘플링이 실행된다는 사실을 기억해야 합니다. 예를 들어, 30프레임 동안 0에서 180도로 회전하는 애니메이션이 있고, 중간 지점에 도달했을때 코드가 이를 알게 하려면, 현재 위치가 90도인지 확인하는 조건부 명령문을 코드에 넣어서는 안됩니다. 이는 Unity가 게임의 가변 프레임수에 따라 애니메이션을 샘플링하므로, 회전 각도가 90도에 미치지 못하거나 그 이상인 경우에 샘플링할 수 있는 문제가 생기기 때문입니다. 애니메이션의 특정 지점에 도달한 사실을 코드에 알리려면 대신 애니메이션 이벤트를 사용해야 합니다.

또한 가변 프레임수 샘플링의 결과로서, WrapMode.Once 을 사용하여 재생되는 애니메이션은 최종 프레임의 정확한 시점에 샘플링되지 않을 수 있습니다. 게임의 한 프레임에서는 애니메이션 종료 바로 직전에 샘플링할 수도 있고, 아니면 애니메이션 종료 이후에 샘플링을 시도하였으나 애니메이션이 종료되었기에 샘플링이 비활성화될 수도 있습니다. 정확하게 애니메이션의 최종 프레임을 샘플링하려면 사용자가 직접 애니메이션을 중단하기 이전 최종 프레임을 반복해서 샘플링하는 WrapMode.ClampForever 를 사용합니다.

애니메이션(Animation)
레거시 GUI