enemy의 이동 가속을 높이고, 공격 대상의 표면으로부터 거리 검사를 하도록 함

미끄러지는 현상 감소
코어 앞에서 코어를 못 찾는 문제 수정
This commit is contained in:
2026-02-25 17:26:03 +09:00
parent 84f4020d47
commit 0828bb214f

View File

@@ -90,7 +90,7 @@ namespace Northbound
private bool _isAttacking = false;
private GameObject _pendingAttackTarget;
private float _attackStartTime;
private const float ATTACK_TIMEOUT = 3f; // 애니메이션 이벤트 미발생 시 타임아웃
private const float ATTACK_TIMEOUT = 1f; // 애니메이션 이벤트 미발생 시 타임아웃
public override void OnNetworkSpawn()
{
@@ -106,8 +106,8 @@ namespace Northbound
{
// NavMeshAgent 초기 설정
_agent.speed = moveSpeed;
_agent.acceleration = 8f;
_agent.angularSpeed = 120f;
_agent.acceleration = 100f; // 높은 가속도로 즉시 정지 가능
_agent.angularSpeed = 360f; // 빠른 회전
_agent.stoppingDistance = attackRange * 0.9f; // 공격 범위까지 더 가까이 이동
_agent.autoBraking = true;
_agent.updateRotation = true;
@@ -173,19 +173,15 @@ namespace Northbound
if (_isRecalculatingPath) return; // 코루틴 대기 중이면 중단
if (_coreTransform == null) { FindCore(); return; }
// 0. 경로 계산 중이거나 이동 중이 아니면 아무것도 하지 않음
if (_agent.pathPending) return; // 경로 계산 중
// 0. 경로 계산 중이면 대기
if (_agent.pathPending) return;
// 0.5. 실제로 이동 중인지 확인 (velocity로 판단)
if (_agent.hasPath && _agent.velocity.sqrMagnitude > 0.01f)
// 0.5. 코어 콜라이더 표면까지의 거리로 도달 확인 (이동 중 여부와 관계없이 항상 체크)
if (GetDistanceToCoreSurface() <= attackRange)
{
// 이동 중이면 코어 콜라이더 표면까지의 거리로 도달 확인
if (GetDistanceToCoreSurface() <= attackRange)
{
SetTargetPlayer(_coreTransform.gameObject);
TransitionToState(EnemyAIState.Attack);
return;
}
SetTargetPlayer(_coreTransform.gameObject);
TransitionToState(EnemyAIState.Attack);
return;
}
// 1. Player 감지 (코어로 가는 도중에도 Player/건물을 타겟팅)
@@ -231,7 +227,20 @@ namespace Northbound
return;
}
float distanceToPlayer = Vector3.Distance(transform.position, targetPlayer.transform.position);
// 코어 타겟인지 확인
bool isCoreTarget = (targetPlayer == _coreTransform?.gameObject);
// 거리 계산 - 코어는 콜라이더 표면까지의 거리 사용
float distanceToPlayer;
if (isCoreTarget)
{
distanceToPlayer = GetDistanceToCoreSurface();
}
else
{
distanceToPlayer = GetDistanceToTarget(targetPlayer);
}
Vector3 chaseReferencePoint = (aiType == TeamType.Monster) ? _chaseStartPosition : _originPosition;
float distanceFromReference = Vector3.Distance(transform.position, chaseReferencePoint);
@@ -258,6 +267,12 @@ namespace Northbound
// 공격 타임아웃 체크 (애니메이션 이벤트가 발생하지 않은 경우)
if (_isAttacking && Time.time - _attackStartTime > ATTACK_TIMEOUT)
{
if (showDebugInfo)
{
Debug.LogWarning($"[EnemyAIController] 공격 타임아웃 - 애니메이션 이벤트 미발생: {gameObject.name}");
}
// 타임아웃 시 공격 실행 후 상태 리셋
PerformAttack();
_isAttacking = false;
_pendingAttackTarget = null;
}
@@ -475,6 +490,9 @@ namespace Northbound
else if (_animator != null)
{
_animator.SetTrigger("Attack");
// 애니메이션은 있지만 NetworkAnimator가 없으면 즉시 공격 (이벤트 미지원)
PerformAttack();
_isAttacking = false;
}
else
{
@@ -487,6 +505,10 @@ namespace Northbound
else
{
// 공격할 수 없는 대상이면 상태를 해제합니다.
if (showDebugInfo)
{
Debug.LogWarning($"[EnemyAIController] IDamageable을 찾을 수 없음: {target.name}");
}
OnLostTarget();
}
}
@@ -513,7 +535,14 @@ namespace Northbound
/// </summary>
private void PerformAttack()
{
if (_pendingAttackTarget == null) return;
if (_pendingAttackTarget == null)
{
if (showDebugInfo)
{
Debug.LogWarning("[EnemyAIController] PerformAttack: 타겟이 없음");
}
return;
}
IDamageable damageable = _pendingAttackTarget.GetComponentInChildren<IDamageable>();
if (damageable == null) damageable = _pendingAttackTarget.GetComponent<IDamageable>();
@@ -523,6 +552,18 @@ namespace Northbound
{
damageable.TakeDamage(attackDamage, NetworkObjectId);
OnAttackPerformed?.Invoke(_pendingAttackTarget);
if (showDebugInfo)
{
Debug.Log($"[EnemyAIController] {gameObject.name}이(가) {_pendingAttackTarget.name}에게 {attackDamage} 데미지 적용");
}
}
else
{
if (showDebugInfo)
{
Debug.LogWarning($"[EnemyAIController] PerformAttack: 대상이 유효하지 않음 - {_pendingAttackTarget.name}");
}
}
}
@@ -569,6 +610,8 @@ namespace Northbound
case EnemyAIState.Attack:
_agent.isStopped = true;
_agent.ResetPath();
// 즉시 정지를 위해 velocity 초기화
_agent.velocity = Vector3.zero;
break;
case EnemyAIState.MoveToCore:
_agent.isStopped = false;
@@ -699,6 +742,57 @@ namespace Northbound
Gizmos.DrawLine(transform.position, transform.position + right);
}
}
private void OnDrawGizmosSelected()
{
#if UNITY_EDITOR
// 상태 및 타겟 정보 표시
string stateInfo = $"State: {_currentState.Value}";
string targetInfo = "Target: None";
if (_cachedTargetPlayer != null)
{
targetInfo = $"Target: {_cachedTargetPlayer.name}";
}
else if (_targetPlayerId.Value != 0)
{
targetInfo = $"Target ID: {_targetPlayerId.Value}";
}
else if (_coreTransform != null && _currentState.Value == EnemyAIState.MoveToCore)
{
targetInfo = "Target: Core (moving)";
}
string attackInfo = _isAttacking ? " [ATTACKING]" : "";
string distanceInfo = "";
if (_coreTransform != null)
{
float distToCore = GetDistanceToCoreSurface();
distanceInfo = $"\nDistToCore: {distToCore:F1} (Range: {attackRange})";
}
string fullInfo = $"{gameObject.name}\n{stateInfo}\n{targetInfo}{attackInfo}{distanceInfo}";
UnityEditor.Handles.Label(
transform.position + Vector3.up * 3f,
fullInfo,
new GUIStyle(GUI.skin.label) { fontSize = 12, normal = { textColor = Color.white } }
);
// 타겟으로 선 그리기
if (_cachedTargetPlayer != null)
{
Gizmos.color = Color.magenta;
Gizmos.DrawLine(transform.position + Vector3.up, _cachedTargetPlayer.transform.position + Vector3.up);
}
else if (_coreTransform != null && _currentState.Value == EnemyAIState.MoveToCore)
{
Gizmos.color = Color.cyan;
Gizmos.DrawLine(transform.position + Vector3.up, _coreTransform.position + Vector3.up);
}
#endif
}
#endregion
}
}