총구 화염 효과(x,y,z)

using System.Collections;
using UnityEngine;

public class Stage2Enemy : EnemyBase
{
    public float detectionRadius = 5.0f;
    public LayerMask playerLayer;
    public LayerMask wallLayer;
    public float rayHeight = 1.0f;
    private GameObject detectedPlayer = null;

    public GameObject enemyBulletPrefab;
    public Transform firePos;
    public float fireForce;


    protected override void Update()
    {
        base.Update(); //EnemyBase의 Update 메서드를 호출
        PlayerDetect();
        RotateTowardsPlayer();
    }

    protected override IEnumerator EnemyAnimation()
    {
        while (true)
        {
            switch (enemyState)
            {
                case EnemyState.Idle:
                    animator.SetInteger("state", 0);
                    break;
                case EnemyState.Walk:
                    animator.SetInteger("state", 1);
                    break;
                case EnemyState.GetHit:
                    animator.SetInteger("state", 2);
                    break;
                case EnemyState.Die:
                    animator.SetInteger("state", 3);
                    targetPoint.SetActive(false);
                    this.GetComponent<Collider>().enabled = false;

                    yield return new WaitUntil(() => animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1f && animator.GetCurrentAnimatorStateInfo(0).IsName("Die"));
                    Destroy(this.gameObject);
                    break;
                case EnemyState.Attack:
                    animator.SetInteger("state", 4);
                    break;
            }

            yield return null;
        }
    }
    void RotateTowardsPlayer()
    {
        if (enemyState == EnemyState.Attack && detectedPlayer != null)
        {
            Vector3 lookDirection = (detectedPlayer.transform.position - transform.position).normalized;
            lookDirection.y = 0; // Y축 변경을 방지
            Quaternion lookRotation = Quaternion.LookRotation(lookDirection);
            transform.rotation = Quaternion.Slerp(transform.rotation, lookRotation, Time.deltaTime * 5.0f); // 부드러운 회전
        }
    }
    void PlayerDetect()
    {
        if (enemyHP <= 0 || enemyState == EnemyState.GetHit) return; // 체력이 0 이하이거나 피격 상태이면 감지를 하지 않음
        Collider[] detectedPlayers = Physics.OverlapSphere(transform.position, detectionRadius, playerLayer);

        if (detectedPlayers.Length > 0)
        {
            detectedPlayer = detectedPlayers[0].gameObject;

            Vector3 rayDirection = (detectedPlayer.transform.position - transform.position).normalized;
            Vector3 rayStartPosition = transform.position + new Vector3(0, rayHeight, 0);

            RaycastHit hit;
            if (Physics.Raycast(rayStartPosition, rayDirection, out hit, detectionRadius, playerLayer | wallLayer))
            {
                if (hit.collider.CompareTag("Player"))
                {
                    enemyState = EnemyState.Attack;
                }
                else if (hit.collider.gameObject.layer == LayerMask.NameToLayer("Wall"))
                {
                    detectedPlayer = null;
                    enemyState = EnemyState.Idle;
                }
            }
            else
            {
                detectedPlayer = null;
                enemyState = EnemyState.Idle;
            }
        }
        else
        {
            detectedPlayer = null;
            enemyState = EnemyState.Idle;
        }
    }

    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 enemyBullet = Instantiate(enemyBulletPrefab, firePos.position, fireRotation);


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

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

        }


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

    }

}
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 (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();
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class PlayerController : MonoBehaviour
{
    public Joystick joystick;
    public float moveSpeed = 5.0f;
    public float detectionRadius = 5.0f; // 적을 탐지하는 범위
    public LayerMask enemyLayer;
    public LayerMask wallLayer;
    public float rayHeight = 1.0f; 

    private AnimationState currentState;
    private Animator animator;
    private EnemyBase targetedEnemy = null;

    // Ray의 시작점과 끝점을 저장할 변수
    private Vector3 rayStart;
    private Vector3 rayEnd;

    public GameObject bulletPrefab;
    public Transform firePos;

    public float fireForce = 1000f;

    public GameObject targetPlayer;
    private Collider playerCollider;


    void Start()
    {
        currentState = AnimationState.Idle;
        animator = GetComponent<Animator>();
        targetPlayer.SetActive(true);
        playerCollider = GetComponent<Collider>();
    }

    void Update()
    {
        if (currentState != AnimationState.Die)
        {
            Move();
            DetectEnemies();
        }

        HandleAnimation();
    }




    void Move()
    {
        Vector3 moveDirection = new Vector3(-joystick.Vertical, 0, joystick.Horizontal).normalized;
        transform.Translate(moveDirection * moveSpeed * Time.deltaTime, Space.World);

        if (moveDirection != Vector3.zero)
        {
            Quaternion toRotation = Quaternion.LookRotation(moveDirection, Vector3.up);
            transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, 360f * Time.deltaTime);
            currentState = AnimationState.Run;
        }
        else
        {
            if (targetedEnemy != null)
            {
                currentState = AnimationState.Attack;
                LookAtEnemy(targetedEnemy.transform);
                


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

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

        GameObject bullet = Instantiate(bulletPrefab, firePos.position, fireRotation);
        StartCoroutine(FireEffectCoroutine());

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

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

        }


    }
    private IEnumerator FireEffectCoroutine() // 총구 화염 효과 코루틴
    {
        
        foreach (Transform child in firePos)
        {
            child.gameObject.SetActive(true);
        }

        
        yield return new WaitForSeconds(0.2f);

        
        foreach (Transform child in firePos)
        {
            child.gameObject.SetActive(false);
        }
    }
    void DetectEnemies()
    {
        Vector3 rayDirection;
        Vector3 rayStartPosition = transform.position + new Vector3(0, rayHeight, 0);

        // 타겟이 범위를 벗어났는지 확인
        if (targetedEnemy != null)
        {
            float distanceToTarget = Vector3.Distance(transform.position, targetedEnemy.transform.position);
            if (distanceToTarget > detectionRadius || !targetedEnemy.targetPoint.activeSelf)
            {
                targetedEnemy.targetPoint.SetActive(false);
                targetedEnemy = null;
            }
        }

        if (targetedEnemy == null)
        {
            Collider[] detectedEnemies = Physics.OverlapSphere(transform.position, detectionRadius, enemyLayer);
            EnemyBase closestVisibleEnemy = null;
            float minDistance = float.MaxValue;

            foreach (Collider collider in detectedEnemies)
            {
                rayDirection = (collider.transform.position - transform.position).normalized;
                RaycastHit hit1; 

                // Ray 발사
                if (Physics.Raycast(rayStartPosition, rayDirection, out hit1, detectionRadius, enemyLayer | wallLayer))
                {
                    if (hit1.collider.GetComponent<EnemyBase>())
                    {
                        float distance = Vector3.Distance(transform.position, collider.transform.position);
                        if (distance < minDistance)
                        {
                            closestVisibleEnemy = collider.GetComponent<EnemyBase>();
                            minDistance = distance;
                        }
                    }
                }
            }

            if (closestVisibleEnemy && closestVisibleEnemy.enemyState != EnemyBase.EnemyState.Die)
            {
                rayDirection = (closestVisibleEnemy.transform.position - transform.position).normalized;

                if (targetedEnemy != null)
                    targetedEnemy.targetPoint.SetActive(false);

                targetedEnemy = closestVisibleEnemy;
                targetedEnemy.targetPoint.SetActive(true);
            }
            else
            {
                return;
            }
        }
        else
        {
            rayDirection = (targetedEnemy.transform.position - transform.position).normalized;
        }

        RaycastHit hit2; 
        if (Physics.Raycast(rayStartPosition, rayDirection, out hit2, detectionRadius, enemyLayer | wallLayer))
        {
            rayStart = rayStartPosition;
            rayEnd = hit2.point;

            if (hit2.collider.gameObject.layer == LayerMask.NameToLayer("Wall"))
            {
                if (targetedEnemy != null)
                {
                    targetedEnemy.targetPoint.SetActive(false);
                    targetedEnemy = null;
                }
            }
        }
    }


    void LookAtEnemy(Transform enemyTransform)
    {
        Vector3 lookDirection = enemyTransform.position - transform.position;
        lookDirection.y = 0;
        Quaternion rotation = Quaternion.LookRotation(lookDirection);
        transform.rotation = rotation;
    }

    void HandleAnimation()
    {
        animator.SetInteger("state", (int)currentState);
    }

    private void OnCollisionEnter(Collision collision) // Enemy와 직접적인 충돌
    {
        if (collision.gameObject.CompareTag("Enemy"))
        {
            GameManager.Instance.PlayerHP -= 1;  // HP 감소
            Debug.Log("Player HP: " + GameManager.Instance.PlayerHP);
            if (GameManager.Instance.PlayerHP <= 0)
            {
                currentState = AnimationState.Die;
                targetPlayer.SetActive(false);
                playerCollider.enabled = false;
            }
        }
    }
    private void OnTriggerEnter(Collider other) // Enemy 투사체에 의한 충돌
    {
        // EnemyBullet과 충돌 시
        if (other.gameObject.CompareTag("EnemyBullet"))
        {
            GameManager.Instance.PlayerHP -= 1;
            Destroy(other.gameObject);
            Debug.Log("Player HP: " + GameManager.Instance.PlayerHP);

            // HP가 0이면 Die 상태로 전환
            if (GameManager.Instance.PlayerHP <= 0)
            {
                currentState = AnimationState.Die;
                targetPlayer.SetActive(false);
                playerCollider.enabled = false;
                //Destroy(gameObject, 3f);  // 3초 후에 Player를 Destroy (Die 애니메이션이 끝날 때까지 대기)
            }
            else
            {
                currentState = AnimationState.GetHit;
            }
        }
    }
 

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.white;
        Gizmos.DrawWireSphere(this.transform.position, detectionRadius);

        // 저장된 Ray의 정보를 기반으로 선을 그림
        Gizmos.color = Color.red;
        Gizmos.DrawLine(rayStart, rayEnd);
    }


}

public enum AnimationState
{
    Idle,
    Run,
    Attack,
    Reload,
    GetHit,
    Die
}

 

[구현 목록]

1. Enemy 스크립트들을 EnemyBase 클래스를 상속받게 구성해서 Player 스크립트 단일화

2. Enemy와  Player의 발사체 궤적 구현

3. Player의 총구 화염 효과 구현

4. Stage2 Enemy 구현 (Player 인식 및 회전, Attack시 발사체 구현)

 

+ Recent posts