Enemy의 이동 및 공격 로직 개선
포탈 추가
This commit is contained in:
@@ -1,45 +1,129 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI; // NavMesh 기능을 위해 필수
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class EnemyAI : MonoBehaviour
|
||||
[RequireComponent(typeof(NavMeshAgent))]
|
||||
public class EnemyMoveDefault : MonoBehaviour
|
||||
{
|
||||
[Header("Movement Settings")]
|
||||
[SerializeField] private Transform _finalTarget; // 최종 목적지 (Core)
|
||||
[SerializeField] private float _updateInterval = 0.2f; // 경로 갱신 간격
|
||||
[SerializeField] private float _detectionRange = 1.5f; // 전방 장애물 감지 거리
|
||||
|
||||
private Transform _currentTarget; // 현재 추적 중인 대상
|
||||
private NavMeshAgent _agent;
|
||||
private Transform _target;
|
||||
private EnemyAttack _attackScript;
|
||||
private Coroutine _pathUpdateCoroutine;
|
||||
|
||||
// EnemyAttack 스크립트에서 현재 타겟을 참조하기 위한 프로퍼티
|
||||
public Transform Target => _currentTarget;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_agent = GetComponent<NavMeshAgent>();
|
||||
_attackScript = GetComponent<EnemyAttack>();
|
||||
|
||||
// 인스펙터에서 비어있다면 태그로 자동으로 찾음
|
||||
if (_finalTarget == null)
|
||||
{
|
||||
GameObject core = GameObject.FindWithTag("Core"); // Core 오브젝트에 "Core" 태그를 달아주세요.
|
||||
if (core != null) _finalTarget = core.transform;
|
||||
}
|
||||
|
||||
_currentTarget = _finalTarget;
|
||||
}
|
||||
|
||||
void Start()
|
||||
void OnEnable()
|
||||
{
|
||||
// --- 2번 방법: 위치 보정 로직 시작 ---
|
||||
// 현재 위치에서 반경 2.0f 이내에 가장 가까운 NavMesh가 있는지 검사합니다.
|
||||
if (NavMesh.SamplePosition(transform.position, out NavMeshHit hit, 2.0f, NavMesh.AllAreas))
|
||||
{
|
||||
// 에이전트를 해당 위치로 강제 순간이동(Warp) 시킵니다.
|
||||
// 이 작업은 에러를 방지하고 에이전트를 활성화합니다.
|
||||
_agent.Warp(hit.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"{gameObject.name} 근처에 NavMesh를 찾을 수 없습니다! 스폰 위치를 확인하세요.");
|
||||
}
|
||||
// --- 2번 방법 끝 ---
|
||||
if (_pathUpdateCoroutine != null) StopCoroutine(_pathUpdateCoroutine);
|
||||
_pathUpdateCoroutine = StartCoroutine(UpdatePathRoutine());
|
||||
}
|
||||
|
||||
// 기존 타겟(Core) 설정 로직
|
||||
GameObject coreObj = GameObject.FindWithTag("Core");
|
||||
if (coreObj != null)
|
||||
void OnDisable()
|
||||
{
|
||||
if (_pathUpdateCoroutine != null) StopCoroutine(_pathUpdateCoroutine);
|
||||
}
|
||||
|
||||
IEnumerator UpdatePathRoutine()
|
||||
{
|
||||
// Agent가 NavMesh에 배치될 시간을 줌
|
||||
yield return null;
|
||||
WaitForSeconds delay = new WaitForSeconds(_updateInterval);
|
||||
|
||||
while (true)
|
||||
{
|
||||
_target = coreObj.transform;
|
||||
// 1. 타겟 유효성 체크: 타겟이 파괴되었으면 다시 코어로 설정
|
||||
if (_currentTarget == null || !_currentTarget.gameObject.activeInHierarchy)
|
||||
{
|
||||
_currentTarget = _finalTarget;
|
||||
_agent.isStopped = false;
|
||||
}
|
||||
|
||||
if (_currentTarget != null && _agent.isOnNavMesh)
|
||||
{
|
||||
float distance = Vector3.Distance(transform.position, _currentTarget.position);
|
||||
|
||||
// 2. 공격 사거리 체크 (EnemyAttack의 사거리 참조)
|
||||
if (distance <= _attackScript.attackRange)
|
||||
{
|
||||
// 사거리 안이면 멈춤
|
||||
_agent.isStopped = true;
|
||||
_agent.velocity = Vector3.zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3. 사거리 밖이면 전방 장애물(성벽 등) 감지
|
||||
DetectObstacle();
|
||||
|
||||
// 4. 장애물 때문에 멈춘 게 아니라면 목적지로 전진
|
||||
if (!_agent.isStopped)
|
||||
{
|
||||
_agent.SetDestination(_currentTarget.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
yield return delay;
|
||||
}
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
/// <summary>
|
||||
/// 레이캐스트를 쏴서 전방에 길을 막는 IDamageable(성벽)이 있는지 확인
|
||||
/// </summary>
|
||||
private void DetectObstacle()
|
||||
{
|
||||
if (_target != null && _agent.isOnNavMesh)
|
||||
RaycastHit hit;
|
||||
// 적의 위치(발밑)보다 약간 위(가슴 높이)에서 전방으로 레이 발사
|
||||
Vector3 rayOrigin = transform.position + Vector3.up * 1.0f;
|
||||
|
||||
// Scene 뷰에서 레이를 시각적으로 확인 (디버깅용)
|
||||
Debug.DrawRay(rayOrigin, transform.forward * _detectionRange, Color.yellow);
|
||||
|
||||
if (Physics.Raycast(rayOrigin, transform.forward, out hit, _detectionRange))
|
||||
{
|
||||
_agent.SetDestination(_target.position);
|
||||
// 부딪힌 대상에게서 IDamageable 인터페이스 확인
|
||||
IDamageable damageable = hit.collider.GetComponentInParent<IDamageable>();
|
||||
|
||||
// 대상이 존재하고, 그 대상이 현재 코어가 아닐 때 (즉, 가로막는 성벽일 때)
|
||||
if (damageable != null && hit.collider.transform != _finalTarget)
|
||||
{
|
||||
_currentTarget = hit.collider.transform;
|
||||
|
||||
// 공격 스크립트에도 즉시 새로운 타겟을 알려줌
|
||||
_attackScript.SetAttackTarget(_currentTarget);
|
||||
|
||||
// 일단 멈추고 다음 루프에서 사거리 체크를 받도록 함
|
||||
_agent.isStopped = true;
|
||||
_agent.velocity = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 외부(스패너 등)에서 최종 목적지를 설정해줄 때 사용
|
||||
/// </summary>
|
||||
public void SetFinalTarget(Transform target)
|
||||
{
|
||||
_finalTarget = target;
|
||||
_currentTarget = target;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user