Chapter 7-8. AI : 공격하는 동물
카테고리: Unity Lesson 3
태그: Unity Game Engine
인프런에 있는 케이디님의 [유니티 3D] 실전! 생존게임 만들기 - Advanced 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click
Chapter 7. 동물들의 공격형 AI, 도망형 AI 구현
🚖 공격하는 동물
- 📜Animal
    - 시야각 내에서 플레이어를 발견했다면
        - 📜WeakAnimal 👉 Run
            - 플레이어의 반대 방향으로 도망간다.
- 📜Pig.cs
 
- 📜StrongAnimal 👉 Chase
            - 플레이어 방향으로 플레이어를 따라와 추격한다.
                - 플레이어를 추격 중이다가 가까이 있고 + 시야 내에도 있게 될 때 공격한다.
 
- 📜Lion.cs
 
- 플레이어 방향으로 플레이어를 따라와 추격한다.
                
 
- 📜WeakAnimal 👉 Run
            
 
- 시야각 내에서 플레이어를 발견했다면
        
공격 애니메이션


사자의 공격 애니메이션 만듬.
📜Animal.cs
    protected bool isAttacking; // 공격중인지 판별
    protected FieldOfViewAngle theFieldOfViewAngle;
    protected StatusController thePlayerStatus;
    protected void Start()
    {
        currentTime = waitTime;   // 대기 시작
        isAction = true;   // 대기도 행동
        theAudio = GetComponent<AudioSource>();
        nav = GetComponent<NavMeshAgent>();
        theFieldOfViewAngle = GetComponent<FieldOfViewAngle>();
        thePlayerStatus = FindObjectOfType<StatusController>();
    }
    protected void ElapseTime()
    {
        if (isAction)
        {
            currentTime -= Time.deltaTime;
            if (currentTime <= 0 && !isChasing && !isAttacking)  // 랜덤하게 다음 행동을 개시
                ReSet();
        }
    }
- 공격 중일 땐 다음 행동을 개시하지 않도록 한다.
    - 공격이 가능한 상황일 땐 계속 공격을 이어나가야 하므로.
 
- 공격하면 플레이어의 HP, DP를 깎기 위해 📜StatusController 로딩
📜StrongAnimal.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StrongAnimal : Animal
{
    [SerializeField]
    protected int attackDamage; // 공격 데미지
    [SerializeField]
    protected float attackDelay; // 공격 딜레이
    [SerializeField]
    protected float attackDistance; // 공격 가능한 사정 거리. 이 안에 플레이어가 들어오면 가까이 있다고 판단.
    [SerializeField]
    protected LayerMask targetMask; // 플레이어 레이어가 할당 될 것.
    [SerializeField]
    protected float chaseTime;  // 총 추격 시간
    protected float currentChaseTime;  
    [SerializeField]
    protected float chaseDelayTime; // 추격 딜레이
    public void Chase(Vector3 _targetPos)
    {
        destination = _targetPos;
        isChasing = true;
        isWalking = false;
        isRunning = true;
        nav.speed = runSpeed;
        anim.SetBool("Running", isRunning);
        if(!isDead)
          nav.SetDestination(destination);
    }
    public override void Damage(int _dmg, Vector3 _targetPos)
    {
        base.Damage(_dmg, _targetPos);
        if (!isDead)
            Chase(_targetPos);
    }
    protected IEnumerator ChaseTargetCoroutine()
    {
        currentChaseTime = 0;
        Chase(theFieldOfViewAngle.GetTargetPos());
        while (currentChaseTime < chaseTime)
        {
            Chase(theFieldOfViewAngle.GetTargetPos());
            // 플레이어와 충분히 가까이 있고 
            if (Vector3.Distance(transform.position, theFieldOfViewAngle.GetTargetPos()) <= attackDistance)
            {
                if (theFieldOfViewAngle.View())  // 눈 앞에 있을 경우
                {
                    Debug.Log("플레이어 공격 시도");
                    StartCoroutine(AttackCoroutine());
                }
            }
            yield return new WaitForSeconds(chaseDelayTime);
            currentChaseTime += chaseDelayTime;
        }
        isChasing = false;
        isRunning = false;
        anim.SetBool("Running", isRunning);
        nav.ResetPath();
    }
    protected IEnumerator AttackCoroutine()
    {
        isAttacking = true;
        // 공격은 제자리에서 이루어져야 함.
        nav.ResetPath();
        currentChaseTime = chaseTime;
        // 공격은 대상을 바라보고 하도록. (0.5초 대기한 후)
        yield return new WaitForSeconds(0.5f);
        transform.LookAt(theFieldOfViewAngle.GetTargetPos());
        // 공격 애니메이션이 어느정도 진행된 후 데미지를 입히도록 대기
        anim.SetTrigger("Attack");
        yield return new WaitForSeconds(0.5f);
        RaycastHit _hit;
        if (Physics.Raycast(transform.position + Vector3.up, transform.forward, out _hit, attackDistance, targetMask))
        {
            Debug.Log("플레이어가 적중!!");
            thePlayerStatus.DecreaseHP(attackDamage);
        }
        else
        {
            Debug.Log("플레이어 빗나감!!");
        }
        yield return new WaitForSeconds(attackDelay);
        isAttacking = false;
        StartCoroutine(ChaseTargetCoroutine());
    }
}
추격과 공격 실제 처리를 📜StrongAnimal 로 옮겼다.
- ChaseTargetCoroutine() 추격 코루틴
    - 추격하다가 1️⃣ 플레이어와 충분히 가까이 있고 + 2️⃣ 눈 앞에 있을 경우
        - 플레이어를 공격한다.
 
 
- 추격하다가 1️⃣ 플레이어와 충분히 가까이 있고 + 2️⃣ 눈 앞에 있을 경우
        
- AttackCoroutine() 공격 코루틴
    - isAttackingTrue 👉 공격 중일땐 다음 랜덤 행동을 개시하지 않도록
- 공격은 제자리에서 이루어지도록 한다.
        - 목적지 추적 리셋
- currentChaseTime를- chaseTime로 만들어 ChaseTargetCoroutine 코루틴의 실제 추적 처리가 이루어지지 않도록 한다.- 코루틴 함수는 병행하여 실행되므로 병렬적으로 ChaseTargetCoroutine가 실행될 수 있기 때문. 공격할 땐 추적을 멈춰야 한다.
 
 
- 공격은 플레이어를 바라보고 하도록 한다.
- 공격 애니메이션이 어느정도 진행한 후 데미지를 입힐 수 있도록 공격 애니메이션 재생시킨 후 0.5초 대기
- 이제 공격을 시도한다.
        - 공격 애니메이션 도중에 플레이어에게 닿았다면
            - 플레이어의 HP 를 깎는다.
 
- 공격 애니메이션 도중에 플레이어에게 닿지 않았다면
            - 이건 공격이 빗나가 실패한 것
 
 
- 공격 애니메이션 도중에 플레이어에게 닿았다면
            
- 이렇게 공격 과정을 한번 할때마다 attackDelay만큼 대기 시간을 가진다.
- isAttackingFalse
- 다시 플레이어를 추적
 
- Chase(Vector3 _targetPos)
    - 사자가 죽으면 더 이상 플레이어를 추격하지 않도록.. (코루틴에 의해서 플레이어가 사라지거나 해도 한동안 추적을 계속 하기 때문에 이 처리를 안해주면 사자가 죽은 상태에서도 플레이어 따라다녔다 ㅠㅠ)
        if(!isDead) nav.SetDestination(destination);
 
- 사자가 죽으면 더 이상 플레이어를 추격하지 않도록.. (코루틴에 의해서 플레이어가 사라지거나 해도 한동안 추적을 계속 하기 때문에 이 처리를 안해주면 사자가 죽은 상태에서도 플레이어 따라다녔다 ㅠㅠ)
        

🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
 
      
    
댓글 남기기