주인공 캐릭터의 hp가 0 이하일때는 죽는 애니메이션을 실행하고 나서 Game Over화면으로 넘어가게 된다.  계속 공격하는 몬스터에게 주인공의 죽음을 알릴 필요가 있다. 몬스터는 공격을 멈추고 추적도 정지하게 된다.

씬뷰의 오브젝트에 접근하는 방법중 Tag를 이용하는 방법이 있다.

Tag로 설정된 오브젝트를 하나만 리턴하는 함수와 여러개를 배열로 리턴하는 2가지 함수가 마련되어 있다.

GameObject.FindGameObjectWithTag(string tag);
GameObject.FindGameObjectsWithTag(string tag);

MOSTER라는 태그를 새로 추가하고 Monster의 태그로 지정한다. 없다면 +를 눌러 만든후 지정해 준다.

Player가 사망했을때 호출되는 PlayerCtrl스크립트의 PlayerDie함수에서 모든 몬스터를 찾아 공격 중지 함수를 호출하는 코드를 다음과 같이 추가한다.

PlayerCtrl - PlayerDie()

SendMessage() 함수는 "OnPlayDie()"가 해당 게임오브젝트의 스크립트에 있다면 실행하라는 거다. 두번째 인자는 함수가 없더라도 없다는 메시지를 반환하지 않겠다는 옵션이다. 

void PlayerDie() {
    //MONSTER 태그를 가진 모든 게임오브젝트를 찾아옴
    GameObject[] monsters = GameObject.FindGameObjectsWithTag("MONSTERS");
    foreach(GameObject monster in monsters) {  //모든 오브젝트를 순차적으로 불러옴
        monster.SendMessage("OnPlayerDie", SendMessageOptions.DontRequireReceiver);
    }
}

이제 MonsterCtrl에 - OnPlayerDie()함수를 추가하겠다.

private readonly int hashPlayerDie = Animator.StringToHash("PlayerDie");

void OnPlayerDie() {
    StopAllCoroutines();
    agent.isStopped= true;
    anim.SetTrigger(hashPlayerDie);
}

MonsterAnim을 더블클릭해 애니메이터를 열고 07.animations폴더에서 Monster@Gangnam Style애니메이션 파일을 드래그에 추가한다. 파라미터 +를 누르고 PlayerDie라는 이름의 Trigger를 만든다. Gangnam Style애니메이션은 주인공이 사망하는 경우에 실행할 것이므로 Any State에서 Gangnam Style 스테이트로 Transition을 연결하고 Gangnam Style에서 Exit로 가는 Transition도 연결한다.

 

 

Gangnam Style state를 한번만 클릭해서 인스펙터에서 Multiplier Parameter를 체크하면 자동으로 Speed가 생긴다

MonsterCtrl 스크립터에 아래 코드를 추가한다.

애니메이터의 Speed 변수를 랜덤으로 설정하고 Any->GangnamStyle transition을 트리거 시킨다.

private readonly int hashSpeed = Animator.StringToHash("Speed");

void OnPlayerDie() {
    StopAllCoroutines();
    agent.isStopped= true;
    anim.SetFloat(hashSpeed, Random.Range(0.5f, 1.2f));
    anim.SetTrigger(hashPlayerDie);
}

하이라키의 몬스터를 프로젝트 프리팹 폴더로 끌어와 프리팹을 만든다. 몬스터 프리팹을 끌어다 씬뷰에 여러개 배치한다.

다가가면 몬스터가 공격을 하고 HP가 0이되면 몬스터들이 승리의 강남스타일을 춤춘다.

처음에 잘안되서 디버깅 해보니 몬스트의 TAG는 MONSTER지만 wrist는 PUNCH로 되어 있어야 HP가 떨어진다.

만일 잘못되어 있다면 하이라키의 몬스터가 아니라 이제는 프로젝트 프리팹의 몬스터 프리팹을 수정해야한다.

 

 

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;  //for Navigation


public class MonsterCtrl : MonoBehaviour
{
    //몬스트 상태 
    public enum State {  //인스펙터뷰에 콤보박스로 표시됨
        IDLE, PATROL, TRACE, ATTACK, DIE ,DANCE
    }

    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;
    private Animator anim;

    public readonly int hashTrace = Animator.StringToHash("IsTrace");
    public readonly int hashAttack = Animator.StringToHash("IsAttack");
    private readonly int hashHit = Animator.StringToHash("Hit");
    private readonly int hashPlayerDie = Animator.StringToHash("PlayerDie");
    private readonly int hashSpeed = Animator.StringToHash("Speed");
    private GameObject bloodEffect;

    void Start()
    {
        monsterTr= GetComponent<Transform>();
        playerTr = GameObject.FindWithTag("PLAYER").GetComponent<Transform>();  
        agent = GetComponent<NavMeshAgent>();
        anim = GetComponent<Animator>();
        bloodEffect = Resources.Load<GameObject>("GoopSpray");
        StartCoroutine(CheckMonsterState());
        StartCoroutine(MonsterAction());
        
    }

    //일정한 간격으로 몬스터의 행동 상태를 체크
    IEnumerator CheckMonsterState() {
        while(!isDie) {
            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) {
            transform.LookAt(playerTr);
            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;
                case State.DIE: //사망상태
                    break;

            }
            yield return new WaitForSeconds(0.3f);
        }
    }
    private void OnCollisionEnter(Collision coll) {
        if (coll.collider.CompareTag("BULLET")) {
            Destroy(coll.gameObject);             //충돌한 총알을 삭제
            anim.SetTrigger(hashHit);  //피격 리액셔  애니메이션 실행
            Vector3 pos = coll.GetContact(0).point;
            Quaternion rot = Quaternion.LookRotation(-coll.GetContact(0).normal);
            ShowBloodEffect(pos, rot);  
        }
    }
    void ShowBloodEffect(Vector3 pos, Quaternion rot) {
        GameObject blood = Instantiate<GameObject>(bloodEffect, pos, rot, monsterTr);
        blood.transform.localScale = new Vector3(5, 5, 5);  //효과가 작아서 확대했다.
        Destroy(blood, 1.0f);
    }
    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);
        }
    }
    void OnPlayerDie() {
        Debug.Log("PlayerDie");
        StopAllCoroutines();
        agent.isStopped= true;
        anim.SetFloat(hashSpeed, Random.Range(0.5f, 1.2f));
        anim.SetTrigger(hashPlayerDie);
    }
}

 

+ Recent posts