2 분 소요

Intro

이번 포스팅에서는 플레이어가 몬스터주변에 다가갈때 몬스터가 플레이어를 인식하고, 쫓아가는 Chase 상태를 구현해보았습니다.

먼저, 몬스터가 플레이어를 탐지할 범위를 만들어주었습니다.

몬스터에 BoxCollider 컴포넌트를 추가한 뒤, 원하는 정도의 ScanRange 만큼 늘려줍니다.

그런다음 IsTrigger를 체크합니다.

image


또한 몬스터가 플레이어를 쫓을수 있도록 Nav Mesh Agent 컴포넌트를 추가합니다.

Ground 오브젝트를 선택한 뒤, Navigation Static 옵션을 체크, Navigation Area는 “Walkable”로 설정합니다.

이제 몬스터가 Ground 위에서 움직일 수 있습니다.

image

Monster.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

/*
            Monster 클래스는 몬스터의 공통데이터를 관리

 */
public class Monster : MonoBehaviour
{
    #region **Monster Stats**
    [Header("#Monster Stats")]
    public int maxHp;
    public int curHp;
    public float speed;
    public float maxDistance;                       // 플레이어와의 거리(복귀하기위한 최대거리)
    public float idleThreshold;                     // 복귀후 처음 위치와의 차이
    #endregion

    public Vector3 startPosition;                   // 몬스터의 첫 위치
    public bool isReset = false;                    // 원점으로 복귀했는지 여부

    protected PlayerController targetPlayer;        // 타깃 플레이어

    private BoxCollider scanRange;                  // 몬스터 탐지범위
    private BoxCollider hitBoxCol;                  // 몬스터 히트박스
    private Animator anim;                          // 몬스터 애니메이터
    private NavMeshAgent nav;                       // 몬스터 네비게이션

    #region ** Properties **

    public Animator Anim => anim;
    public NavMeshAgent Nav => nav;
    public PlayerController Target => targetPlayer;
    #endregion

    protected void Awake()
    {
        Init();
    }
    
    private void Init()
    {
        targetPlayer = GameManager.Instance.player;
        startPosition = transform.position;

        anim = GetComponent<Animator>();
        nav = GetComponent<NavMeshAgent>();
        hitBoxCol = GetComponent<BoxCollider>();
        scanRange = GetComponent<BoxCollider>();
    }
}

  • 방금 추가한 BoxCollider와 NavMeshAgent를 가져옵니다.
  • 처음 위치를 기억할 변수 startPosition을 현재 위치로 초기화합니다.
  • idleThreshold 변수는 플레이어를 쫓다가 원점으로 복귀할 때 첫위치와의 오차범위입니다.
  • 플레이어를 쫓다가 처음위치로 복귀가 완료되면 isReset 변수를 true로 바꾸어 복귀했음을 알 수 있도록했습니다.


TurtleShell.cs

    private void InitValues()
    {
        // TurtleShell 능력치 초기화
        maxHp = 100;
        curHp = maxHp;
        speed = 1f;
        maxDistance = 5f;
        idleThreshold = 0.3f;

        // 초기상태는 Idle
        curState = States.Idle;
        // StateMachine 객체 생성(Idle상태)
        stateMachine = new StateMachine<TurtleShell>(new IdleState<TurtleShell>(this));

        Nav.speed = speed;
    }

    private void Update()
    {
        stateMachine.curState.OnStateUpdate();

        if (isReset)
        {
            ChangeState(States.Idle);
            isReset = false;
        }
    }

    // Scan Range에 플레이어가 들어왔을경우
    private void OnTriggerEnter(Collider other)
    {
        if (!other.CompareTag("Player"))
            return;

        ChangeState(States.Chase);
    }
  • 새로 추가된 변수들을 초기화해주었습니다.
  • OnTriggerEnter() 함수에서 Player가 범위내에 들어왔는지 확인후 Chase 상태로 진입하게됩니다.
  • Update() 함수에서는 Chase 상태에서 플레이어를 쫓다가 처음위치로 되돌아왔음을 확인하면 Idle 상태로 진입한뒤, isReset을 false로 바꿔줍니다.
    • isReset이 true이면 계속해서 로직이 실행되기때문입니다.


ChaseState.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/*
                Monster State - Chase (추격상태)

        - 추격 애니메이션 설정(Walk)
        - Chase 상태진입시, Player를 쫓음
            - Player와 일정거리(maxDistance)이상 멀어질경우 원래자리(startPosition)로 되돌아감
            - 되돌아가는 위치가 원점과 근접할경우 isReset = true로 복귀했음을 알림
 */
public class ChaseState<T> : BaseState<T> where T : Monster
{
    public ChaseState(T monster) : base(monster) { }

    public override void OnStateEnter()
    {
        monster.Anim.SetBool("Walk", true);

    }

    public override void OnStateUpdate()
    {
        // Target 추격
        monster.Nav.SetDestination(monster.Target.transform.position);

        float distanceToPlayer = Vector3.Distance(monster.transform.position, monster.Target.transform.position);

        // 일정거리이상 벌어지면 원래자리로 복귀
        if (distanceToPlayer > monster.maxDistance)
        {
            monster.Nav.SetDestination(monster.startPosition);

            if(Vector3.Distance(monster.transform.position, monster.startPosition) <= monster.idleThreshold)
            {
                monster.isReset = true;
            }
        }
    }

    public override void OnStateExit()
    {
        monster.Anim.SetBool("Walk", false);
    }
}


이제 플레이어가 ScanRange 안에 들어갈경우 몬스터는 플레이어를 쫓게되며, 플레이어와 일정거리이상 멀어지게되면

몬스터는 자동으로 원래자리로 돌아가도록했습니다.

원래자리로 돌아간뒤에는 Idle 상태로 진입하게됩니다.

테스트

image

댓글남기기