From 0828bb214f22eb56a089d8c90565a5fe484320dc Mon Sep 17 00:00:00 2001 From: dal4segno Date: Wed, 25 Feb 2026 17:26:03 +0900 Subject: [PATCH] =?UTF-8?q?enemy=EC=9D=98=20=EC=9D=B4=EB=8F=99=20=EA=B0=80?= =?UTF-8?q?=EC=86=8D=EC=9D=84=20=EB=86=92=EC=9D=B4=EA=B3=A0,=20=EA=B3=B5?= =?UTF-8?q?=EA=B2=A9=20=EB=8C=80=EC=83=81=EC=9D=98=20=ED=91=9C=EB=A9=B4?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=EB=B6=80=ED=84=B0=20=EA=B1=B0=EB=A6=AC=20?= =?UTF-8?q?=EA=B2=80=EC=82=AC=EB=A5=BC=20=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 미끄러지는 현상 감소 코어 앞에서 코어를 못 찾는 문제 수정 --- Assets/Scripts/EnemyAIController.cs | 126 ++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 16 deletions(-) diff --git a/Assets/Scripts/EnemyAIController.cs b/Assets/Scripts/EnemyAIController.cs index baa2eb8..08df0c6 100644 --- a/Assets/Scripts/EnemyAIController.cs +++ b/Assets/Scripts/EnemyAIController.cs @@ -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 /// private void PerformAttack() { - if (_pendingAttackTarget == null) return; + if (_pendingAttackTarget == null) + { + if (showDebugInfo) + { + Debug.LogWarning("[EnemyAIController] PerformAttack: 타겟이 없음"); + } + return; + } IDamageable damageable = _pendingAttackTarget.GetComponentInChildren(); if (damageable == null) damageable = _pendingAttackTarget.GetComponent(); @@ -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 } } \ No newline at end of file