3 분 소요

AI에서의 첫번째가 주어진 명령을 끝까지 수행하는 것이라면

두번째는 각각의 상황에서 선택의 순서를 잘 만들어 주는 것이다.

  • int x = Random.Range(0,2): 0과1만 나옴/ 이상~미만
  • float x = Random.Range(0,2): 0,1,2가나옴 / 이상~이하

   public eCharacter _eCh;
    public float _minTime = 2;
    public float _maxTime = 7;
    float _timeWait;
    bool _isSelectAi;
    int _characterStd;


    public enum eCharacter
    {
        Fierce = 0,
        lazy
    }


void Update()
{
        switch (_stateAction)
        {
            case eActionState.IDLE:
                _timeWait -= Time.deltaTime;    //시간이 깎여나가야함
                if(_timeWait< 0) //기다리는 시간이 다 지나면
                {
                    //다시 선택
                    _isSelectAi = false;

                }
                break;
            case eActionState.WALK:
                if (Vector3.Distance(_posTarget, transform.position) <= 0.1f)
                {
                    //다시선택
                    _isSelectAi = false;
                }
                break;
            case eActionState.RUN:
                if (Vector3.Distance(_posTarget, transform.position) <= 0.1f)
                {
                    //다시선택
                    _isSelectAi = false;
                }
                break;
        }

        ProceesAI();
}


    void ProceesAI()
    {
        if (!_isSelectAi)
        {
            int r = Random.Range(0, 100);      //(이상,미만: 0, 1만 나옴)

            if (r <10)
            {   //IDLE
                ChangedAction(eActionState.IDLE);
                _timeWait = Random.Range(_minTime, _maxTime);

            }
            else if(r<_characterStd)
            {   //WALK
                ChangedAction(eActionState.WALK);
                _posTarget = GetRandomPos(_startPos, _limitX, _limitZ);
                _navAgent.destination = _posTarget;
            }

            else if (r > _characterStd)
            {
                ChangedAction(eActionState.RUN);
                _posTarget = GetRandomPos(_startPos, _limitX, _limitZ);
                _navAgent.destination = _posTarget;
            }
            _isSelectAi = true;
        }
        
    }

  • 랜덤한 숫자를 통해 좀비가 대기,걷기,뛰기의 상태로 변하고,
  • 특별히 2가지 성격을 통해 걷는 좀비, 뛰는좀비로 나눌수도 있음
  • 대기하는 시간을 조절가능

최종

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

public class ZombieController : MonoBehaviour
{
    public enum eActionState
    {
        IDLE              =0,
        WALK,
        RUN,
        ATTACK,
        HIT,
    }

    public enum eCharacter
    {
        Fierce = 0,
        lazy
    }

    [SerializeField] float _limitX = 5;
    [SerializeField] float _limitZ = 5;
    [SerializeField] float _moveSpeed;   //뛰는 스피드
    [SerializeField] float _walkSpeed;  //걷는 스피드
    [SerializeField] BoxCollider _damageZone;
    
    public eCharacter _eCh;
    public float _minTime = 2;
    public float _maxTime = 7;

    eActionState _stateAction;
    Animator _ctrlAni;
    Vector3 _posTarget;
    float _Speed;       //최종 스피드
    bool _isDeath;   //플래그 (키고 끄고 할수있는 작용 
    bool _isAttack;
    NavMeshAgent _navAgent;
    Vector3 _startPos;          //cube상에서의 중심 위치 = center
    float _timeWait;
    bool _isSelectAi;
    int _characterStd;

    private void Awake()
    {
        _isDeath = false;   //초기화
        _isAttack = false;  //초기화
        _ctrlAni = GetComponent<Animator>();
        _navAgent = GetComponent<NavMeshAgent>();   // 좀비에 있는 navmeshagent를 가져옴
        _startPos = _posTarget = transform.position;
        _timeWait = 0;
        _isSelectAi = false;
        _characterStd= 0;
    }
    void Start()
    {
        switch (_eCh)
        {
            case eCharacter.Fierce:
                _characterStd = 25;
                break;
            case eCharacter.lazy:
                _characterStd = 80;
                break;
        }

    }

    private void Update()
    {
        if (_isDeath)
            return;



        switch (_stateAction)
        {
            case eActionState.IDLE:
                _timeWait -= Time.deltaTime;    //시간이 깎여나가야함
                if(_timeWait< 0) //기다리는 시간이 다 지나면
                {
                    //다시 선택
                    _isSelectAi = false;

                }
                break;
            case eActionState.WALK:
                if (Vector3.Distance(_posTarget, transform.position) <= 0.1f)
                {
                    //다시선택
                    _isSelectAi = false;
                }
                break;
            case eActionState.RUN:
                if (Vector3.Distance(_posTarget, transform.position) <= 0.1f)
                {
                    //다시선택
                    _isSelectAi = false;
                }
                break;
        }

        ProceesAI();
    }

    void ProceesAI()
    {
        if (!_isSelectAi)
        {
            int r = Random.Range(0, 100);      //(이상,미만: 0, 1만 나옴)

            if (r <10)
            {   //IDLE
                ChangedAction(eActionState.IDLE);
                _timeWait = Random.Range(_minTime, _maxTime);

            }
            else if(r<_characterStd)
            {   //WALK
                ChangedAction(eActionState.WALK);
                _posTarget = GetRandomPos(_startPos, _limitX, _limitZ);
                _navAgent.destination = _posTarget;
            }

            else if (r > _characterStd)
            {
                ChangedAction(eActionState.RUN);
                _posTarget = GetRandomPos(_startPos, _limitX, _limitZ);
                _navAgent.destination = _posTarget;
            }
            _isSelectAi = true;
        }
        
    }

    Vector3 GetRandomPos(Vector3 center, float limitX, float limitZ)            //좀비가 움직일 임의의 위치를 반환
    {
         float rX = Random.Range(-limitX, limitX);              //센터를 중심으로 -5, +5 사이에서 x좌표 랜덤값 (이상,이하)
         float rZ = Random.Range(-limitZ, limitZ);              //센터를 중심으로 -5, +5 사이에서 z좌표 랜덤값

        Vector3 rv = new Vector3(rX,0,rZ);
        return center+ rv;      //center가 있어야 cube에서의 중심점에서 x,z에 따라 움직임
    }

    void ChangedAction(eActionState state)
    {

        switch (state)                                  //애니메이션이 바뀌는 곳에서 속도를 변경 해주는게 직관적임
        {
            case eActionState.WALK:         //walk일때 최종스피드에다가 워크스피드를 넣어줌
                if (_stateAction != eActionState.ATTACK)
                {
                    _navAgent.speed = _walkSpeed;
                    _ctrlAni.SetInteger("AniState", (int)_stateAction);
                    _stateAction = state;
                }
                break;
            case eActionState.RUN:
                _navAgent.speed = _moveSpeed;
                _ctrlAni.SetInteger("AniState", (int)_stateAction);
                _stateAction = state;
                break;
            case eActionState.ATTACK:       //모든것이 변하는 곳에 공격할때는 isAttack을 킴
                if (_stateAction == eActionState.RUN)
                {
                    _isAttack = true;
                    _ctrlAni.SetInteger("AniState", (int)_stateAction);
                    _stateAction = state;

                }
                break;
            case eActionState.HIT:      //맞으면 isDeath를 킴
                _isDeath = true;
                _ctrlAni.SetInteger("AniState", (int)_stateAction);
                _stateAction = state;
                break;
            case eActionState.IDLE:
                _ctrlAni.SetInteger("AniState", (int)_stateAction);
                _stateAction = state;
                break;

        }

        _stateAction = state;
        _ctrlAni.SetInteger("AniState", (int)_stateAction);
    }

    void DamageOn()
    {
        _damageZone.enabled = true;
    }

    void DamageOff()
    {
        _damageZone.enabled = false;
    }
}

댓글남기기