From 52881e4d7531ea4f83bf37dead9fd6916092b761 Mon Sep 17 00:00:00 2001 From: NTG_Lenovo Date: Wed, 6 Aug 2025 18:58:03 +0900 Subject: [PATCH] =?UTF-8?q?AiMovement=20=EA=B5=AC=EC=A1=B0=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RestaurantCharacter/IAiMovement.cs | 26 ++++ .../Npc/RestaurantNpcMovement.cs | 146 ++++++++++++++++++ .../Player/RestaurantPlayerMovement.cs | 4 +- .../RestaurantCharacterMovement.cs | 2 +- 4 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 Assets/_DDD/_Scripts/RestaurantCharacter/IAiMovement.cs create mode 100644 Assets/_DDD/_Scripts/RestaurantCharacter/Npc/RestaurantNpcMovement.cs diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/IAiMovement.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/IAiMovement.cs new file mode 100644 index 000000000..0b7eed70e --- /dev/null +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/IAiMovement.cs @@ -0,0 +1,26 @@ +using UnityEngine; + +namespace DDD +{ + public interface IAiMovement + { + Vector3 CurrentPosition { get; } + Vector3 Destination { get; } + float CurrentSpeed { get; } + bool IsMoving { get; } + + void EnableMove(); + void DisableMove(); + void PlayMove(); + void StopMove(); + void SetMoveSpeed(float speed); + + bool TryMoveToPosition(Vector3 position); + bool TryMoveToTarget(Collider targetCollider); + Vector3 GetRandomBetweenTwoPoints(Vector2? normalizedRange = null); + bool TryTeleportToPosition(Vector3 position); + bool HasReachedDestination(); + bool IsPositionMovable(Vector3 endPosition); + bool TryMoveToRandomPositionInRange(float range, int graphIndex = 0); + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/Npc/RestaurantNpcMovement.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/Npc/RestaurantNpcMovement.cs new file mode 100644 index 000000000..64a0f0590 --- /dev/null +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/Npc/RestaurantNpcMovement.cs @@ -0,0 +1,146 @@ +using Pathfinding; +using UnityEngine; + +namespace DDD +{ + public class RestaurantNpcMovement : RestaurantCharacterMovement, IAiMovement + { + private IAstarAI _iAstarAi; + + protected override void Awake() + { + base.Awake(); + + _iAstarAi = GetComponent(); + Debug.Assert(_iAstarAi != null, "_iAstarAi is null"); + } + + private const int MaxRandomMoveAttempts = 1000; + + public Vector3 CurrentPosition => _iAstarAi.position; + public Vector3 Destination => _iAstarAi.destination; + public float CurrentSpeed => _iAstarAi.velocity.magnitude; + public bool IsMoving => !_iAstarAi.isStopped && _iAstarAi.hasPath; + + public void EnableMove() + { + _iAstarAi.canMove = true; + } + + public void DisableMove() + { + _iAstarAi.canMove = false; + } + + public void PlayMove() + { + _iAstarAi.isStopped = false; + } + + public void StopMove() + { + _iAstarAi.isStopped = true; + _iAstarAi.SetPath(null); + } + + public void SetMoveSpeed(float speed) + { + _iAstarAi.maxSpeed = speed; + } + + public bool TryMoveToPosition(Vector3 position) + { + if (IsPositionMovable(position) == false) + { + Debug.LogWarning($"{gameObject.name}이 이동 불가능한 위치를 대상으로 이동을 시도함"); + return false; + } + + _iAstarAi.destination = position; + PlayMove(); + return true; + } + + public bool TryMoveToTarget(Collider targetCollider) + { + if (targetCollider == null) + { + Debug.LogWarning($"{gameObject.name}이 이동하려는 타겟을 찾지 못함"); + StopMove(); + return false; + } + + return TryMoveToPosition(targetCollider.transform.position); + } + + public Vector3 GetRandomBetweenTwoPoints(Vector2? normalizedRange = null) + { + var range = normalizedRange ?? new Vector2(0.2f, 0.8f); + var randomFactor = Random.Range(range.x, range.y); + return Vector3.Lerp(_iAstarAi.position, _iAstarAi.destination, randomFactor); + } + + public bool TryTeleportToPosition(Vector3 position) + { + if (IsPositionMovable(position) == false) + { + Debug.LogWarning($"{gameObject.name}오브젝트가 이동 불가능한 위치로 텔레포트 시도됨"); + return false; + } + + _iAstarAi.Teleport(position); + return true; + } + + public bool HasReachedDestination() + { + return _iAstarAi.pathPending == false && _iAstarAi.reachedEndOfPath; + } + + public bool IsPositionMovable(Vector3 endPosition) + { + var nearestNode = AstarPath.active.GetNearest(endPosition).node; + return nearestNode != null && nearestNode.Walkable; + } + + public bool TryMoveToRandomPositionInRange(float range, int graphIndex = 0) + { + if (graphIndex < 0 || graphIndex >= AstarPath.active.graphs.Length) + { + Debug.LogWarning($"{gameObject.name} - 유효하지 않은 그래프 인덱스: {graphIndex}"); + return false; + } + + int attempts = 0; + Vector3 randomPosition; + var isMovable = false; + var graphBounds = AstarPath.active.graphs[graphIndex].bounds; + + do + { + var randomDirection = Random.insideUnitCircle.normalized; + var randomOffset = new Vector3(randomDirection.x, 0, randomDirection.y) * Random.Range(0, range); + + randomPosition = _iAstarAi.position + randomOffset; + if (!graphBounds.Contains(randomPosition)) + { + continue; + } + + isMovable = IsPositionMovable(randomPosition); + + attempts++; + } while (!isMovable && attempts < MaxRandomMoveAttempts); + + if (isMovable == false) + { + Debug.LogWarning($"{gameObject.name}오브젝트의 랜덤 위치 탐색 실패"); + return false; + } + + _iAstarAi.destination = randomPosition; + PlayMove(); + return true; + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/Player/RestaurantPlayerMovement.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/Player/RestaurantPlayerMovement.cs index 9a3c14013..0ec205d5f 100644 --- a/Assets/_DDD/_Scripts/RestaurantCharacter/Player/RestaurantPlayerMovement.cs +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/Player/RestaurantPlayerMovement.cs @@ -35,8 +35,10 @@ public class RestaurantPlayerMovement : RestaurantCharacterMovement #region Unity Lifecycle - private void Awake() + protected override void Awake() { + base.Awake(); + InitializeComponents(); } diff --git a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterMovement.cs b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterMovement.cs index 3493afb94..19920b0b2 100644 --- a/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterMovement.cs +++ b/Assets/_DDD/_Scripts/RestaurantCharacter/RestaurantCharacterMovement.cs @@ -5,7 +5,7 @@ namespace DDD public class RestaurantCharacterMovement : MonoBehaviour { private RestaurantCharacterMovementConstraint _constraint; - private void Awake() + protected virtual void Awake() { _constraint = gameObject.AddComponent(); }