Patrol AI

Eunho Bae·2022년 5월 24일

코드

public class MonsterController : CreatureController
{
    Coroutine _coPatrol;
    Vector3Int _destCellPos;

    public override CreatureState State
    {
        get { return _state; }
        set
        {
            if (_state == value)
                return;

            base.State = value;

            if (_coPatrol != null)
            {
                StopCoroutine(_coPatrol);
                _coPatrol = null;
            }
        }
    }

    protected override void Init()
    {
        base.Init();
        State = CreatureState.Idle;
        Dir = MoveDir.None;
    }

    protected override void UpdateIdle()
    {
        base.UpdateIdle();   

        if(_coPatrol == null)
        {
            _coPatrol = StartCoroutine("CoPatrol");
        }
    }

    protected override void MoveToNextPos()
    {
        Vector3Int moveCellDir = _destCellPos - CellPos;

        if (moveCellDir.x > 0)
            Dir = MoveDir.Right;
        else if (moveCellDir.x < 0)
            Dir = MoveDir.Left;
        else if (moveCellDir.y > 0)
            Dir = MoveDir.Up;
        else if (moveCellDir.y < 0)
            Dir = MoveDir.Down;
        else
            Dir = MoveDir.None;

        Vector3Int destPos = CellPos;
        switch (Dir)
        {
            case MoveDir.Up:
                destPos += Vector3Int.up;
                break;
            case MoveDir.Down:
                destPos += Vector3Int.down;
                break;
            case MoveDir.Left:
                destPos += Vector3Int.left;
                break;
            case MoveDir.Right:
                destPos += Vector3Int.right;
                break;
        }

        if (Managers.Map.CanGo(destPos) && Managers.Obj.Find(destPos) == null)
        {
            CellPos = destPos;
        }
        else
        {
            State = CreatureState.Idle;
        }
    }

    public override void OnDamanged()
    {
        GameObject effect = Managers.Resource.Instantiate("Effect/DieEffect");
        effect.transform.position = transform.position;
        effect.GetComponent<Animator>().Play("DieStart");
        GameObject.Destroy(effect, 0.5f);

        Managers.Obj.Remove(gameObject);
        Managers.Resource.Destroy(gameObject);
    }

    IEnumerator CoPatrol()
    {
        int waitSeconds = Random.Range(1, 4);
        yield return new WaitForSeconds(waitSeconds);

        for (int i = 0; i < 10; i++)
        {
            int xRange = Random.Range(-5, 6);
            int yRange = Random.Range(-5, 6);
            Vector3Int randPos = CellPos + new Vector3Int(xRange, yRange, 0);

            if (Managers.Map.CanGo(randPos) && Managers.Obj.Find(randPos) == null)
            {
                _destCellPos = randPos;
                State = CreatureState.Moving;
                yield break;
            }
        }

        State = CreatureState.Idle;

    }
}

몬스터가 생성되고 CreatureState가 Idle일 때 코루틴 함수 CoPatrol이 실행되고 _coPatrol에 코루틴이 반환된다. 그러면 1~4초 마다 for문의 로직이 실행되는데 현재 위치에서 x,y 각각 -5~6이 더해진 위치가 갈 수 있는지 여부를 체크하고 만약 갈 수 있으면 도착 위치를 갱신하고 yield break 명령어를 통해 코루틴 함수를 그 시점에서 중단시킨다.

만약 for문을 10번 반복할 동안 갈 수 있는 위치를 찾지 못했으면 그대로 for문을 빠져나와 State를 Idle로 바꿔버린다.
이때 State는 CreatureController에서 virtual로 선언한 것을 override한 것인데 _coPatrol이 null이 아니라면 코루틴을 중지시키고 null로 갱신시켜 다시 CreatureController에서 UpdateController()가 호출될 때 UpdateIdle을 실행시키도록 하였다.


참고

  • AI는 클라이언트에서 처리하지 않고 서버에 위임하여 실질적으로 변화만 클라이언트에 통보하는 식으로 구현된다.
profile
개인 공부 정리

0개의 댓글