[유니티]탑다운 슈팅 따라하기 #7 게임의 끝

안녕하세요 유랑입니다.

 

 

실력향상을 위해서 오늘은 유튜브를 따라하면서 공부하겠습니다.

궁금하신점 있으시다면 댓글로 남겨주세요^^

 

 

 

1. 탑다운 슈팅 따라하기

 

 

이번 강의는 Sebastian Lague님께서 만든 예제이며,

유튜브를 보시면 자세한 내용을 배우실 수 있습니다.

 

유튜브 사이트 => 유튜브 

 

 

 

유니티 슈팅

 

 

 

1-1) 게임의 끝

 

 

이번 시간에는 캐릭터 사망시 적의 움직임을

조정해 보겠습니다.

 

 

 

유니티 슈팅

 

 

 

1-2) 스크립트 작성 -IDamageable

 

 

IDamageable 스크립트에서 TakeDamage라는 메소드를 추가해 주겠습니다.

TakeHit의 기능을 가지고 있지만, 레이캐스트 정보를 받지 않을 때 사용할 예정입니다!!

 

 

 

유니티 슈팅

 

 

 

1-3) 스크립트 작성 -㉡LivingEntity 

 

 

IDamageable을 상속한 LivingEntity 스크립트에는 

TakeHit과 TakeDamage에 대한 기능을 정의해 줍니다.

 

 

 

유니티 슈팅

 

 

 

1-4) 스크립트 작성 -㉢Enemy

 

 

Enemy 스크립트에는 플레이어에게 데미지를 주는 부분과

플레이어 사망시 움직임에 대한 내용을 추가해 주겠습니다.

 

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

[RequireComponent(typeof(NavMeshAgent))]
public class Enemy : LivingEntity
{
    // 상태 (기본, 추격, 공격)
    public enum State{Idle, Chasing, Attacking};
    State currentState; // 현재 상태

    NavMeshAgent pathfinder;
    Transform target;
    LivingEntity targetEntity;
    Material skinMaterial;
    Color originColor;

    float attackDistanceThreshold = 0.5f; // 공격 사정거리
    float timeBetweenAttacks = 1; // 공격 딜레이
    float damage = 1; // 공격 데미지

    float nextAttackTime; // 다음 공격이 가능한 시간
    float myCollisionRadius; // 자신의 충돌 범위
    float targetCollisionRadius; // 목표의 충돌 범위

    bool hasTarget; // 공격 타겟의 여부

    protected override void Start()
    {
        base.Start();
        pathfinder = GetComponent<NavMeshAgent>();
        skinMaterial = GetComponent<Renderer>().material;
        originColor = skinMaterial.color;

        if (GameObject.FindGameObjectWithTag("Player") != null)
        {
            currentState = State.Chasing;
            hasTarget = true;
            target = GameObject.FindGameObjectWithTag("Player").transform;
            targetEntity = target.GetComponent<LivingEntity>();
            targetEntity.OnDeath += OnTargetDeath;

            myCollisionRadius = GetComponent<CapsuleCollider>().radius;
            targetCollisionRadius = target.GetComponent<CapsuleCollider>().radius;
            StartCoroutine(UpdatePath());
        }
    }
    // 타겟 사망시 정지
    void OnTargetDeath()
    {
        hasTarget = false;
        currentState = State.Idle;
    }
    void Update()
    {
        if (hasTarget)
        {
            if (Time.time > nextAttackTime)
            {
                // (목표 위치 - 자신의 위치) 제곱을 한 수
                float sqrDstToTarget = (target.position - transform.position).sqrMagnitude;

                if (sqrDstToTarget < Mathf.Pow(attackDistanceThreshold + myCollisionRadius + targetCollisionRadius, 2))
                {
                    nextAttackTime = Time.time + timeBetweenAttacks;
                    StartCoroutine(Attack());
                }
            }
        }
    }
    // 적 공격
    IEnumerator Attack()
    {
        currentState = State.Attacking;
        pathfinder.enabled = false; // 네비게이션 추적 종료

        Vector3 originalPosition = transform.position;
        Vector3 dirToTarget = (target.position - transform.position).normalized;
        Vector3 attackPosition = target.position - dirToTarget * (myCollisionRadius);

        float attackSpeed = 3;
        float percent = 0;

        skinMaterial.color = Color.red;
        bool hasAppliedDamage = false; // 데미지를 적용하는 도중인가

        while (percent <= 1)
        {
            if(percent >= 0.5f && !hasAppliedDamage)
            {
                hasAppliedDamage = true;
                targetEntity.TakeDamage(damage);
            }
            percent += Time.deltaTime * attackSpeed;
            float interpolation = (-Mathf.Pow(percent, 2) + percent) * 4;
            transform.position = Vector3.Lerp(originalPosition, attackPosition, interpolation);

            yield return null;
        }
        skinMaterial.color = originColor;
        currentState = State.Chasing;
        pathfinder.enabled = true; // 네비게이션 추적 시작
    }
    // 적 추적
    IEnumerator UpdatePath()
    {
        float refreshRate = 0.25f;

        while (hasTarget)
        {
            if (currentState == State.Chasing)
            {
                Vector3 dirToTarget = (target.position - transform.position).normalized;
                Vector3 targetPosition = target.position - dirToTarget * (myCollisionRadius + targetCollisionRadius + attackDistanceThreshold/2);
                if (!dead)
                {
                    pathfinder.SetDestination(targetPosition); // 네비게이션 목표 설정
                }
            }
            yield return new WaitForSeconds(refreshRate);
        }
    }
}

 

 

Target에 죽었는지 감지하기 위해 LivingEntity 스크립트 정보를 가져옵니다.

그리고 공격데미지와 공격 타겟의 여부에 대한 정보,

타겟 사망시 OnTargetDeath라는 메소드가 실행하게 할겁니다.

 

 

 

유니티 슈팅

 

 

OnTargetDeath는 타겟 사망시 정지 하도록 정의가 내려져 있으며,

Update에서 타겟이 없다면 공격하지 않도록 넣어줍니다.

 

 

유니티 슈팅

 

 

물론 적 추격하는 코드에도 적이 없다면 동작하지 않도록 넣어줄게요^^

 

 

유니티 슈팅

 

 

마지막으로 데미지 적용여부에 따른 데미지 추는 코드를 넣어주겠습니다.

이제 플레이어에게 데미지를 줄 수 있겠네요ㅎㅎ

 

 

유니티 슈팅

 

 

테스트를 위해 플레이어 체력을 3으로 변경해 주세요.

 

 

유니티 슈팅

 

 

적에게 3번 맞으면 플레이어가 사망 즉 없어지며,

적은 Idle 상태로 가만히 있게됩니다.

 

 

유니티 슈팅

 

 

 

1-5) 스크립트 작성 -㉣Projectile

 

 

총알이 없어지고 적을 통과하지 못하도록 코드를 추가해 주겠습니다.

적이 빠르게 오면 충돌 감지가 씹힐 수 있는데요.

skinWidth를 통해 오차범위까지 적용됩니다.

그리고 총알이 생성 되었을 때 이미 적과 겹쳐있다면

Physics.OverlapSphere를 통해 겹친 물체에 데미지를 주게됩니다ㅎㅎ

 

 

 

유니티 슈팅

 

 

콜라이더용 데미지 메소드도 만들어 줄게요!!

 

 

유니티 슈팅

 

 

총알도 없어지고,

적한테 맞아서 플레이어도 사라졌네요.

 

 

유니티 슈팅

 

 

 

2. 마무리

 

 

오늘 강의는 여기까지입니다.

탑다운 슈팅을 따라하면서 게임의 끝을 만들어 보았습니다.

감사합니다.

 

 

 

수업자료탑다운 슈팅 따라하기 #7 게임의 끝

 

 

 

 

 

 

댓글

Designed by JB FACTORY