Chapter 7-8. AI : 공격하는 동물

Date:     Updated:

카테고리:

태그:

인프런에 있는 케이디님의 [유니티 3D] 실전! 생존게임 만들기 - Advanced 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click

Chapter 7. 동물들의 공격형 AI, 도망형 AI 구현

🚖 공격하는 동물

  • 📜Animal
    • 시야각 내에서 플레이어를 발견했다면
      • 📜WeakAnimal 👉 Run
        • 플레이어의 반대 방향으로 도망간다.
        • 📜Pig.cs
      • 📜StrongAnimal 👉 Chase
        • 플레이어 방향으로 플레이어를 따라와 추격한다.
          • 플레이어를 추격 중이다가 가까이 있고 + 시야 내에도 있게 될 때 공격한다.
        • 📜Lion.cs

공격 애니메이션

image

image

사자의 공격 애니메이션 만듬.

📜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️⃣ 눈 앞에 있을 경우
      • 플레이어를 공격한다.
  • AttackCoroutine() 공격 코루틴
    • isAttacking True 👉 공격 중일땐 다음 랜덤 행동을 개시하지 않도록
    • 공격은 제자리에서 이루어지도록 한다.
      • 목적지 추적 리셋
      • currentChaseTimechaseTime로 만들어 ChaseTargetCoroutine 코루틴의 실제 추적 처리가 이루어지지 않도록 한다.
        • 코루틴 함수는 병행하여 실행되므로 병렬적으로 ChaseTargetCoroutine가 실행될 수 있기 때문. 공격할 땐 추적을 멈춰야 한다.
    • 공격은 플레이어를 바라보고 하도록 한다.
    • 공격 애니메이션이 어느정도 진행한 후 데미지를 입힐 수 있도록 공격 애니메이션 재생시킨 후 0.5초 대기
    • 이제 공격을 시도한다.
      • 공격 애니메이션 도중에 플레이어에게 닿았다면
        • 플레이어의 HP 를 깎는다.
      • 공격 애니메이션 도중에 플레이어에게 닿지 않았다면
        • 이건 공격이 빗나가 실패한 것
    • 이렇게 공격 과정을 한번 할때마다 attackDelay 만큼 대기 시간을 가진다.
    • isAttacking False
    • 다시 플레이어를 추적
  • Chase(Vector3 _targetPos)
    • 사자가 죽으면 더 이상 플레이어를 추격하지 않도록.. (코루틴에 의해서 플레이어가 사라지거나 해도 한동안 추적을 계속 하기 때문에 이 처리를 안해주면 사자가 죽은 상태에서도 플레이어 따라다녔다 ㅠㅠ)
      if(!isDead)
            nav.SetDestination(destination);
      

image



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

맨 위로 이동하기

댓글남기기