From 0f988c4cef6e9978fdaeeefb0bab714a57397010 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 10 Jul 2026 11:59:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(enemy):=20=E6=96=B0=E5=A2=9E=20EnemyLocomo?= =?UTF-8?q?tion=20=E6=89=A7=E8=A1=8C=E5=99=A8(Idle/Patrol-Wander/Face/Appr?= =?UTF-8?q?oach)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../BaseGames.Enemies.Navigation.asmdef | 1 + .../Enemies/Navigation/EnemyLocomotion.cs | 105 ++++++++++++++++++ .../Navigation/EnemyLocomotion.cs.meta | 11 ++ 3 files changed, 117 insertions(+) create mode 100644 Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs create mode 100644 Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs.meta diff --git a/Assets/_Game/Scripts/Enemies/Navigation/BaseGames.Enemies.Navigation.asmdef b/Assets/_Game/Scripts/Enemies/Navigation/BaseGames.Enemies.Navigation.asmdef index 97bce052..be5a1583 100644 --- a/Assets/_Game/Scripts/Enemies/Navigation/BaseGames.Enemies.Navigation.asmdef +++ b/Assets/_Game/Scripts/Enemies/Navigation/BaseGames.Enemies.Navigation.asmdef @@ -9,6 +9,7 @@ "rootNamespace": "BaseGames.Enemies.Navigation", "references": [ "BaseGames.Enemies", + "BaseGames.AI", "PathBerserker2d" ], "autoReferenced": true, diff --git a/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs b/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs new file mode 100644 index 00000000..23495d6e --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs @@ -0,0 +1,105 @@ +using UnityEngine; +using BaseGames.AI; + +namespace BaseGames.Enemies +{ + /// + /// 敌人移动执行器:唯一的移动/朝向入口。AI 状态与能力经 IEnemyLocomotion + /// 声明意图,本组件每帧把当前模式翻译为对 EnemyBase/IPathAgent 的调用。 + /// 取代散落的 MoveTo/StopMovement/FacePlayer/FaceTarget 直调与三个协程能力。 + /// + [DisallowMultipleComponent] + public sealed class EnemyLocomotion : MonoBehaviour, IEnemyLocomotion + { + [Header("巡逻策略(P6 实现 Pace/Waypoints;当前仅 Wander)")] + [SerializeField] private PatrolStrategy _patrolStrategy = PatrolStrategy.Wander; + + private EnemyBase _enemy; + private LocomotionMode _mode = LocomotionMode.Idle; + private Transform _approachTarget; + private Vector2 _facePoint; + + public LocomotionMode CurrentMode => _mode; + public bool IsMoving => _enemy != null && _enemy.Nav != null && _enemy.Nav.IsMoving; + + private void Awake() + { + _enemy = GetComponentInParent(); + if (_enemy == null) + Debug.LogError("[EnemyLocomotion] 找不到 EnemyBase。", this); + } + + // ── IEnemyLocomotion(声明意图,不立即 actuate 之外的副作用)── + public void SetMode(LocomotionMode mode) + { + if (_mode == mode) return; + _mode = mode; + if (mode == LocomotionMode.Idle) _enemy?.StopMovement(); + if (mode == LocomotionMode.Patrol && _enemy?.Stats != null) + _enemy.Nav?.SetSpeed(_enemy.Stats.WalkSpeed); + } + + public void Approach(Transform target) + { + _mode = LocomotionMode.Approach; + _approachTarget = target; + if (_enemy?.Stats != null) _enemy.Nav?.SetSpeed(_enemy.Stats.RunSpeed); + } + + public void MoveTo(Vector2 point) + { + _mode = LocomotionMode.Approach; + _approachTarget = null; + _enemy?.MoveTo(point); + } + + public void Face(Vector2 lookAt) + { + _mode = LocomotionMode.Face; + _facePoint = lookAt; + } + + public void Stop() + { + _mode = LocomotionMode.Idle; + _enemy?.StopMovement(); + } + + // ── 每帧把模式翻译成执行 ── + private void Update() + { + if (_enemy == null) return; + switch (_mode) + { + case LocomotionMode.Idle: + break; // SetMode(Idle) 已停;保持 + case LocomotionMode.Patrol: + TickPatrol(); + break; + case LocomotionMode.Face: + _enemy.StopMovement(); + _enemy.FaceTarget(_facePoint); + break; + case LocomotionMode.Approach: + if (_approachTarget != null) _enemy.MoveTo(_approachTarget.position); + break; + } + } + + private void TickPatrol() + { + var nav = _enemy.Nav; + if (nav == null) return; + switch (_patrolStrategy) + { + case PatrolStrategy.Wander: + if (!nav.IsMoving) nav.WalkToRandom(); + break; + // Pace / Waypoints:P6 实现 + default: + if (!nav.IsMoving) nav.WalkToRandom(); + break; + } + } + } +} diff --git a/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs.meta b/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs.meta new file mode 100644 index 00000000..5a6dd929 --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Navigation/EnemyLocomotion.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9201463979893884288d3645a607eb90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: