From 2cf0d6b1a9e23f998ade03691facd8c6c9b4e46d Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 24 Jul 2026 11:58:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(enemy):=20EnemyAbilityBase=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20IAttackCandidate=20=E5=A5=91=E7=BA=A6(=E8=87=AA?= =?UTF-8?q?=E7=AE=A1=E5=9C=86=E5=BD=A2=E5=B0=84=E7=A8=8B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Enemies/Abilities/EnemyAbilityBase.cs | 20 ++++++++++++++++++- .../Enemies/Abilities/IAttackCandidate.cs | 17 ++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Assets/_Game/Scripts/Enemies/Abilities/IAttackCandidate.cs diff --git a/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs b/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs index 5751efd6..98b7ec86 100644 --- a/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs +++ b/Assets/_Game/Scripts/Enemies/Abilities/EnemyAbilityBase.cs @@ -16,7 +16,7 @@ namespace BaseGames.Enemies.Abilities /// - 受击/死亡时由 调用 。 /// [DisallowMultipleComponent] - public abstract class EnemyAbilityBase : MonoBehaviour + public abstract class EnemyAbilityBase : MonoBehaviour, IAttackCandidate { [Header("配置 SO")] [SerializeField] protected EnemyAbilitySO _config; @@ -43,6 +43,24 @@ namespace BaseGames.Enemies.Abilities /// BD 任务统一查询入口:当前是否可用(冷却完毕且未执行中)。 public virtual bool CanUse => !_isRunning && !IsOnCooldown && _enemy != null && _enemy.IsAlive; + // ── IAttackCandidate(供 EnemyAttackSelector 选招)────────────────── + public bool RequiresLineOfSight => _config != null && _config.requiresLineOfSight; + public bool RequiresGrounded => _config != null && _config.requiresGrounded; + public float Weight => _config != null ? _config.weight : 0f; + public int Priority => _config != null ? _config.priority : 0; + + /// 本招圆形攻击范围内是否有玩家(招式自管其射程;LOS 复用敌人级 IsPlayerVisible)。 + public virtual bool InAttackRange() + { + if (_config == null || _config.rangeRadius <= 0f + || _enemy == null || _enemy.PlayerTransform == null) return false; + float sign = _transform.localScale.x < 0f ? -1f : 1f; + Vector2 origin = (Vector2)_transform.position + + new Vector2(_config.rangeOffset.x * sign, _config.rangeOffset.y); + float r = _config.rangeRadius; + return ((Vector2)_enemy.PlayerTransform.position - origin).sqrMagnitude <= r * r; + } + protected virtual void Awake() { _enemy = GetComponentInParent(); diff --git a/Assets/_Game/Scripts/Enemies/Abilities/IAttackCandidate.cs b/Assets/_Game/Scripts/Enemies/Abilities/IAttackCandidate.cs new file mode 100644 index 00000000..2cb61610 --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Abilities/IAttackCandidate.cs @@ -0,0 +1,17 @@ +namespace BaseGames.Enemies.Abilities +{ + /// + /// 攻击选择器的候选契约。攻击类 实现之; + /// 抽象出来使 可脱离 MonoBehaviour 单测。 + /// + public interface IAttackCandidate + { + bool CanUse { get; } // 未冷却 + 未运行 + 存活 + bool RequiresLineOfSight { get; } // 是否需要视线(由敌人级 LOS 提供) + bool RequiresGrounded { get; } // 是否需要着地 + float Weight { get; } // WeightedRandom 权重 + int Priority { get; } // Priority 优先级 + bool InAttackRange(); // 招式自管圆形射程内是否有玩家 + bool Execute(); // 触发本招 + } +}