몬스터가 코어를 제대로 공격하지 못하는 문제 수정
navMesh는 다시 이전으로 되돌림 코어의 중심이 아닌, 코어의 표면을 기준으로 공격하도록 함
This commit is contained in:
@@ -2821,6 +2821,10 @@ PrefabInstance:
|
|||||||
propertyPath: GlobalObjectIdHash
|
propertyPath: GlobalObjectIdHash
|
||||||
value: 1153289458
|
value: 1153289458
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 6473738761679328420, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3}
|
||||||
|
propertyPath: m_Convex
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
- target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3}
|
- target: {fileID: 6727958582834582256, guid: ace4a11b046dac9498e6897c2b5eb245, type: 3}
|
||||||
propertyPath: m_LocalPosition.x
|
propertyPath: m_LocalPosition.x
|
||||||
value: 10
|
value: 10
|
||||||
@@ -3027,6 +3031,10 @@ PrefabInstance:
|
|||||||
propertyPath: GlobalObjectIdHash
|
propertyPath: GlobalObjectIdHash
|
||||||
value: 1738409839
|
value: 1738409839
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 9130994837512732106, guid: 368961a0f2d71ce4aad5d8ffe52e0b7f, type: 3}
|
||||||
|
propertyPath: m_Convex
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
m_RemovedComponents: []
|
m_RemovedComponents: []
|
||||||
m_RemovedGameObjects: []
|
m_RemovedGameObjects: []
|
||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
@@ -3415,6 +3423,14 @@ PrefabInstance:
|
|||||||
propertyPath: m_Name
|
propertyPath: m_Name
|
||||||
value: Core
|
value: Core
|
||||||
objectReference: {fileID: 0}
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8929210192865157768, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3}
|
||||||
|
propertyPath: m_Convex
|
||||||
|
value: 0
|
||||||
|
objectReference: {fileID: 0}
|
||||||
|
- target: {fileID: 8929210192865157768, guid: e56926eda34629f4fbf3e4c53f0f8bd4, type: 3}
|
||||||
|
propertyPath: m_Enabled
|
||||||
|
value: 1
|
||||||
|
objectReference: {fileID: 0}
|
||||||
m_RemovedComponents: []
|
m_RemovedComponents: []
|
||||||
m_RemovedGameObjects: []
|
m_RemovedGameObjects: []
|
||||||
m_AddedGameObjects: []
|
m_AddedGameObjects: []
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
|||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.AI;
|
||||||
|
|
||||||
namespace Northbound
|
namespace Northbound
|
||||||
{
|
{
|
||||||
@@ -332,5 +333,44 @@ namespace Northbound
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Navigation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AI가 코어에 접근할 수 있는 NavMesh 위치 반환 (콜라이더 표면 근처)
|
||||||
|
/// </summary>
|
||||||
|
public Vector3 GetNavMeshPosition()
|
||||||
|
{
|
||||||
|
Collider coreCollider = GetComponent<Collider>();
|
||||||
|
if (coreCollider == null)
|
||||||
|
{
|
||||||
|
return transform.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 코어 콜라이더 표면에서 남쪽(앞쪽) 방향으로 검색 시작
|
||||||
|
// 적들이 보통 남쪽에서 오기 때문에 전면부 검색
|
||||||
|
Vector3 searchDirection = Vector3.back; // -Z 방향 (남쪽)
|
||||||
|
Vector3 searchCenter = coreCollider.ClosestPoint(transform.position + searchDirection * 5f);
|
||||||
|
|
||||||
|
// 2. 표면 방향으로 attackRange(약 2m)보다 조금 더 큰 거리에서 NavMesh 샘플링
|
||||||
|
NavMeshHit hit;
|
||||||
|
float maxDistance = 3f; // 콜라이더 표면에서 탐색할 거리
|
||||||
|
|
||||||
|
if (NavMesh.SamplePosition(searchCenter, out hit, maxDistance, NavMesh.AllAreas))
|
||||||
|
{
|
||||||
|
return hit.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 실패 시 코어 중심에서 더 넓게 탐색 (fallback)
|
||||||
|
if (NavMesh.SamplePosition(transform.position, out hit, 10f, NavMesh.AllAreas))
|
||||||
|
{
|
||||||
|
return hit.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogWarning("[Core] 코어 주변에서 NavMesh를 찾을 수 없습니다.");
|
||||||
|
return transform.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,6 +63,7 @@ namespace Northbound
|
|||||||
|
|
||||||
private NavMeshAgent _agent;
|
private NavMeshAgent _agent;
|
||||||
private EnemyUnit _enemyUnit;
|
private EnemyUnit _enemyUnit;
|
||||||
|
private Core _core;
|
||||||
private Transform _coreTransform;
|
private Transform _coreTransform;
|
||||||
private Collider _coreCollider;
|
private Collider _coreCollider;
|
||||||
private Vector3 _originPosition;
|
private Vector3 _originPosition;
|
||||||
@@ -99,7 +100,7 @@ namespace Northbound
|
|||||||
_agent.speed = moveSpeed;
|
_agent.speed = moveSpeed;
|
||||||
_agent.acceleration = 8f;
|
_agent.acceleration = 8f;
|
||||||
_agent.angularSpeed = 120f;
|
_agent.angularSpeed = 120f;
|
||||||
_agent.stoppingDistance = attackRange * 0.7f;
|
_agent.stoppingDistance = attackRange * 0.9f; // 공격 범위까지 더 가까이 이동
|
||||||
_agent.autoBraking = true;
|
_agent.autoBraking = true;
|
||||||
_agent.updateRotation = true;
|
_agent.updateRotation = true;
|
||||||
_agent.updateUpAxis = false;
|
_agent.updateUpAxis = false;
|
||||||
@@ -164,7 +165,22 @@ namespace Northbound
|
|||||||
if (_isRecalculatingPath) return; // 코루틴 대기 중이면 중단
|
if (_isRecalculatingPath) return; // 코루틴 대기 중이면 중단
|
||||||
if (_coreTransform == null) { FindCore(); return; }
|
if (_coreTransform == null) { FindCore(); return; }
|
||||||
|
|
||||||
// 0. Player 감지 (코어로 가는 도중에도 Player를 타겟팅)
|
// 0. 경로 계산 중이거나 이동 중이 아니면 아무것도 하지 않음
|
||||||
|
if (_agent.pathPending) return; // 경로 계산 중
|
||||||
|
|
||||||
|
// 0.5. 실제로 이동 중인지 확인 (velocity로 판단)
|
||||||
|
if (_agent.hasPath && _agent.velocity.sqrMagnitude > 0.01f)
|
||||||
|
{
|
||||||
|
// 이동 중이면 코어 콜라이더 표면까지의 거리로 도달 확인
|
||||||
|
if (GetDistanceToCoreSurface() <= attackRange)
|
||||||
|
{
|
||||||
|
SetTargetPlayer(_coreTransform.gameObject);
|
||||||
|
TransitionToState(EnemyAIState.Attack);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Player 감지 (코어로 가는 도중에도 Player를 타겟팅)
|
||||||
GameObject detectedPlayer = DetectTarget();
|
GameObject detectedPlayer = DetectTarget();
|
||||||
if (detectedPlayer != null)
|
if (detectedPlayer != null)
|
||||||
{
|
{
|
||||||
@@ -173,26 +189,19 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 코어로 가는 경로가 '완전(Complete)'한지 먼저 확인
|
// 2. 코어로 가는 경로가 '완전(Complete)'한지 확인
|
||||||
// NavMesh가 갱신되었다면 에이전트는 즉시 Complete 상태가 됩니다.
|
// NavMesh가 갱신되었다면 에이전트는 즉시 Complete 상태가 됩니다.
|
||||||
if (_agent.hasPath && _agent.pathStatus == NavMeshPathStatus.PathComplete)
|
if (_agent.hasPath && _agent.pathStatus == NavMeshPathStatus.PathComplete)
|
||||||
{
|
{
|
||||||
if (!_hasSetCoreDestination)
|
if (!_hasSetCoreDestination)
|
||||||
{
|
{
|
||||||
_agent.SetDestination(_coreTransform.position);
|
_agent.SetDestination(_core.GetNavMeshPosition());
|
||||||
_hasSetCoreDestination = true;
|
_hasSetCoreDestination = true;
|
||||||
}
|
}
|
||||||
// [중요] 길이 열렸으므로 아래의 장애물 탐지 로직을 아예 실행하지 않고 리턴!
|
// [중요] 길이 열렸으므로 아래의 장애물 탐지 로직을 아예 실행하지 않고 리턴!
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 코어 도달 확인
|
|
||||||
if (GetDistanceToCoreSurface() <= attackRange)
|
|
||||||
{
|
|
||||||
TransitionToState(EnemyAIState.Attack);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 길이 막혔을 때(Partial)만 아주 좁은 범위에서 장애물을 찾음
|
// 3. 길이 막혔을 때(Partial)만 아주 좁은 범위에서 장애물을 찾음
|
||||||
GameObject obstacle = DetectObstacle();
|
GameObject obstacle = DetectObstacle();
|
||||||
if (obstacle != null)
|
if (obstacle != null)
|
||||||
@@ -205,7 +214,7 @@ namespace Northbound
|
|||||||
// 4. 경로가 유효하지 않을 때만 재설정
|
// 4. 경로가 유효하지 않을 때만 재설정
|
||||||
if (!_agent.hasPath || _agent.pathStatus == NavMeshPathStatus.PathInvalid)
|
if (!_agent.hasPath || _agent.pathStatus == NavMeshPathStatus.PathInvalid)
|
||||||
{
|
{
|
||||||
_agent.SetDestination(_coreTransform.position);
|
_agent.SetDestination(_core.GetNavMeshPosition());
|
||||||
_hasSetCoreDestination = true;
|
_hasSetCoreDestination = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -267,12 +276,23 @@ namespace Northbound
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 핵심: ClosestPoint를 사용해 '벽 표면'까지의 실제 거리 계산
|
// 코어 공격 시에는 도달 시점부터 항상 공격 가능하다고 가정 (거리 체크 생략)
|
||||||
|
bool isCoreTarget = (target == _coreTransform?.gameObject);
|
||||||
|
|
||||||
|
if (!isCoreTarget)
|
||||||
|
{
|
||||||
|
// 일반 타겟(플레이어, 장애물)은 거리 체크 필요
|
||||||
float distance = GetDistanceToTarget(target);
|
float distance = GetDistanceToTarget(target);
|
||||||
|
|
||||||
// 코어 혹은 일반 타겟 공격 범위 체크 (공격 시 약간의 거리 여유 1.2f 부여)
|
// 공격 범위 체크 (공격 시 약간의 거리 여유 1.2f 부여)
|
||||||
if (distance <= attackRange * 1.2f)
|
if (distance > attackRange * 1.2f)
|
||||||
{
|
{
|
||||||
|
// 거리가 멀어지면 추적 상태로 전환
|
||||||
|
TransitionToState(EnemyAIState.ChasePlayer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 타겟 바라보기
|
// 타겟 바라보기
|
||||||
Vector3 direction = (target.transform.position - transform.position).normalized;
|
Vector3 direction = (target.transform.position - transform.position).normalized;
|
||||||
direction.y = 0;
|
direction.y = 0;
|
||||||
@@ -287,13 +307,6 @@ namespace Northbound
|
|||||||
AttackTarget(target);
|
AttackTarget(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// 거리가 멀어지면 타겟 종류에 따라 상태 전환
|
|
||||||
if (target == _coreTransform?.gameObject) TransitionToState(EnemyAIState.MoveToCore);
|
|
||||||
else TransitionToState(EnemyAIState.ChasePlayer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateReturnToOrigin()
|
private void UpdateReturnToOrigin()
|
||||||
{
|
{
|
||||||
@@ -503,9 +516,9 @@ namespace Northbound
|
|||||||
case EnemyAIState.MoveToCore:
|
case EnemyAIState.MoveToCore:
|
||||||
_agent.isStopped = false;
|
_agent.isStopped = false;
|
||||||
_agent.speed = moveSpeed;
|
_agent.speed = moveSpeed;
|
||||||
if (_coreTransform != null)
|
if (_core != null)
|
||||||
{
|
{
|
||||||
_agent.SetDestination(_coreTransform.position);
|
_agent.SetDestination(_core.GetNavMeshPosition());
|
||||||
_hasSetCoreDestination = true;
|
_hasSetCoreDestination = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -579,11 +592,25 @@ namespace Northbound
|
|||||||
|
|
||||||
private void FindCore()
|
private void FindCore()
|
||||||
{
|
{
|
||||||
Core core = FindFirstObjectByType<Core>();
|
_core = FindFirstObjectByType<Core>();
|
||||||
if (core != null)
|
if (_core != null)
|
||||||
{
|
{
|
||||||
_coreTransform = core.transform;
|
_coreTransform = _core.transform;
|
||||||
_coreCollider = core.GetComponent<Collider>() ?? core.GetComponentInChildren<Collider>();
|
// 물리적 충돌을 막는 MeshCollider를 우선 찾기 (Trigger 콜라이더 무시)
|
||||||
|
Collider[] allColliders = _core.GetComponentsInChildren<Collider>();
|
||||||
|
foreach (Collider col in allColliders)
|
||||||
|
{
|
||||||
|
if (!col.isTrigger)
|
||||||
|
{
|
||||||
|
_coreCollider = col;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: 모든 콜라이더가 Trigger이면 첫 번째 콜라이더 사용
|
||||||
|
if (_coreCollider == null && allColliders.Length > 0)
|
||||||
|
{
|
||||||
|
_coreCollider = allColliders[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ NavMeshProjectSettings:
|
|||||||
cost: 1
|
cost: 1
|
||||||
m_LastAgentTypeID: -887442657
|
m_LastAgentTypeID: -887442657
|
||||||
m_Settings:
|
m_Settings:
|
||||||
- serializedVersion: 2
|
- serializedVersion: 3
|
||||||
agentTypeID: 0
|
agentTypeID: 0
|
||||||
agentRadius: 0.5
|
agentRadius: 0.05
|
||||||
agentHeight: 2
|
agentHeight: 2
|
||||||
agentSlope: 45
|
agentSlope: 45
|
||||||
agentClimb: 0.75
|
agentClimb: 0.75
|
||||||
@@ -84,7 +84,9 @@ NavMeshProjectSettings:
|
|||||||
cellSize: 0.16666667
|
cellSize: 0.16666667
|
||||||
manualTileSize: 0
|
manualTileSize: 0
|
||||||
tileSize: 256
|
tileSize: 256
|
||||||
accuratePlacement: 0
|
buildHeightMesh: 0
|
||||||
|
maxJobWorkers: 0
|
||||||
|
preserveTilesOutsideBounds: 0
|
||||||
debug:
|
debug:
|
||||||
m_Flags: 0
|
m_Flags: 0
|
||||||
m_SettingNames:
|
m_SettingNames:
|
||||||
|
|||||||
Reference in New Issue
Block a user