주인공이 죽으면 PlayerDie()::PlayerCtrl 함수에서 씬뷰의 모든 몬스터들을 찾아 SendMessage를 이용 하나하나 메세지를 보냈다. 만일 적캐릭터가 엄청 많다면 그닥 효율적인 방법이 아니다.
void PlayerDie() {
//MONSTER 태그를 가진 모든 게임오브젝트를 찾아옴
GameObject[] monsters = GameObject.FindGameObjectsWithTag("MONSTER");
foreach(GameObject monster in monsters) { //모든 오브젝트를 순차적으로 불러옴
monster.SendMessage("OnPlayerDie", SendMessageOptions.DontRequireReceiver);
}
}
순차적 호출 방식을 이벤트 구동방식으로 변경하자. 주인공이 죽었다는 이벤트를 시스템이 통보해주는 방식이다.
델리게이트
델리게이트는 함수(메서드)를 참조하는 변수를 의미한다. C++의 함수 포인터와 같은 의미이다. 사용하기전 아래와 같이 델리게이션 타일을 선언후 델리게이트타입을 이용 변수를 만들고 함수와 연결후 사용해야 한다.
using UnityEngine;
public class DelegateDemo : MonoBehavior
{
//델리게이트 타입선언
delegate float SumHandler(float a, float b);
//델리게이트타입 델리게이트변수
SumHandler sumHandler;
//덧셈연산을 하는 함수
float sum(float a, float b) {
return a + b;
}
void Start() {
sumHandler = sum; //델리게이트변수에 함수를 연결
float sum = sumHandler(10.0f, 5.0f);
Dubug.Log($"Sum = {sum}"};
}
}
주인공의 사망 이벤트 처리
PlayerCtrl() 함수내 델리게이트와 이벤트를 선언한다.
public delegate void PlayerDieHandler(); //델리게이트타입 선언
public static event PlayerDieHandler OnPlayerDie; //델리게이트 변수선언
PlayerCtrl() 함수내PlayerDie()함수 내용을 지우고 OnPlayerDie()함수로 이벤트를 콜한다
void PlayerDie() {
//MONSTER 태그를 가진 모든 게임오브젝트를 찾아옴
/*
GameObject[] monsters = GameObject.FindGameObjectsWithTag("MONSTER");
foreach(GameObject monster in monsters) { //모든 오브젝트를 순차적으로 불러옴
monster.SendMessage("OnPlayerDie", SendMessageOptions.DontRequireReceiver);
}
*/
OnPlayerDie(); //주인공 사망 이벤트 호출(발생)
}
이제 MonsterCtrl를 변경한다. 우선 애니메이터의 "Die" 파라미터 해시값을 추출해 놓는다.
몬스트의 생명치를 저장할 hp변수를 만들어 놓는다.
private readonly int hashDie = Animator.StringToHash("Die"); //파라미터해시값 추출
private int hp = 100; //몬스터 생명점수
이제 MonsterCtrl에서 발생하는 이벤트에 반응할 OnPlayerDie()함수를 Player.OnPlayerDie와 연결한다.
PlayerCtrl는 스크립트이름인데 선언없이 사용가능하다. 교재는 PlayerCtrl이다. 변경하려고 했는데 자잘한 에러때문에 그냥 쓴다.
이벤트 스크립트는 반드시 스크립트의 활성화 시점에 연결하고 비활성화될때 해제해야 한다.
MonsterCtrl Start()함수 앞에 OnEnable() OnDisable()함수를 추가한다.
private int hp = 20; //몬스터 생명점수
private void OnEnable() { //스크립트가 활성활 될때 콜백되는 함수
PlayerCtrl.OnPlayerDie += this.OnPlayerDie; //교재는 PlayerCtrl.
}
private void OnDisable() { //스크립트가 활성활 될때 콜백되는 함수
PlayerCtrl.OnPlayerDie -= this.OnPlayerDie; //교재는 PlayerCtrl.
}
몬스터의 사망처리
CheckMonsterState()에 state.DIE일 경우 코루틴을 빠져나가는 조건문을 추가한다.
IEnumerator CheckMonsterState() {
while(!isDie) {
yield return new WaitForSeconds(0.3f);
if(state == State.DIE) yield break; //몬스터 상태가 DIE면 코루틴 종료
OnCollisionEnter()함수에 충돌이 났을때 hp를 -10을 감소시키고 state를 DIE로 바꿔준다.
private void OnCollisionEnter(Collision coll) {
if (coll.collider.CompareTag("BULLET")) {
//중략
hp -= 10;
if(hp <= 0) state = State.DIE;
}
}
state가 DIE로 변경되면 MonsterAction()코루틴에서 "DIE" 트리거가 일어나고 Collider가 비활성화돼 죽은 몬스터에 총을 발사했을때 혈흔이 일어나지 않는다.
IEnumerator MonsterAction() {
while (!isDie) {
//중략
case State.DIE: //사망상태
isDie= true;
agent.isStopped = true; //추적 정지
anim.SetTrigger(hashDie); // 애니메이트 hasDie 트리거발생
GetComponent<CapsuleCollider>().enabled = false; //충돌컴포넌트 비활성화
break;
}
yield return new WaitForSeconds(0.3f);
}
}
Monster의 die 애니메이션 클립을 MonsterAnim 애니메이터뷰에 추가하고 Any State->Die State로 Transition을 만든다. 조건으로 사용할 파라미터는 Trigger타입으로 die를 추가하고 설정한다. Die State는 마지막 스테이트인 Exit스테이트로 연결하고 각 Transition의 연결조건은 다음과 같다.
실행해보면 죽은 몬스터가 떠 있다. 0.5 모델/Monster폴더의 Monster의 Die 애니메이션의 프리뷰를 봐도 떠 있다.
05.Models>Monster>Monster모델 원본을 눌러 인스펙터에서
Animation 탭을 눌러 die애니메이션을 골라 프리뷰를 봐도 공중에 떠 있다. 몬스터 아래 원이 피봇위치이다. 피봇위치는 Transform.position이다
아래쪽 Root Transform Position(Y)의 Bake Into Pose를 체크하던지 Based Upon값을 Feed로 설정하면 Pivot좌표값이 발위치로 조정된다.
실행하려면 방금 몬스터 모델 원형을 변경했기 때문에 Apply를 물어보면 적용시킨다. 실행해보면 잘된다.
MonsterAnim을 더블클릭해 애니메이터를 열고 07.animations폴더에서 Monster@Gangnam Style애니메이션 파일을 드래그에 추가한다. 파라미터 +를 누르고 PlayerDie라는 이름의 Trigger를 만든다. Gangnam Style애니메이션은 주인공이 사망하는 경우에 실행할 것이므로 Any State에서 Gangnam Style 스테이트로 Transition을 연결하고 Gangnam Style에서 Exit로 가는 Transition도 연결한다.
보통의 인체형 모델은 애미메이션을 수행하기 위해 Rig(본 구조, 뼈대)가 설정돼 있다.적 캐릭터 모델도 이러한 관절이 설정돼 있으며 하리라키 뷰에서 확인할 수 있다. 하이라키 뷰상에 표시된 각 관절은 게임오브젝트이며 모드 Transform 컴포넌트를 가지고 있다. 이렇게 하나의 3D모델안의 많은 Transform연산은 로드가 높으므로 최적화가 필요하다. 필요한것만 두고 다른것들은 노출되지 않게 하면 속도 향상에 도움을 줄 수 있다.
몬스터 모델에서 필요한 Rig는 Sphere Collider와 Rigidbody를 추가한 양손이므로 이부분만을 하이라키 뷰에 노출되게 설정해보자. 05.Models/Monster폴더에 있는 Monster원본 모델을 선택후 인스펙터뷰 Rig 탭의 Optimize Game Objects 옵션을 선택하면 하단에 Extra Transforms to Exppose 항목이 나타난다.
L_wrist와 R_wrist만 선택하고 Apply를 누르면 하이라키뷰의 Monster가 정리된걸 볼 수 있다.
본 구조 최적화는 프리팹으로 만들기 전에 해야한다. 하이라키뷰의 Monster는 아직 프리팹으로 만들기 전이므로 원본 Monster모델을 선택하고 본 구조를 최적화 할 수 있었다.
물론 스크린좌표를 월드좌표로 변환해서 레이를 쏴서 그 오브젝트를 바라보게해도 되는데 간단하게 LookAt()을 이용해보겠다.
스크립트를 하나 만들고 RightClick으로 이름 짓는다. 스크립트를 Player에 적용한다.
Monster 인스펙터 TAG가 MONSTER로 되어 있어야 한다.
FindwidhtTag()로 간단히 찾아 monsterTR에 저장하고 LookAt()을 이용해 방향을 바꾼다.
다만. 순간적으로 바꿔지기 때문에 부드럽게 바꾸려면 다른 함수를 써야 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RightClick : MonoBehaviour
{
// Start is called before the first frame update
private Transform monsterTr; //컴포넌트의 캐시를 처리할 변수
void Start()
{
monsterTr = GameObject.FindWithTag("MONSTER").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(1)) {
transform.LookAt(monsterTr);
}
}
}
다른 방법은 어차피 NavMesh가 적에 적용되어 있으니 그걸 응용해보기로 했다. 목적은 자동추적보다는 플레이어의 자동 회전에 가깝다. 일단 하이리키 Player에 NavMesh Agent를 추가한다.
교재의 프로젝트에 영향을 주지 않게 하기 위해 따로 스크립트도 추가하고 NavCtrl로 이름짓는다.
코드는 간단하다. 마우스 우클릭을 하면 Player가 자동으로 적을 따라가고 다시 우클릭을 하면 멈춘다.
다른 스크립트에도 Navigation을 멈추게 하기 위해 SetNavStop()을 추가한다.
public void SetNavStop() {
agent.isStopped = true;
}
추가된 NavCtrl 전체코드, 대부분 MonsterCtrl에서 NavMesh에 관련된 코드를 복붙했다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI; //for Navigation
public class NavCtrl : MonoBehaviour
{
// Start is called before the first frame update
private NavMeshAgent agent;
private Transform monsterTr; //컴포넌트의 캐시를 처리할 변수
void Start()
{
agent = GetComponent<NavMeshAgent>();
agent.isStopped = true; //추적을 중지
monsterTr = GameObject.FindWithTag("MONSTER").GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(1)) {
if (agent.isStopped == true) {
agent.SetDestination(monsterTr.position); //목표설정
agent.isStopped = false; //추적을 개시
} else {
agent.isStopped = true; //추적을 중지
}
}
}
public void SetNavStop() {
agent.isStopped = true;
}
}
WASD키를 눌렀을때 Navigation을 멈추기 위해 다음 코드를 PlayerCtrl스크립트 PlayerAnim() 맨윗에 추가한다.
GetComponent<NavCtrl>().SetNavStop();로 NavCtrl의 SetNavStop에 접근할 수 있다. 스크립트 이름이 class이름이고 이걸 GetComponet<T>의 인수로 바로 사용할 수 있다.
주인공 캐릭터(Player)가 쏜 총알이 몬스터에 명중하면 몬스터가 피를 흘리는 혈흔 효과(Blood Effect)를 추가해보자. 혈은 효과는 파티클로 구현하며 프로젝트뷰의 03.Prefabs/EffecExamples/Blood/Prefabs폴더에 있는 BloodSprayeffect를 사용한다. 버전의 차이인지 발견하지 못해서 아래 EffectExamples > Goop Effects > Prefabs> GoopSpray를 사용하였다.
Resources 폴더
지금까지는 프리팹을 사용하기 위해 public GameObject xxx를 선언하고 인스펙터에서 연결해놓고 사용하였다. 이번에는 스크립트에서 런모드(동적)으로 로드하는 방법을 사용한다. 이방법을 쓰기 위해서는 프리팹이나 에셋이 Resouces 폴더안에 있어야 한다.
Assets root에 Resources 폴더를 하나 만들자. BloodSprayeffect나 GoopSpray를 끌어다 놓는다. 이 Resources폴더는 빌드할때 포함되므로 필요한 것들만 놓기 바랍니다.
MonsterCtrl 스크립트에 다음과 같이 추가한다. Goopspray효과는 피튀는게 좀 작아서 localScale을 변경해 크게했다
blood.transform.localScale = new Vector3(5, 5, 5); //효과가 작아서 확대했다.
효과의 인스턴스를 만드는데 인수가 4개이다 마지막 monsterTr의 Child로 만들겠다는 뜻이다.
MonsterActionState()에서는 state가 Trace라면 agent.SetDestination(playerTr.position);로 목표물을 지정해주고, agent.isStopped = false로 추적을 시작한다.; 다른 state에 대해서도 적절한 처리를 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI; //for Navigation
public class MonsterCtrl : MonoBehaviour
{
//몬스트 상태
public enum State { //인스펙터뷰에 콤보박스로 표시됨
IDLE, PATROL, TRACE, ATTACK, DIE
}
public State state = State.IDLE;
public float traceDist = 10.0f;
public float attackDist = 2.0f;
public bool isDie = false;
private Transform monsterTr;
private Transform playerTr;
private NavMeshAgent agent;
public Vector3 pPos, mPos;
void Start()
{
monsterTr= GetComponent<Transform>();
playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();
agent = GetComponent<NavMeshAgent>();
//agent.destination = playerTr.position;
StartCoroutine(CheckMonsterState());
StartCoroutine(MonsterAction());
}
IEnumerator CheckMonsterState() {
while(!isDie) {
pPos = playerTr.position;
mPos = monsterTr.position;
yield return new WaitForSeconds(0.3f);
//Player와 Monster간의 거리측정 스칼라값
float distance = Vector3.Distance(playerTr.position, monsterTr.position);
if(distance <= attackDist) {
state = State.ATTACK; //Attack로 상태변경
} else if(distance <= traceDist) {
state = State.TRACE; //Trace로 상태변경
} else {
state = State.IDLE; //Idle로 상태변경
}
}
}
IEnumerator MonsterAction() {
while (!isDie) {
switch(state) {
case State.IDLE:
agent.isStopped= true;
break;
case State.TRACE:
agent.SetDestination(playerTr.position);
agent.isStopped = false;
break;
case State.ATTACK:
break;
case State.DIE:
break;
}
yield return new WaitForSeconds(0.3f);
}
}
private void OnDrawGizmos() { //상태에 따라 기즈모 색깔 변경
if(state == State.TRACE) {
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(transform.position, traceDist);
}
if(state == State.ATTACK) {
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, attackDist);
}
}
}
실행하면서 Monster의 인스펙터의 MonsterCtrl 스크립트뷰를 보면 거리가 좁혀지면 IDEL이 TRACE로 바뀌면 Monster가 추적을 시작하고 더 가가워지면 ATTACK로 바뀐다. 막 도망가서 거리를 벌리면 다시 IDLE상태로 된다.
마지막으로 OnDrawGizmos()는 씬뷰에서만 보인다. 디버그용이다.
트레이스 범위에서 범위에 해당하는 파란색 구를 그려주고 공격범위에서는 범위 크기의 빨간색 구를 그려준다.
MonsterCtrl 스크립터의 코루틴 MonsterAction() 내부의 Trace와 Attack case문에 IsAttack 파라메터 부분을 넣어준다
IEnumerator MonsterAction() {
while (!isDie) {
switch(state) {
case State.IDLE:
agent.isStopped= true; //추적을 중지
anim.SetBool("IsTrace", false);
break;
case State.TRACE:
agent.SetDestination(playerTr.position); //목표설정
agent.isStopped = false; //추적을 재개
anim.SetBool("IsTrace", true);
anim.SetBool("IsAttack", false);
break;
case State.ATTACK: //공격상태
anim.SetBool("IsAttack", true);
break;
SetBool()은 파라메테명의 오자가 있으면 오류도 발생하고 호출될때 마다 해시테이블을 검색하므로 파라메터 해시값을 미리 추출해 전달하는 방식이 바람직하다. 우선 다음같이 스트링을 해시값으로 미리 바꿔 놓는다 "Hit"는 몬스터가 피격받을때 상태인데 미리 넣어두겠다
public readonly int hashTrace = Animator.StringToHash("IsTrace");
public readonly int hashAttack = Animator.StringToHash("IsAttack");
private readonly int hashHit = Animator.StringToHash("Hit");
코루팅 switch()문안의 스트링을 해시값으로 변경한다.
IEnumerator MonsterAction() {
while (!isDie) {
switch(state) {
case State.IDLE:
agent.isStopped= true; //추적을 중지
anim.SetBool(hashTrace, false);
break;
case State.TRACE:
agent.SetDestination(playerTr.position); //목표설정
agent.isStopped = false; //추적을 재개
anim.SetBool(hashTrace, true);
anim.SetBool(hashAttack, false); //공격파라메터 변경
break;
case State.ATTACK: //공격상태
anim.SetBool(hashAttack, true); //공격파라메터 변경
break;
실행해 보면 이제 몬스터가 걷고 공격한다. 이제부터는 몬스터가 피격받을때 취할 리액션을 붙여 넣겠다.
게임뷰탭옆 Ani탭을 누른후 Hit Trigger 파라메터를 하나 만든후 몬스터 모델에서 gothit을 끌어다 놓고 다음과 transition을 만든다.
Any->gothit transition은 HasExitTime 언체크, condition +를 누르고 Hit로 설정한다
gothit에서 idle 로 단방향 transition을 만든다 HasExitTime는 체크, ExitTime 0.9 isTrace false로 한다.
gothit에서 walk 로 단방향 transition을 만든다 HasExitTime는 체크, ExitTime 0.9 condition + IsTrace true, IsAttack false로 한다.
적 캐릭터가 장애물을 피해서 가는 걸 구현한다면 PathFinding같은 알고리즘이 필요하다. 유니티가 3D모델을 분석해 추적할 수 있는 내비게이션 기능을 제공하기 이전 많이 사용되었다. 유니티의 내비게이션 기능을 이용해 주인공을 추적하는 로직을 구현해 보자. 추적을 시작하면 walk애니메이션으로 변경하고 플레이어에 근접했을때 attack 애니메이션으로 변경되도록 메카님을 구성해본다.
네비게이션은 스테이즈를 구성하고 있는 3D메시(지형:Geometry)를 분석해 네비메시(NavMesh) 데이터를 미리 생성하는 방식이다. 추적할 수 있는 영역과 지나갈수 없는 영역을 미리 메시로 만들어 동작을 연출할 수 있다.
네비게이션 설정 - Navigation Static Flag
네비게이션을 만들(Bake)려면 그 대상이 무엇인지 지정해야 한다. 우선 바닥으로 사용되는 Floor의 static 옵션을 설정한다. Floor 인스펙터의 이름 오른쪽의 Static옆 삼각형을 눌러 Navigation Static만 체크한다.
Monster를 선택하고. window>AI>Navigation을 선택하면 Navigation뷰가 나오고 Bake를 누르고 마치면 파란색 영역이 만들어진다. 드럼통 주위는 파란색 메시로 채워지지 않고 구멍이 나있다. 이 영영읍 지나갈 수 없는 영억으로 인식해 추적한 대상이 장애물로 판단한다. Bake는 씬뷰에 플레이전 있는 것들만 인식한다. 동적으로 생성되는 것은 예측할수 없는 것 같다. 따라서 스테이지에 적용된 Barrel 스크립트를 제거한고 프리팹 Barrel을 여기저기 배치하고 Bake를 한번 더 누른다.
플레이어와 몬스터를 떨어트려 놓과 사이에 배치해주기 바란다. 몬스터가 피해가는지
NavMeshAgent 컴포넌트
NavMeshAgent 컴포넌트는 목표를 향해 움직일 때 서로를 피해가는 캐릭터 생성에 유용합니다. 에이전트는 내비메시를 이용하여 게임 월드에 대해 추론하고 서로 또는 기타 움직이는 장애물을 피할 방법을 이해하고 있습니다. 내비메시 에이전트의 스크립팅 API를 이용하여 경로를 찾거나 공간을 추론할 수 있습니다.
에이전트는 수직으로 서 있는 실린더에 의해 정의되며 실린더의 크기는Radius과Height프로퍼티에 의해 특정됩니다. 실린더는 오브젝트와 함께 움직이지만 오브젝트가 회전한다 해도 계속 수직으로 서 있습니다. 실린더의 모양은 다른 에이전트와 장애물간의 충돌 감지와 대응에 사용됩니다. 게임 오브젝트의 앵커 포인트가 실린더의 베이스에 없을 때 높이의 차이를 메우기 위해베이스 오프셋프로퍼티를 사용할 수 있습니다.
실린더의 높이와 반경은 실제로두 개의 다른 장소에 특정됩니다.내비메시 베이크 설정과 각 에이전트의 프로퍼티 입니다.
내비메시 베이크 설정은 내비메시 에이전트가 어떻게 정적인 월드 지오메트리와 충돌하고 또는 어떻게 회피하는지를 설명합니다. 메모리 여유량을 유지하고 CPU 로드를 지속적으로 체크하기 위해 오직 하나의 크기만이 베이크 설정에서 특정될 수 있습니다. -내비메시 에이전트 프로퍼티값은 에이전트가 움직이는 장애물 및 다른 에이전트와 어떻게 충돌하는지를 설명합니다.
대부분의 경우 두 장소 모두에 에이전트 크기를 똑같이 설정합니다. 하지만 예를 들어 크기가 큰 에이전트의 반경이 더 넓다면 다른 에이전트는 그 에이전트 주변에 공간을 더 많이 남겨둡니다. 하지만 그렇지 않다면 크기가 큰 에이전트도 마찬가지로 환경을 무시합니다.
하이라키뷰에서 Player를 선택하고 TAG에서 PLAYER를 생성하고 적용한다
Nave Mesh Agent는 인스펙터에서 추적대상을 연결할 수 없고 스크립트를 생성해야한다.
MonsterCtrl스크립트를 생성하자. Monster에 적용한다.
코드는 간단한다 다음한줄로 자동으로 추적한다. 다만 명령을 실행할 당시의 위치만이다. 명령실행후 플레이어가 움직이면 자동 갱신되지 않는다.
agent.destination = playerTr.position;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI; //for Navigation
public class MonsterCtrl : MonoBehaviour
{
private Transform monsterTr;
private Transform playerTr;
private NavMeshAgent agent;
void Start()
{
monsterTr= GetComponent<Transform>();
playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();
agent = GetComponent<NavMeshAgent>();
agent.destination = playerTr.position; //목표를 플레이어로 설정
}
}
잘 된다. 처음에는 PLAYER TAG를 Monster에도 설정했다가 뺑글뺑글 도는 걸 보고 풀어줬더니 잘되었다. PLAYER TAG는 Player에만
Monster는 NavMeshAgent가 미리 베이크된 NavMesh에 붙어서 이동하기 때문에 살짝 떠 있다. 방법은 NavMesh를 Bake할대 낮춰주던지 Monster NavMeshAgent의 BaseOffset을 -0.1로 낮춰주는 거다
메카님 리타게팅을 사용하려면 적용하려는 모델과 애니메이션 클립의 Type이 둘다 Humanoid로 설정해야 한다. 따라서 monster@Gangnam Style.fbx도 Rig탭에서 타입을 Humanoid로 바꾸고 apply한다. Animation탭을 누루고 Loop Time도 체크후 Apply해 준다.
같은 Animation탭 맨밑의 프리뷰창에 05.Model/Monster폴더의 Monster를 끌어다 놓고 Play시켜보면 춤을 추는걸 확인할수 있다.
Animator 컴포넌트
Monster모델을 씬뷰또는 하이라키로 끌어다 Player옆에 놓는다.
씬뷰에 추가된 Monster는 기본적으로 Animator가 추가되어 있다. 3D모델의 애니메이션 타입을 메카님으로 설정하면 Animator컴포넌트가 기본적으로 추가된다. 앞서 제작한 Player모델은 애니메이션 타입을 레거시로 설정했기 때문에 Animation 컴포넌트가 추가된것이다. Animator는 Animation을 컨트롤 하는 컴포넌트로 레가시는 Animation컴포넌트가 그 역활을 한다.
애니메이터 컨트롤러를 사용하여 캐릭터나 오브젝트의 애니메이션 클립 세트와 관련 애니메이션 전환을 정렬하고 관리할 수 있습니다. 대부분의 경우, 여러 애니메이션을 이용하여 게임 내에서 특정 상황이 일어났을 때 에셋을 바꿔가면서 사용합니다. 예를 들어 스페이스바를 누를 때마다 걷기 애니메이션 클립에서 점프 애니메이션 클립으로 전환할 수 있습니다. 하지만 애니메이션 클립이 하나만 있을 때에도 이 클립을 게임 오브젝트에 사용하기 위해서는 애니메이터 컨트롤러에 배치해야 합니다.
애니메이터 컨트롤러는 그 안에서 사용되는 애니메이션 클립에 대한 레퍼런스를 포함하고 있으며, 애니메이션 클립과 전환의 순서도 또는 Unity 에디터 내에서 비주얼 프로그래밍 언어로 작성된 간단한 프로그램이라고 생각할 수 있는상태 머신을 사용하여 다양한 애니메이션 클립과 각 클립 사이의 전환을 관리합니다. 상태 머신에 대한 자세한 내용은여기에서 확인할 수 있습니다.