Capsule을 하이라키에 추가하고 이름을 Enemy로 변경합니다.

M_Enemy 머터리얼도 하나 만들어 빨간색으로 지정합고 Enemy에 지정합니다.

충돌을 쉽게 처리하기 위해 플레이어 캐릭터 처럼 capsuler collider는 제거합니다.

EnemyFSM이라는 스크립트를 만들고 적용시킵니다.

EnemyState라는 Enemy의 상태를 만들고 m_State에 저장할겁니다.

using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;

public class EnemyFSM : MonoBehaviour
{
    enum EnemyState
    {
        Idle, Move, Attack, Return, Damaged, Die
    }
    EnemyState m_State;
    public Slider hpSlider;
    public Text statusText;
    public float findDistance = 8f;
    public float attackDistance = 2f;
    public float moveSpeed = 5f;
    public int attackPower = 3;

    public int weaponPower = 5;
    float currentTime = 0;
    float attackDelay = 2f;
    int hp = 15;
    int maxHp = 15;
    Transform player;
    CharacterController cc;
    Vector3 originPos;
    public float moveDistance = 20f;
    
    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        m_State = EnemyState.Idle;
        statusPrint();
        player = GameObject.Find("Player").transform;
        cc = GetComponent<CharacterController>();
        originPos = transform.position;
    }

    // Update is called once per frame
    void Update()
    {
        switch (m_State)
        {
            case EnemyState.Idle:
                Idle();
                break;
            case EnemyState.Move:
                Move();
                break;
            case EnemyState.Attack:
                Attack();
                break;
            case EnemyState.Return:
                Return();
                break;
            case EnemyState.Damaged:
                //Damaged();
                break;
            case EnemyState.Die:
                //Die();
                break;
        }
        hpSlider.value = (float) hp/ (float) maxHp;
    }

    void Move()
    {
        if(Vector3.Distance(transform.position, originPos) > moveDistance)
        {
            m_State = EnemyState.Return;
            statusPrint();
            print("State : Move -> Return");
        } 
        else if (Vector3.Distance(transform.position, player.position) > attackDistance)
        {
            Vector3 dir = (player.position - transform.position).normalized;
            cc.Move(dir * moveSpeed * Time.deltaTime);
        }
        else
        {
            m_State = EnemyState.Attack;
            statusPrint();
            print("State Change : Move -> Attack");
            currentTime = attackDelay;
        }

    }
    void Attack()
    {
        if (Vector3.Distance(transform.position, player.position) < attackDistance)
        {
            currentTime += Time.deltaTime;
            if (currentTime > attackDelay)
            {
                player.GetComponent<PlayerMove>().DamageAction(attackPower);
                print("Attack");
                currentTime = 0;
            }
        }
        else
        {
            m_State = EnemyState.Move;
            statusPrint();
            print("State Change : Attack -> Move");
            currentTime = 0;
        }
    }
    public void HitEnemy(int hitPower)
    {
        if(m_State == EnemyState.Damaged || m_State == EnemyState.Die || m_State == EnemyState.Return)
        {
            return;
        }
        hp -= hitPower;
        if (hp > 0)
        {
            m_State = EnemyState.Damaged;
            statusPrint();
            Damaged();
        }
        else
        {
            m_State = EnemyState.Die;
            statusPrint();
            Die();
        }
    }
    void Damaged()
    {
        //throw new NotImplementedException();
        StartCoroutine(DamageProcess());
    }

    void Return()
    {
        if (Vector3.Distance(transform.position, originPos) > 0.1f)
        {
            Vector3 dir = (originPos - transform.position).normalized;
            cc.Move(dir * moveSpeed *Time.deltaTime);
        }
        else
        {
            transform.position = originPos;
            hp = maxHp;
            m_State = EnemyState.Idle;
            statusPrint();
            print("State : Return -> Idle");
        }
    }

    void Idle()
    {
        if (Vector3.Distance(transform.position, player.position) < findDistance)
        {
            m_State = EnemyState.Move;
            statusPrint();
            print("State Change : Idle -> Move");
        }
    }

    IEnumerator DamageProcess()
    {
        yield return new WaitForSeconds(0.5f);
        m_State = EnemyState.Move;
        statusPrint();
    }

    private void Die()
    {
        StopAllCoroutines();
        StartCoroutine(DieProcess());
    }
    IEnumerator DieProcess()
    {
        cc.enabled = false;
        m_State = EnemyState.Move;
        statusPrint();
        yield return new WaitForSeconds(2f);
        Destroy(gameObject);
    }
    void statusPrint()
    {
        statusText.text = m_State.ToString() + hp;
    }
}

Enemy의 초기 상태는 Idle이고

Update() 함수에서 Player와의 거리가 findDistance 이하로 되면 Move 상태로 전환하고 Player를 따라 갑니다.

Move()상태에서는 attackDistance 이하가 되면 Player를 공격합니다.

Player의 PlayerMove 스크립트에 DamageAction()함수를 불러서 피해량을 처리합니다.

player.GetComponent<PlayerMove>().DamageAction(attackPower);

 

Player도 공격시 Enemy의  EnemyFSM 스크립트안의 HitEnemy()할수를 통해 피해를 전달합니다.

EnemyFSM eFSM = hitInfo.transform.gameObject.GetComponent<EnemyFSM>();
eFSM.HitEnemy(weaponPower);

피해를 받을때 액션을 처리하기 위해 StartCoroutine()으로 지연처리를 하고 있습니다.

 

'인생유니티 > FPS게임' 카테고리의 다른 글

Enemy Animation  (0) 2025.04.22
모델링 교체와 애니메이션 - 알파  (1) 2025.04.22
UI - Proto  (0) 2025.04.22
FPS Game 제작 - 프로토 - 무기 제작  (0) 2025.04.21
FPS Game 제작 - 프로토 - 플레이어 제작  (1) 2025.04.21

+ Recent posts