Unity

[TIL] Unity 최종프로젝트 몬스터 애니메이션(Animation Event)

unihee1 2025. 4. 21. 20:28

2025.04.16 - [Unity] - [TIL] Unity 최종프로젝트 몬스터 HP바 만들기

 

[TIL] Unity 최종프로젝트 몬스터 HP바 만들기

2025.04.11 - [Unity] - [TIL] Unity 최종프로젝트 몬스터 AI 구현하기(오브젝트풀)_ 4일차 [TIL] Unity 최종프로젝트 몬스터 AI 구현하기(오브젝트풀)_ 4일차2025.04.10 - [Unity] - [TIL] Unity 최종프로젝트 몬스터 AI

unihee1.tistory.com

이제 이전에 임시로 적용했던 에셋말고 최종프로젝트 게임에 맞는 로우폴리 에셋을 적용해줄 것이다.

또한 여러가지 애니메이션을 추가하여 좀 더 생동감 있게 구현할 것이다.

오늘은 몬스터가 Chase 상태에 진입하기 전에 소리를 지르는 애니메이션을 재생하여 위압감을 줄 것이다.

 

1.  Animation Event 사용하기

- Animation Event란?

애니메이션 클립의 특정 시점에 스크립트의 함수를 호출할 수 있게 해주는 기능으로 애니메이션과 코드 사이의 타이밍을 정밀하게 조절할 수 있게 해준다.

 

- Animation Event 추가하기

Animation Event를 추가할 애니메이션을 두번 클릭하여 Animation 창을 열어준다.

그런 다음 애니메이션이 끝나는 지점을 한 번 클릭하고 우클릭하여 Add Animation Event를 눌러준다.

 

Animation Event를 추가하고 나면 위의 사진과 같이 인스펙터 창에 나타난다.

Function 부분에 Event 함수명을 지정해준다.

이제 Function에서 지정한 함수명을 구현해줄 차례다.

 

- MonsterController 스크립트 

using UnityEngine;
using UnityEngine.AI;

public class MonsterController : MonoBehaviour
{
    [Header("Data")]
    [SerializeField] private MonsterData originalData;
    public MonsterData Data { get; private set; }

    [Header("Components")]
    public NavMeshAgent nav;
    private Rigidbody rigid;
    public Animator animator;
    public MonsterHPUI hpUI;

    private StateMachine stateMachine;
    private TakeDamageState takeDamageState;

    public Transform target { get; private set; }

    private void OnEnable()
    {
        Data = Instantiate(originalData);
        Data.monsterHP = Data.maxHP;

        RefreshHealthUI();

        if (stateMachine == null)
        {
            stateMachine = new StateMachine();
            takeDamageState = new TakeDamageState(this, stateMachine);
        }

        stateMachine.ChangeState(new IdleState(this, stateMachine));
    }

    private void Start()
    {
        nav = GetComponent<NavMeshAgent>();
        rigid = GetComponent<Rigidbody>();
        animator = GetComponent<Animator>();
        hpUI = GetComponentInChildren<MonsterHPUI>();

        target = GameObject.FindWithTag("Test")?.transform;

        if (Data != null)
        {
            nav.speed = Data.monsterRunSpeed;
        }
    }

    private void Update()
    {
        stateMachine?.Update();
    }

    private void FixedUpdate()
    {
        FreezeVelocity();
    }

    private void FreezeVelocity()
    {
        rigid.velocity = Vector3.zero;
        rigid.angularVelocity = Vector3.zero;
    }

    public void ApplyDamage(float damage)
    {
        takeDamageState.SetDamage(damage);
        stateMachine.ChangeState(takeDamageState);
    }

    public void RefreshHealthUI()
    {
        if (hpUI != null)
        {
            hpUI.UpdateHP(Data.monsterHP);
        }
    }
    public void OnScreamEnd()
    {
        stateMachine.ChangeState(new ChaseState(this, stateMachine));
    }
}

Animation Event는 애니메이션이 끝나는 지점에 생성했기 때문에 OnScream 함수 안에 애니메이션이 끝나면 Chase상태로 변환될 수 있게 구현하였다. 

 

- IdleState 스크립트

using UnityEngine;

public class IdleState : IState
{
    [SerializeField] private MonsterController monster;
    [SerializeField] private StateMachine stateMachine;
    private float idleTime = 10f;
    private float timer;

    public IdleState(MonsterController monster, StateMachine stateMachine)
    {
        this.monster = monster;
        this.stateMachine = stateMachine;
    }
    public void Enter()
    {
        monster.animator.SetBool("isIdle", true);
        timer = 0f;
    }    
    public void Update()
    {
        timer += Time.deltaTime;
        float distanceToTarget = Vector3.Distance(monster.transform.position, monster.target.position);
        if (distanceToTarget < monster.Data.monsterAttackRange)
        {
            stateMachine.ChangeState(new AttackState(monster, stateMachine));
        }
        else if (distanceToTarget < monster.Data.monsterDetectRange)
        {
           
            monster.animator.SetTrigger("isScream");
        }
        else if (timer > idleTime)
        {
            stateMachine.ChangeState(new WanderState(monster, stateMachine));
        }
    }
    public void Exit()
    {
        monster.animator.SetBool("isIdle", false);
    }

}

그럼 이제 기존에 있던 Chase상태로 변환하는 코드를 삭제해주고 그자리에 소리 지르는 애니메이션을 재생할 수 있게 

monster.animator.SetTrigger("isScream");을 추가해주면 애니메이션이 재생되고 재생이 끝나면 MonsterController의 OnScream 함수를 통해 Chase 상태로 진입할 수 있다.

 

2. 실행해보기

몬스터가 소리 지르는 애니메이션이 끝나고 플레이어를 쫓아가는 것을 확인할 수 있다.

 

※ 애니메이션: 믹사모_ Zombie Scream, Zombie Idle, Zombie Run

※ 몬스터 에셋: POLYGON City Zombies - Low Poly 3D Art by Synty | Characters | Unity Asset Store