1. 실행 이미지

 

using System.Collections;
using UnityEngine;

public class Boss : EnemyBase
{
    public GameObject bossBulletPrefab;
    public Transform firePos;
    public float fireForce = 10f;
    public int bossHP;

    protected Animator anim;
    public BossState bossState = BossState.Idle;

    public float detectionRadius = 50.0f;
    public LayerMask playerLayer;
    private GameObject detectedPlayer = null;

    public float attackRange = 5.0f;


    public float moveSpeed = 1.0f;
    private float curruntMoveSpeed;

    public int magicCoolTime;
    public float rotationSpeed = 5.0f;

    public SkinnedMeshRenderer skinnedMeshRenderer;
    public Material objectMaterial;
    public Material redMaterial;
    private bool isHit = false;

    public enum BossState
    {
        Idle,
        Walk,
        Attack,
        MagicAttack,
        Die
    }

    void Start()
    {
        curruntMoveSpeed = moveSpeed;
        anim = GetComponent<Animator>();
        targetPoint.SetActive(false);
        magicCoolTime = 0;
        StartCoroutine("MagicCoolTime");
        StartCoroutine("EnemyAnimation");
    }

    protected override void Update()
    {
        //base.Update();
        PlayerDetect();
        if(bossHP <= 0)
        {
            bossState = BossState.Die;
        }
        else
        {
            if (bossState == BossState.Walk && detectedPlayer != null)
            {
                float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
                MoveTowardsPlayer(distanceToPlayer);
            }
        }
     
    }


    IEnumerator MagicCoolTime()
    {
        magicCoolTime = 0;

        while (true)
        {
            yield return new WaitForSeconds(1);
            magicCoolTime++;

            // magicCoolTime이 3초 이상이고, 플레이어가 감지되었을 때 MagicAttack 상태로 변경
            if (magicCoolTime >= 3 && detectedPlayer != null)
            {
                bossState = BossState.MagicAttack;
            }
        }
    }

    void PlayerDetect()
    {
        if (GameManager.Instance.PlayerHP <= 0)
        {
            detectedPlayer = null;
            bossState = BossState.Idle;
            return;
        }

        Collider[] detectedPlayers = Physics.OverlapSphere(transform.position, detectionRadius, playerLayer);
        if (detectedPlayers.Length > 0)
        {
            detectedPlayer = detectedPlayers[0].gameObject;
            float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);

            if (distanceToPlayer > attackRange)
            {
                // 플레이어가 감지되었지만 magicCoolTime 조건에 따라 MagicAttack 상태가 이미 설정되었는지 확인
                if (bossState != BossState.MagicAttack)
                {
                    bossState = BossState.Walk;
                }
            }
            else
            {
                bossState = BossState.Attack;
            }
        }
        else
        {
            detectedPlayer = null;
            bossState = BossState.Idle;
        }
    }
    void MoveTowardsPlayer(float distanceToPlayer)
    {
        Vector3 directionToPlayer = (detectedPlayer.transform.position - transform.position).normalized;
        directionToPlayer.y = 0;
        transform.position += directionToPlayer * curruntMoveSpeed * Time.deltaTime;
        RotateTowardsPlayer();
    }

    void MagicAttack()
    {
        Debug.Log("MagicAttack 호출");
        FireAtPlayer();
    }

    public void FireAtPlayer() //애니메이션 이벤트
    {
        // 적의 중앙을 향한 방향 계산
        Vector3 playerCenter = detectedPlayer.transform.position + new Vector3(0, detectedPlayer.GetComponent<Collider>().bounds.extents.y, 0); //player의 position + Enemy의 콜라이더 높이 절반
        Vector3 fireDirection = (playerCenter - firePos.position).normalized;

        // 총알 방향을 설정
        Quaternion fireRotation = Quaternion.LookRotation(fireDirection);
        fireRotation *= Quaternion.Euler(90, 0, 0);  // X축으로 90도 회전을 추가

        GameObject bossBullet = Instantiate(bossBulletPrefab, firePos.position, fireRotation);


        Rigidbody rb = bossBullet.GetComponent<Rigidbody>();

        if (rb != null)
        {
            rb.AddForce(fireDirection * fireForce, ForceMode.Impulse);

        }


    }

    void PlayerAttackDamage()
    {
        float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
        if (distanceToPlayer <= attackRange)
        {
            GameManager.Instance.PlayerHP--;
            Debug.LogFormat("PlayerHP : {0}", GameManager.Instance.PlayerHP);
        }
    }

    void RotateTowardsPlayer()
    {
        if (detectedPlayer != null)
        {
            Vector3 lookDirection = (detectedPlayer.transform.position - transform.position).normalized;
            lookDirection.y = 0;
            Quaternion lookRotation = Quaternion.LookRotation(lookDirection);
            transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * rotationSpeed);
        }
    }

    private IEnumerator GetHit()
    {
        skinnedMeshRenderer.material = redMaterial;
        yield return new WaitForSeconds(0.1f);
        skinnedMeshRenderer.material = objectMaterial;
        isHit = false;
    }


    protected override void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Bullet"))
        {
            bossHP--;
            Destroy(other.gameObject);
            if (bossHP <= 0)
            {
                bossState = BossState.Die;
            }
            else
            {
                StartCoroutine("GetHit");
            }
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(this.transform.position, detectionRadius);
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(this.transform.position, attackRange);
    }

    protected override IEnumerator EnemyAnimation()
    {
        while (true)
        {
            switch (bossState)
            {
                case BossState.Idle:
                    curruntMoveSpeed = 0;
                    anim.SetInteger("state", 0);
                    break;
                case BossState.Walk:
                    curruntMoveSpeed = moveSpeed;
                    anim.SetInteger("state", 1);
                    break;
                case BossState.Attack:
                    anim.SetInteger("state", 2);
                    curruntMoveSpeed = 0;
                    RotateTowardsPlayer();
                    yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && anim.GetCurrentAnimatorStateInfo(0).IsName("Attack"));

                    // 애니메이션이 완료된 후 Player의 위치를 확인
                    if (detectedPlayer != null)
                    {
                        float distanceToPlayer = Vector3.Distance(transform.position, detectedPlayer.transform.position);
                        // Player가 공격 범위 밖으로 이동했다면 상태를 Walk로 변경
                        bossState = distanceToPlayer > attackRange ? BossState.Walk : BossState.Attack;
                    }
                    else
                    {
                        bossState = BossState.Walk;
                        //anim.SetInteger("state", 1);
                    }
                    break;
                case BossState.MagicAttack:
                    StopCoroutine("MagicCoolTime");
                    anim.SetInteger("state", 3);
                    curruntMoveSpeed = 0;
                    yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && anim.GetCurrentAnimatorStateInfo(0).IsName("MagicAttack"));
                    bossState = BossState.Walk;
                    StartCoroutine("MagicCoolTime");
                    //anim.SetInteger("state", 1);
                    break;
                case BossState.Die:
                    curruntMoveSpeed = 0;
                    anim.SetInteger("state", 4);

                    Destroy(this.GetComponent<BoxCollider>());
                    targetPoint.SetActive(false);

                    yield return new WaitUntil(() => anim.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && anim.GetCurrentAnimatorStateInfo(0).IsName("Die"));
                    yield return new WaitForSeconds(1);
                    Destroy(this.gameObject);
                    yield break; // Boss가 사망한 경우 코루틴 종료
            }
            yield return null;
        }
    }
}

Boss.cs

 

using System.Collections;
using UnityEngine;

public abstract class EnemyBase : MonoBehaviour
{
    public GameObject targetPoint;
    public int enemyHP;
    protected Animator animator;
    public EnemyState enemyState = EnemyState.Idle;

    public enum EnemyState
    {
        Idle,
        Walk,
        GetHit,
        Die,
        Attack
    }

    protected virtual void Start()
    {
        animator = GetComponent<Animator>();
        targetPoint.SetActive(false);
        StartCoroutine(EnemyAnimation());
    }

    protected virtual void Update()
    {
        AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
        if (stateInfo.IsName("GetHit") && stateInfo.normalizedTime >= 1.0f)
        {
            enemyState = EnemyState.Idle;
        }
    }

    protected virtual void OnTriggerEnter(Collider other)
    {
        if (animator == null) return;
        if (other.gameObject.CompareTag("Bullet"))
        {
            enemyHP--;
            Destroy(other.gameObject);
            if (enemyHP <= 0)
            {
                enemyState = EnemyState.Die;
            }
            else
            {
                if (enemyState != EnemyState.GetHit)
                {
                    enemyState = EnemyState.GetHit;
                    animator.Play("GetHit", -1, 0f);
                }
            }
        }
    }

    protected abstract IEnumerator EnemyAnimation();
}

EnemyBase.cs

반응형

+ Recent posts