
유니티에서 사물을 상태에 따라 지정된 움직임을 수행하게 하는 방법을 알아본다

현실에서의 물체의 움직임과 기능을 게임 공간에서는 애니메이션으로 구현할 수 있다. 플레이어와 동물, 몬스터, 심지어 사물의 움직임도 애니메이션으로 처리할 수 있다. 상호작용에 따른 움직임 구현을 위해 애니메이션이 사용된다
애니메이션은 유한상태머신(FSM)을 사용하여 구현한다. 이 패턴은 게임 AI를 개발할때도 유용하다
애니메이션은 게임 오브젝트에게 움직임을 부여하는 기능. 이를 사용해 미리 저장되어 있는 움직임을 적용하거나, 움직임을 만들어 내기도 한다.
단일의 애니메이션을 적용하기 위한 컴포넌트
MVC 모델로 볼 수 있다. PlayerController(Controller), Animator Parameter(Model), 상태 중 클립(View)
- 프로그래머 입장에서 애니메이션은 UI에서 많이 쓰인다. 플레이어, 몬스터 등 물체들의 움직임을 구현하는 것은 전문적으로 애니메이터들이 한다
상태 패턴을 기반으로 동작을 제어하는 시스템을 구현하기 위한 모델MVC 패턴모델뷰컨트롤러상태 변화에 따른 객체의 동작이 복잡하고, 여러 가지 조건에 대응해 상태를 변경해야 하는 경우 사용애니메이션 컨트롤러가 유한상태머신을 이용해 구현되어 있으며, 부여된 객체의 상태변화에 따른 애니메이션을 유동적으로 관리한다
public class AnimPlayerController : MonoBehaviour
{
[SerializeField] Rigidbody rigid;
[SerializeField] AnimPlayerModel 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;
}
// 이동 방법으로 많이 쓰는 방식
// rigidbody의 velocity를 MoveTowards 한다
// 즉, 현재 물체의 속도를 특정 속도까지 "도달" 시키는 것
// MoveTowards는 목적지에 도달하면 더이상 움직이지 않는 것이니까
// 특정 속도(목적지) 도달 후 더 속도가 증가 안함
rigid.velocity = Vector3.MoveTowards(rigid.velocity, dir * model.MaxSpeed, model.Accelation*Time.deltaTime);
if(dir != Vector3.zero)
{
transform.rotation = Quaternion.Lerp(transform.rotation,Quaternion.LookRotation(dir),10*Time.deltaTime);
}
}
}
public class AnimPlayerModel : MonoBehaviour
{
[SerializeField] float maxSpeed;
public float MaxSpeed { get { return maxSpeed; } set { maxSpeed = value; } }
[SerializeField] float accelation;
public float Accelation { get { return accelation; } set { accelation = value; } }
}
객체의 움직임을 미리 정의한(저장한) 파일. 애니메이션 파일이 이에 해당한다


Idle이라 이름을 붙이고, 애니메이터 창에서 Motion에 해당 애니메이션을 참조시킨다
Idle 애니메이션 동작을 만들거다Add Property를 누르면, 여러 속성들을 변경할 수 있게 추가할 수 있고, 속성 변경을 원하는 시간에 Key값을 변경 시켜서 애니메이션이 만들어진다Key 값을 추가시키면서 내용을 조정하면 된다
Humernoid 애니메이션은 위와 같이 되어 있다
Key를 늘려가며 작업하기에는 오래걸리고 힘들다. 녹화기능으로 간편하게 해본다


Loop Time 을 체크해서 애니메이션을 반복시킬 수 있다
애니메이터가 직접 모든 애니메이션 클립들을 들고 있는 것이 아니라, 애니메이션 컨트롤러가 들고 있는 방식으로 되어있다
프로젝트 창에서 위와 같이 클릭하면 새로 생성된다
애니메이터 컴포넌트에서 Controller 부분에 참조시키면 적용된다애니메이터 컨트롤러를 더블 클릭하면 자동으로 애니메이터 창이 열리게 된다

Die 애니메이션이 있을 때, 모든 상태에서 Die로 연결시키기에는 너무 많다. 그럴 때 쓸 수 있는 기능이 AnyState이다Idle이든, Walk든, Attack이든 특정 조건을 만족하면 해당 애니메이션으로 변경 시킬 수 있다
Die에서도 AnyState로 인식이 되서, Die 에서 Die로 무한 순환이 일어난다
Can Transition To Self 를 체크 해제 하면 해결된다
Transition의 순서에 따라 우선순위가 정해진다. 더 앞에 있는 Transition으로 적용된다
Animation 컴포넌트.Controller : 애니메이션 컨트롤러를 참조한다Avatar : 애니메이션이 적용될 아바타를 참조한다Update Mode : Normal은 TimeScale의 영향을 받는다. Animated Physics는 Fixed Update에 호출된다. Rigidbody와 함께 물리처리에 적용해야하는 애니메이션의 경우에 사용한다, Unscaled Time은 Time Scale과 상관없이 재생된다



애니메이터를 검색한다

Move() 함수에 다음을 추가한다Move()
{
animator.SetFloat("MoveSpeed", rigid.velocity.magnitude);
}

Player에 위와 같이 RigidBody, 작성한 스크립트 2개를 추가한다


Animator 창 에서 Walk 상태를 추가해주고, Make Transition을 선택해서 Idle과 쌍방으로 이어준다
Animator 창에서 Parameter를 추가해준다. float형 MoveSpeed를 추가했다
Idle 상태를 클릭하고, 동그라미 친 부분을 누른다
Conditions를 추가해준다. Walk 상태는 반대로 Less로 잡아주면 된다
Walk 애니메이션, 멈춰있을 때는 Idle 애니메이션이 재생된다mixamo 사이트의 애니메이션과, 다른 휴머노이드 모델의 예시로 쓸 수 있는 유니티짱을 다운 받는다모델에서 애니메이터 컴포넌트의 아바타가 어떤 타입인지에 따라 다음과 같이 동작 여부가 달라진다
| 모델 | 애니메이션 | 동작여부 |
|---|---|---|
| 휴머노이드 | 휴머노이드 | O |
| 제네릭 | 휴머노이드 | X |
| 휴머노이드 | 제네릭 | X |
| 제네릭 | (모델전용 한정) 제네릭 | O |
- 동물 모델(제네릭) 이면 동물 전용 애니메이션(제네릭)을 써야 한다

T- Pose를 일단 기본으로 다운로드 해야한다. 믹사모에서 다운로드 받을 경우, 제네릭으로 임포트 되는데, 휴머노이드로 변경하면 관절이 제대로 오토매핑 된다. T-Pose만 추가 조정 필요없이 가능하다. 단, 유니티에서 다운 받는 모델, 애니메이션 에셋의 경우 필요없다
Y Bot. 여성형이면 X Bot을 검색해서 모델을 설정한다
fbx 파일을 다운로드 해야 한다fbx 파일은 프로젝트 창에 드래그&드롭 하면 추가된다
Rig를 클릭한다. 그다음 generic을 Humanoid로 변경한다
Apply를 누르고 Configure를 누르면 매핑을 확인 할 수있다

참고
- 에셋 스토어에서 구매한 모델, 애니메이션인 경우 웬만하면
UV매핑과Humanoid설정이 되어 있다
mixamo에서 T-Pose 외에 이제 애니메이션을 추가로 다운로드 받는다. 캐릭터는 동일하게 Y BOT 또는 X BOT으로 해야한다.

T-POSE와 함께 캐릭터 모델을 받았으니 이번에는 Skin(without skin)으로 설정하고 받는다 받는다

임포트한 애니메이션의 Rig를 Humanoid로 설정하면, 자세가 구부정한 것을 볼 수 있다. 이것이 아까 설명한 것처럼 T-Pose 모델이 필요한 이유다

빨강 동그라미가 쳐친 Pose -> reset 을 누르면, 원래 포즈가 나온다. 여기서 매핑이 진행되어서 뒤틀린 것이다


Rig -> Avatar Definition을 Y Bot@T-PoseAvatar를 가져오면된다. 이제 매핑이 제대로 된 것을 볼 수 있다

먼저 Y Bot 모델에 Animator 컴포넌트를 추가한다. Controller에 Animation Controller를 만들어 넣는다
만든 Animation Controller를 연다

다운받은 애니메이션은 프로젝트 창에서 여기에 있다

화면처럼 Throw 애니메이션을 애니메이터 창에서 원하는 상태에 집어넣는다
실행하면 이제 애니메이션이 제대로 수행되는 것을 확인할 수 있다
위 과정들을 정리하자면 아래와 같다
0. Mixamo.com 사이트 접속
1. Chracters : 모델(제네릭) / 남성형 y bot & 여성형 x bot
2. Animations : T pose 검색 후 적용
3. Download -> Format(for unity) + Skin(with skin)
4. 유니티 프로젝트 창에 모델링 파일 넣기
5. 유니티 프로젝트의 모델링 파일을 선택하고 Rig 탭 선택
6. Animation Type : Generic->Humanoid
7.Apply 버튼을 눌러 적용
8. Configure -> T Pose의 모델링의 관절이 잘 매핑되었지 확인
9. 혹시나 자동매핑이 되지 않은 경우면 (손수 관절 넣어주기)
=> 휴머노이드 모델 =================================
0. Mixamo.com 접속
1. 애니메이션에서 원하는 애니메이션(제네릭) 선택
*** 단, 주의사항 ***
모델을 다른 모델로 선정하지 말고 똑같은 모델로 애니메이션
2. Download -> Format(for unity) + Skin(without skin)
3. 유니티 프로젝트 창에 옮겨서 복사
4. 유니티 프로젝트 창으로 옮긴 애니메이션 모델링파일 선택
5. Rig 탭 선택 -> Animation Type -> Humanoid 변경
6. Avatar Definition : Copy From Other Avatar
7. Source : Y bot avatar 또는 X bot avatar T pose로 선택
8. Apply 로 적용
=> 믹사모를 휴머노이드 애니메이션으로 적용 ============="

여러 애니메이션들이 담긴 팩을 다운 받아본다. 이번에는 mixamo에서 Shooter Pack을 받아본다

애니메이션들을 Asset 폴더 적당한 곳에 풀고, 모든 애니메이션을 Rig -> Animation Type -> Humernoid로 바꿔준다
Avatar Definition -> Copy From Other Avatar -> Y Bot@T-PoseAvatar 로 변경해준다

Loop Time을 체크해주면 반복 시킬 수 있다Loop Time 쪽의 loop match가 초록불이면 루프가 가능한 애니메이션 이라는 뜻이다. 첫 프레임의 키와 마지막 프레임의 키가 동일할 경우, 초록불이다. 

Pivot을 기준으로(발바닥) 잡는다. 이유는 여러가지 있는데, 예를들어 Center로 기준이 되어 있을 경우 플레이어가 범위 공격을 했을 때 키가 작은 몬스터는 맞고, 키가 큰 몬스터는 맞지 않는 문제가 생길 수 있다



Center(골반)을 기준으로 모델링 작업한다
mixamo에서 다운 받은 애니메이션들도 골반 기준으로 작업되었다
Root Transform Rotation -> Based Upon을 Original로 바꿔준다. 그러면 발로 기준이 바뀌게 된다

이제 원하는 대로 정면으로 이동한다


이렇게 틀어져 있는 애니메이션의 포지션도 Bake Into Pose를 체크하면

Transform 위치도 잡아준다
유니티짱의 애니메이션들은, 유니티 기준으로 만들어져서 발바닥을 기준으로 잡혀있다


Feet을 기준으로 바꾼다.

Mirror를 체크하면, 애니메이션이 좌우반전된다
Y Bot이 먼저 앞서나가는 것을 볼 수 있다. 이것은 모델의 실제 보폭만큼 걷는 것이기 때문이다. Root Motion과 관련이 있다.
Apply Root Motion을 체크 해제하면, 제자리 걸음을 하게 된다. 이제 스크립트로 이동을 구현하면 둘이 동일하게 걷는다
Root Motion을 켜줘야 한다


Fire 상태를 추가하자. 추가한 상태에 firing rifle 애니메이션을 추가한다Any State에서 연결해준다
Parameter로 Trigger를 추가해준다. 이름은 Fire로 하겠다
Fire 상태 이후 Exit 상태로 연결 해준다. Exit 이후는 자동으로 Entry로 돌아간다
AnyState의 Has Exit Time을 체크 해제 한다
Exit은 반대로 체크해주고, Exit Time을 위와 같이 변경한다public class AnimPlayerController : MonoBehaviour
{
[SerializeField] Rigidbody rigid;
[SerializeField] AnimPlayerModel model;
[SerializeField] Animator animator;
void Update()
{
if(Input.GetKeyDown(KeyCode.Space))
{
Fire();
}
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.Accelation*Time.deltaTime);
if(dir != Vector3.zero)
{
transform.rotation = Quaternion.Lerp(transform.rotation,Quaternion.LookRotation(dir),10*Time.deltaTime);
}
animator.SetFloat("MoveSpeed", rigid.velocity.magnitude);
}
private void Fire()
{
animator.SetTrigger("Fire");
}
}
Space키를 누르면 Trigger로 인해 발사하는 애니메이션이 재생된다Trigger는 bool 처럼 true, false가 아니라 실행 될 때 한 번 ON이 되는 것이다
Fire 트리거를 누르면, 그 즉시 발사 애니메이션이 재생된다
Upper Layer로 지정했다
아바타 마스크는 위와 같이 추가할 수 있다

Mask 부분에 해당 아바타 마스크를 추가하면 된다. Weight는 1로 한다. 애니메이션이 적용되는 정도를 나타낸다Blending은 애니메이션이 덧씌워지는 방식을 결정한다. Override는 덮어쓰기, Additive는 더해서 재생된다
Base Layer가 우선순위가 더 높다. 그래서 먼저 걷기 애니메이션이 적용된 후, 상반신에 덮어쓰기로 발사 애니메이션이 재생 될 수 있다. 반대인 경우, 캐릭터가 멈춰있는 상태가 된다
Idle용으로 애니메이션을 새로 추가한다
Base Layer 설정
Upper Layer 설정

애니메이터 창에서 위와 같이 생성 가능하다. 생성한 이후 이름을 Move로 바꿔준다. 이거 하나로 정지, 걷기, 뛰기 다 해줄 수 있다

Add Motion Field를 누른다
Parameter는 MoveSpeed로 설정한다. Automate Thresholds를 체크 해제 하면 Threshold를 직접 정해줄 수 있다
이제 속도가 조금씩 올라갈 때 마다, 자연스럽게 애니메이션들이 블렌딩이 되어 재생된다

이동이 매우 자연스러워졌다
1D, 2D가 있다. 2D는 2개의 Parameter로 구현한다. 2차 함수라 생각하면 편하다. 애니메이션을 8방향으로 조절이 가능하다. 예를들어 대각선 이동과 정면으로 뛰어서 이동을 합성하면 대각선으로 뛰어서 이동이 가능해진다