using System; using Unity.VisualScripting; using UnityEngine; using UnityEngine.AI; using UnityEngine.SceneManagement; using EnemyType = Define.EnemyType; using Random = UnityEngine.Random; //EnemyController는 몬스터의 '뇌' 역할을 하며, 상태 머신 패턴을 기반으로 동작하고, 외부에서 데이터를 주입받아 초기화되는 구조로 설계하기 public class EnemyController : MonoBehaviour, IDamageable { // 데미지를 입력하기 위한 클래스 선언 필드 private Damage _damage; // 이동을 하기 위한 클래스 선언 필드 private Movement_Base _movement; // 상태 정보가 담긴 클래스 선언 필드 private Status_Enemy _status; public Status_Enemy Status { get { return _status; } private set { _status = value;} } [SerializeField] private EnemyType _type = EnemyType.Unknown; [SerializeField] private int _expRate = 5; public Movement_Base Movement => _movement; public EnemyType Type => _type; private AI_State _currentState; public AI_State CurrentState { get { return _currentState; } set { if (_currentState != null) _currentState.Exit(); _currentState = value; if (_currentState != null) _currentState.Enter(); } } [SerializeField] private bool _explose = false; private bool _hasExploded = false; private void Awake() { _movement = gameObject.GetComponent(); _status = gameObject.GetComponent(); _damage = gameObject.AddComponent(); _damage._targetlayer = LayerMask.GetMask("Player"); } void Start() { } public void Init(EnemyData data) { _type = data.E_Type; if (_damage != null) { _damage.OnHit -= OnBodyHit; _damage.OnHit += OnBodyHit; } } void Update() { if (CurrentState != null) CurrentState.Update(); _damage?.SetTriggerActive(true); if (_explose && !_hasExploded) { _hasExploded = true; OnExplose(); } } private void OnBodyHit(IDamageable target, Collider collider) { Debug.Log($"Player Hit! {collider.name}"); } public void TakeDamage(float damage) { float hp = Status.ApplyDamage(damage); if (hp <= 0) { _explose = true; } Debug.Log($"피격! 데미지: {damage} / 남은 체력: {hp}"); } private void OnExplose() { Vector3 currentPosition = transform.position; int totalExp = Mathf.RoundToInt(_status.ExpAmount); int cubeCount = (totalExp / _expRate) + (totalExp % _expRate > 0 ? 1 : 0); // 5/5/5/3 = 18 // count = 4 for (int i = 0; i < cubeCount; i++) { float amount = Mathf.Min(_expRate, (totalExp - (i * _expRate))); Vector3 spawnPosition = currentPosition + Random.insideUnitSphere * 2f; spawnPosition.y = currentPosition.y; GameObject expCube = Manager.Resource.Instantiate("Prefabs/Objects/ExpCube", GameObject.Find("@Object").transform); expCube.GetComponent().SetExpAmount(amount); expCube.transform.position = spawnPosition; } Destroy(gameObject, 0.1f); } } /* * 행동 패턴 * - 종류 : Idle(대기), Patrol(순찰), Chase(추적), Run(도망), Return(귀환), Hide(숨기), Fight(싸우기) * 1. Idle : 제자리에서 대기 * 2. Patrol : 지정된 경로를 따라 순찰 * 3. Chase : 플레이어를 발견하면 추적 * 4. Run : (옵션) 체력이 너무 낮으면 도망 * 5. Return : 일정 시간 동안 플레이어를 찾지 못하면 원래 위치로 귀환 * 6. Hide : 플레이어의 시야에서 벗어나 숨기 * 7. Fight : 플레이어와 일정 거리 내에 들어오면 싸우기 * - 행동 전환 * 1. Idle -> Patrol : 일정 시간 후 * 2. Idle/Patrol -> Chase : 플레이어 발견 시 * 2.1. 플레이어 발견 조건 : 시야각, 거리, 장애물 유무 등 * 3. Idle/Patrol/Chase -> Run : (옵션) 체력이 일정 이하로 떨어지면 * 4. Idle/Patrol/Chase/Run -> Fight : 플레이어와 일정 거리 내에 들어오면 * 5. Patrol -> Idle : 일정 시간 동안 움직이지 않으면 * 6. Patrol/Chase/Run/Fight -> Return : 플레이어를 일정 시간 동안 찾지 못하면 * 7. Patrol/Chase/Run/Fight -> Hide : 플레이어의 시야에서 벗어나면 */