diff --git a/Assets/_Game/Data/Events/Boss/EVT_BossSkill.asset b/Assets/_Game/Data/Events/Boss/EVT_BossSkill.asset deleted file mode 100644 index 9b19d35e..00000000 --- a/Assets/_Game/Data/Events/Boss/EVT_BossSkill.asset +++ /dev/null @@ -1,15 +0,0 @@ -%YAML 1.1 -%TAG !u! tag:unity3d.com,2011: ---- !u!114 &11400000 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 0} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: 8230eab2acba8c24499b2d20df81adb7, type: 3} - m_Name: EVT_BossSkill - m_EditorClassIdentifier: - description: diff --git a/Assets/_Game/Data/Events/Boss/EVT_BossSkill.asset.meta b/Assets/_Game/Data/Events/Boss/EVT_BossSkill.asset.meta deleted file mode 100644 index a13164da..00000000 --- a/Assets/_Game/Data/Events/Boss/EVT_BossSkill.asset.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 8e2f5df884c7f624da572ab81162da4d -NativeFormatImporter: - externalObjects: {} - mainObjectFileID: 11400000 - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Core/Events/BossEvents.cs b/Assets/_Game/Scripts/Core/Events/BossEvents.cs index 589e79af..929d78b0 100644 --- a/Assets/_Game/Scripts/Core/Events/BossEvents.cs +++ b/Assets/_Game/Scripts/Core/Events/BossEvents.cs @@ -1,15 +1,5 @@ namespace BaseGames.Core.Events { - /// - /// Boss 技能事件负载。 - /// - [System.Serializable] - public struct BossSkillEvent - { - public string BossId; - public string SkillId; - } - /// /// Boss 阶段切换事件负载。 /// diff --git a/Assets/_Game/Scripts/Core/Events/BossSkillEventChannelSO.cs b/Assets/_Game/Scripts/Core/Events/BossSkillEventChannelSO.cs deleted file mode 100644 index 887cba37..00000000 --- a/Assets/_Game/Scripts/Core/Events/BossSkillEventChannelSO.cs +++ /dev/null @@ -1,7 +0,0 @@ -using UnityEngine; - -namespace BaseGames.Core.Events -{ - [CreateAssetMenu(menuName = "BaseGames/Events/BossSkill")] - public class BossSkillEventChannelSO : BaseEventChannelSO { } -} diff --git a/Assets/_Game/Scripts/Core/Events/BossSkillEventChannelSO.cs.meta b/Assets/_Game/Scripts/Core/Events/BossSkillEventChannelSO.cs.meta deleted file mode 100644 index 62966fd6..00000000 --- a/Assets/_Game/Scripts/Core/Events/BossSkillEventChannelSO.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8230eab2acba8c24499b2d20df81adb7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/_Game/Scripts/Editor/Events/CreateEventChannelAssets.cs b/Assets/_Game/Scripts/Editor/Events/CreateEventChannelAssets.cs index 990e607e..06e84c72 100644 --- a/Assets/_Game/Scripts/Editor/Events/CreateEventChannelAssets.cs +++ b/Assets/_Game/Scripts/Editor/Events/CreateEventChannelAssets.cs @@ -60,7 +60,6 @@ namespace BaseGames.Editor CreateAsset ("Combat", "EVT_ShieldRestored"); // ── Boss ────────────────────────────────────────────────────────── - CreateAsset ("Boss", "EVT_BossSkill"); CreateAsset ("Boss", "EVT_BossPhase"); CreateAsset ("Boss", "EVT_BossPhaseChanged"); CreateAsset ("Boss", "EVT_BossDefeated"); diff --git a/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs b/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs index d832b329..b09d1083 100644 --- a/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs +++ b/Assets/_Game/Scripts/Enemies/AIBrain/EnemyAiBrain.cs @@ -5,7 +5,7 @@ namespace BaseGames.Enemies { /// /// 把 BrainGraph 决策层挂到敌人上:按 id 取共享 AiGraph、构造 AiRuntime、逐帧推进。 - /// 取代旧的行为树组件。 + /// 是敌人唯一的决策层组件(小怪与 Boss 共用)。 /// [DisallowMultipleComponent] [RequireComponent(typeof(EnemyBase))] diff --git a/Assets/_Game/Scripts/Enemies/EnemyAnimationConfigSO.cs b/Assets/_Game/Scripts/Enemies/EnemyAnimationConfigSO.cs index 0e8ebca4..6d88953a 100644 --- a/Assets/_Game/Scripts/Enemies/EnemyAnimationConfigSO.cs +++ b/Assets/_Game/Scripts/Enemies/EnemyAnimationConfigSO.cs @@ -33,7 +33,7 @@ namespace BaseGames.Enemies public AnimationClip Investigate; /// - /// 按字段名返回 Clip(BD_PlayAnimation 使用)。 + /// 按字段名返回 Clip(AI 状态片段与能力模块按名取动画时使用)。 /// 支持字段名和常见别名(如 Attack_Melee / Idle 等)。 /// public AnimationClip GetClipByName(string name) diff --git a/Assets/_Game/Scripts/Enemies/EnemyPoiseComponent.cs b/Assets/_Game/Scripts/Enemies/EnemyPoiseComponent.cs index 035b8254..9e2bafbf 100644 --- a/Assets/_Game/Scripts/Enemies/EnemyPoiseComponent.cs +++ b/Assets/_Game/Scripts/Enemies/EnemyPoiseComponent.cs @@ -6,7 +6,7 @@ namespace BaseGames.Enemies /// /// 敌人霸体组件(架构 06_CombatModule §13)。 /// Awake 时自动注入到同节点 HurtBox(无需手动 Inspector 赋值)。 - /// EnemyBase 或 BehaviorDesigner 任务可调用 SetPoiseLevel / ResetPoiseLevel 切换超甲状态。 + /// EnemyBase、能力模块或动画事件可调用 SetPoiseLevel / ResetPoiseLevel 切换超甲状态。 /// [RequireComponent(typeof(EnemyBase))] public class EnemyPoiseComponent : MonoBehaviour, IPoiseSource @@ -31,7 +31,7 @@ namespace BaseGames.Enemies // ── 外部 API ────────────────────────────────────────────────────────── - /// 进入/退出超甲时由 EnemyBase 或 BehaviorDesigner 任务调用。 + /// 进入/退出超甲时由 EnemyBase、能力模块或动画事件调用。 public void SetPoiseLevel(PoiseLevel level) => _currentPoiseLevel = level; /// 恢复到默认霸体等级(超甲结束时调用)。 diff --git a/Assets/_Game/Scripts/Enemies/EnemyQuotaManager.cs b/Assets/_Game/Scripts/Enemies/EnemyQuotaManager.cs index f10fdcab..8b106edd 100644 --- a/Assets/_Game/Scripts/Enemies/EnemyQuotaManager.cs +++ b/Assets/_Game/Scripts/Enemies/EnemyQuotaManager.cs @@ -5,14 +5,16 @@ using BaseGames.Core.Events; namespace BaseGames.Enemies { /// - /// 敌人 BehaviorTree 配额管理器(架构 07_EnemyModule §13)。 + /// 敌人决策层配额管理器(架构 07_EnemyModule §13)。 /// 每 REBALANCE_INTERVAL 帧,按到玩家距离排序已注册敌人, - /// 仅对最近的 _maxActiveBehaviorTrees 个敌人启用 BT。 + /// 仅对最近的 _maxActiveBrains 个敌人启用 ,其余暂停决策与感知。 /// 挂载在场景管理器 GameObject 上(每场景一个实例)。 /// public class EnemyQuotaManager : MonoBehaviour { - [SerializeField, Min(1)] private int _maxActiveBehaviorTrees = 12; + [Tooltip("同时保持决策活跃的敌人上限;超出的按距离由远及近暂停")] + [UnityEngine.Serialization.FormerlySerializedAs("_maxActiveBehaviorTrees")] + [SerializeField, Min(1)] private int _maxActiveBrains = 12; [Tooltip("PlayerController.Start() 广播此频道,替代 FindWithTag")] [SerializeField] private TransformEventChannelSO _onPlayerSpawned; @@ -107,11 +109,11 @@ namespace BaseGames.Enemies if (enemy == null) { _registered.RemoveAt(i); continue; } var brain = enemy.Brain; - bool active = i < _maxActiveBehaviorTrees; + bool active = i < _maxActiveBrains; if (brain != null && brain.enabled != active) { - // 禁用 brain 即停其 Update/Tick,等价于旧的禁用行为树。 + // 禁用 brain 即停其 Update/Tick,敌人保持静止但仍存在于场景中。 brain.enabled = active; // 同步暂停/恢复感知系统,避免远处敌人无效 tick enemy.SensorHub?.SetSuspended(!active); diff --git a/Assets/_Game/Scripts/Enemies/FlyingEnemy.cs b/Assets/_Game/Scripts/Enemies/FlyingEnemy.cs index e5f5c34e..c4e57c16 100644 --- a/Assets/_Game/Scripts/Enemies/FlyingEnemy.cs +++ b/Assets/_Game/Scripts/Enemies/FlyingEnemy.cs @@ -8,7 +8,7 @@ namespace BaseGames.Enemies /// 飞行敌人基类。 /// /// 导航由 实现(IEnemyNavigator), - /// AI 行为逻辑由挂载的 Behavior Designer 树驱动。 + /// AI 决策由挂载的 (BrainGraph)驱动。 /// 本类仅负责: /// /// Rigidbody2D 无重力初始化 diff --git a/Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs b/Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs index d2334000..1996644c 100644 --- a/Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs +++ b/Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs @@ -16,7 +16,7 @@ namespace BaseGames.Enemies.Perception /// /// 视线检测(BatchLOS):敌我之间无遮挡时持续为 true。 - /// 由 PhysicsPerceptionSystem 自研批量计算(OverlapCircle + 单次 Raycast),BD_IsPlayerVisible 读取结果。 + /// 由 PhysicsPerceptionSystem 批量计算(OverlapCircle + 单次 Raycast),AI 图的可见性条件读取结果。 /// public const string LOS = "los"; diff --git a/Assets/_Game/Scripts/Enemies/RangedEnemy.cs b/Assets/_Game/Scripts/Enemies/RangedEnemy.cs index 9ccfd679..a7f0bb02 100644 --- a/Assets/_Game/Scripts/Enemies/RangedEnemy.cs +++ b/Assets/_Game/Scripts/Enemies/RangedEnemy.cs @@ -7,7 +7,7 @@ namespace BaseGames.Enemies { /// /// 远程敌人。重写 以从对象池生成抛射物。 - /// Behavior Designer 任务通过调用 SpawnProjectile(payload) 触发发射。 + /// 能力模块或动画事件通过调用 SpawnProjectile(payload) 触发发射。 /// public class RangedEnemy : EnemyBase { diff --git a/Assets/_Game/Scripts/Enemies/States/EnemyControlledState.cs b/Assets/_Game/Scripts/Enemies/States/EnemyControlledState.cs index f8f80827..95bc5657 100644 --- a/Assets/_Game/Scripts/Enemies/States/EnemyControlledState.cs +++ b/Assets/_Game/Scripts/Enemies/States/EnemyControlledState.cs @@ -2,7 +2,7 @@ namespace BaseGames.Enemies.States { /// /// 受控状态(默认行为)。 - /// 敌人处于 AI 正常控制下(Behavior Designer 运行中),无特殊 Enter/Exit 动画逻辑。 + /// 敌人处于 AI 正常控制下(决策层运行中),无特殊 Enter/Exit 动画逻辑。 /// public sealed class EnemyControlledState : IEnemyState { diff --git a/Docs/Architecture/23_BossSkillModule.md b/Docs/Architecture/23_BossSkillModule.md deleted file mode 100644 index e0291591..00000000 --- a/Docs/Architecture/23_BossSkillModule.md +++ /dev/null @@ -1,841 +0,0 @@ -# 23 · Boss 技能模块(Boss Skill Module) - -> **命名空间** `BaseGames.Boss` -> **程序集** `BaseGames.Enemy`(并入敌人程序集) -> **依赖** `BaseGames.Core.Events` · `BaseGames.Combat`(DamageInfo · HitBox)· `BaseGames.AI`(BossOrchestrator · BehaviorDesigner)· `Kybernetik.Animancer` -> **Design 来源** [47_BossSkillSystem](../Design/47_BossSkillSystem.md) - ---- - -## 目录 - -1. [模块职责与层级](#1-模块职责与层级) -2. [BossSkillType 枚举](#2-bossskilltype-枚举) -3. [VulnerabilityWindow](#3-vulnerabilitywindow) -4. [BossSkillSO](#4-bossskillso) -5. [AttackPatternSO](#5-attackpatternso) -6. [SkillSequenceSO](#6-skillsequenceso) -7. [BossSkillExecutor](#7-bossskillexecutor) -8. [WeakPointSystem](#8-weakpointsystem) -9. [BossOrchestrator 集成](#9-bossorchestrator-集成) -10. [设计规则一览](#10-设计规则一览) -11. [事件频道](#11-事件频道) - ---- - -## 1. 模块职责与层级 - -``` -数据层(ScriptableObject): - ┌─────────────────────────────────────────────────┐ - │ BossSkillSO │ - │ ├─ 技能元信息(类型/弱点窗口/互动标签) │ - │ └─ SkillSequenceSO[] 伤害序列 │ - │ └─ AttackPatternSO[] 单个攻击图案 │ - └─────────────────────────────────────────────────┘ - -运行时层(MonoBehaviour / UniTask): - BossOrchestrator(AI 决策,Behavior Designer 树) - └─ BossSkillExecutor(执行技能序列,处理 VulnerabilityWindow) - ├─ WeakPointSystem(弱点 HurtBox 激活管理) - └─ HitBox[](输出伤害) - -设计原则: - ① 伤害值只写在 AttackPatternSO,不在 BossSkillSO 重复 - ② 每个技能必须有 ≥1 VulnerabilityWindow(后摇 ≥0.5s 或专属弱点) - ③ 技能顺序由 BossOrchestrator 决定,BossSkillExecutor 只负责执行 -``` - ---- - -## 2. BossSkillCategory 和 BossSkillType 枚举 - -```csharp -namespace BaseGames.Boss -{ - /// 高层技能分类(平衡框架用)。 - public enum BossSkillCategory - { - Melee, Ranged, Charge, AoE, Environmental, Summon, - Buff, Debuff, Phase, Passive, Reactive - } - - /// 具体技能类型(战斗设计用)。 - public enum BossSkillType - { - // 基础攻击 - MeleeSlash, // 近战斩击 - ChargeAttack, // 冲刺撞击 - LeapSlam, // 跳起落地震 - ProjectileVolley, // 弹幕齐射 - LaserBeam, // 激光扫射 - - // 阶段技能 - PhaseTransition, // 阶段切换(无攻击,触发动画/护盾等) - SummonMinion, // 召唤小兵 - - // 特殊 - ArenaTrap, // 改变战斗区域地形 - SpeedBurst, // 速度爆发(数帧内免疫/高速) - DefensiveShell, // 防御壳(需攻击弱点) - } - - /// 互动标签:定义玩家可以对该技能执行哪些反制操作。 - [Flags] - public enum InteractionTag - { - None = 0, - Parryable = 1 << 0, // 可弹反 - PerfectParryOnly = 1 << 1, // 仅完美弹反有效 - DodgeWindow = 1 << 2, // 打开逃避窗口 - Unblockable = 1 << 3, // 无法拦截 - CanBeReflected = 1 << 4, // 弹幕可被反射 - ExposesWeakPoint = 1 << 5, // 暴露弱点 - GrantsPlayerReso = 1 << 6, // 多典:命中后给予玩家资源 - ArenaHazard = 1 << 7, // 场地危机(环境相关) - PhaseGate = 1 << 8, // 阶段门关(必须触发才进入下一阶段) - } -} -``` - ---- - -## 3. VulnerabilityWindow - -```csharp -namespace BaseGames.Boss -{ - /// 弱点出现时机类型。 - public enum VulnTriggerType - { - OnAttackRecovery, // 攻击后摇 - OnParriedSuccess, // 弹反成功 - OnCounterSkillHit, // 计算技能命中 - OnPhaseTransition, // 阶段切换时 - OnHazardBackfire, // 场地尃8个 - OnSummonDefeated, // 召唤物被击败 - Manual, // BossSkillExecutor 手动触发 - } - - /// 弱点位置类型。 - public enum WeakPointType - { - FullBody, // 主体全身都是弱点 - HeadOnly, // 仅头部 - BackOnly, // 仅背部 - CoreExposed, // 核心暴露(展开中心 HurtBox) - CustomPoint, // 自定义弱点 HurtBox - } - - [Serializable] - public struct VulnerabilityWindow - { - [Tooltip("弱点触发方式")] - public VulnTriggerType TriggerType; - - [Tooltip("触发后延迟出现(秒)")] - [Min(0f)] - public float TriggerDelay; - - [Tooltip("弱点持续时长(秒,设计约定 ≥0.5s)")] - [Min(0.1f)] - public float Duration; - - [Tooltip("弱点位置类型")] - public WeakPointType WeakPointType; - - [Tooltip("弱点激活时 Boss 的受击乘数(1 = 正常,>1 = 额外伤害)")] - [Min(0.1f)] - public float DamageMultiplier; - - [Tooltip("命中后强制驰恶")] - public bool ForceStagger; - - [Tooltip("驰恶时间(秒),ForceStagger=true 时生效")] - [Min(0f)] - public float StaggerDuration; - - [Tooltip("弱点开启时播放的 Feedback(光效等)")] - public MMF_Player OpenFeedback; - - [Tooltip("弱点关闭时播放的 Feedback")] - public MMF_Player CloseFeedback; - - [Tooltip("弱点激活时的高亮颜色(指示玩家该攻击哪里)")] - public Color HighlightColor; - - // 与旧字段对应(保留为展示用) - public bool ActivateWeakPointHurtBox => WeakPointType != WeakPointType.FullBody; - } -} -``` - ---- - -## 4. BossSkillSO - -```csharp -namespace BaseGames.Boss -{ - [CreateAssetMenu(menuName = "Boss/BossSkill")] - public class BossSkillSO : ScriptableObject - { - [Header("元信息")] - public string skillId; - public string displayName; - [TextArea(1, 4)] - public string designNote; // 设计师注释(不进游戏) - - [Header("技能分类")] - public BossSkillCategory category; // 高层分类(Melee/Ranged 等) - public BossSkillType skillType; // 具体技能类型 - - [Header("阶段可用性")] - [Tooltip("和数据层 BossPhaseConfigSO.PhaseIndex 对应;空 = 全阶段可用")] - public int[] availablePhaseIndices; // 空数组 = 全阶段可用 - - [Header("核心攻击动作引用")] - [Tooltip("构成此技能的 AttackPatternSO 序列(单个技能 = 长度 1;连击/多段 = 多个)")] - public AttackPatternSO[] attackPatterns; // 按执行顺序排列 - - [Header("弱点窗口(至少 1 个)")] - public VulnerabilityWindow[] vulnerabilityWindows; - - [Header("互动标签(与谜题/道具联动)")] - public InteractionTag interactionTags; // [Flags] 枚举,替代旧 string interactionTag - - [Header("连段(执行中的子序列)")] - public SkillSequenceSO sequenceOnHit; // 被玩家攻击弱点时触发的序列(可空) - public SkillSequenceSO sequenceOnMiss; // 弱点未被攻击时触发(可空) - - [Header("玩家反制接口")] - [Tooltip("被不同玩家行为反制时 Boss 的应激反应(见 §4.1)")] - public PlayerCounterResponse[] counterResponses; - - [Header("场景联动")] - [Tooltip("此技能执行时触发的场景事件(见 §4.2)")] - public ArenaEventTrigger[] arenaEvents; - - [Header("Boss 资源")] - [Tooltip("使用此技能消耗的 Boss 自身资源(见 §7)")] - public BossResourceCost resourceCost; - [Tooltip("使用此技能后是否积累愤怒值")] - public bool buildsRage; - - [Header("霸体配置")] - [Tooltip("此技能执行期间的霸体窗口(见 54_PoiseSystem §8);None = 可被打断")] - public PoiseWindowConfig poiseWindow; - - [Header("动画")] - public ClipTransition skillAnimation; // Animancer ClipTransition(技能播放动画) - - [Header("冷却")] - [Min(0f)] - public float cooldown; // 秒,0 = 无冷却(由 BossOrchestrator 决策层管理) - } -} -``` - -### 4.1 PlayerCounterResponse — 玩家反制接口 - -```csharp -namespace BaseGames.Boss -{ - [Serializable] - public struct PlayerCounterResponse - { - [Header("反制条件")] - public CounterType counterType; // 玩家用什么行为触发反制 - public string requiredSkillId; // counterType = SpecificSkill 时填写技能 ID - - [Header("反制效果(对 Boss)")] - public float bossStaggerDuration; // 强制硬直时长(秒,0 = 不强制) - public float bossDamageBonus; // 对 Boss 的额外伤害倍率(0 = 不额外) - public bool openVulnWindow; // 是否触发 VulnerabilityWindow - public bool interruptSkill; // 是否打断 Boss 当前技能 - - [Header("反制收益(对玩家)")] - public int soulPowerGrant; // 给予玩家的灵力数量 - public int spiritPowerGrant; // 给予玩家的魄元数量 - public MMF_Player counterFeedback; // 成功反制的特效/音效反馈 - } - - public enum CounterType - { - Parry, // 弹反成功 - PerfectParry, // 完美弹反 - DodgeThrough, // 冲刺穿越攻击(无敌帧通过) - SpecificSkill, // 使用特定玩家技能(填 requiredSkillId) - WeakPointHit, // 命中暴露的弱点 - HazardBackfire, // 利用场景危险伤到 Boss - SummonKill, // 击败召唤物 - } -} -``` - -### 4.2 ArenaEventTrigger — 场景联动 - -```csharp -namespace BaseGames.Boss -{ - [Serializable] - public struct ArenaEventTrigger - { - public string targetArenaObjectId; // 要影响的场景物件 ID(空 = 广播给所有) - public ArenaEventType eventType; - public float delay; // 从技能触发到场景事件的延迟(秒) - public ArenaEventParams parameters; - } - - public enum ArenaEventType - { - DestroyPlatform, // 破坏指定平台 - ActivateHazard, // 激活陷阱(如喷火管) - DeactivateHazard, // 关闭陷阱 - SpawnHazardArea, // 生成持续危险区域(熔岩/毒液) - ShakeArena, // 场景震动 - ToggleLighting, // 切换场景光照 - SpawnPlatform, // 生成新平台(阶段 2 变化) - TriggerCutscene, // 触发小型过场 - } - - [Serializable] - public struct ArenaEventParams - { - public float duration; // 效果持续时间(0 = 永久) - public float intensity; // 强度(震动幅度、危险区域半径等) - public bool revertsOnPhaseEnd; // 阶段结束时是否恢复 - } - - [Serializable] - public struct ArenaEventData - { - public ArenaEventType type; - public ArenaEventParams parameters; - public string sourceSkillId; // 来源技能 ID(供场景物件做条件判断) - } - - /// - /// 场景中可被 Boss 技能交互的对象实现此接口。 - /// Boss 通过 ArenaEventBus 广播,场景物件监听并响应。 - /// - public interface IArenaInteractable - { - string ArenaObjectId { get; } - void OnBossArenaEvent(ArenaEventData data); - } -} -``` - -### 4.3 BossResourceCost — Boss 资源消耗 - -```csharp -namespace BaseGames.Boss -{ - [Serializable] - public struct BossResourceCost - { - public string resourceId; // 对应 BossResourceConfigSO.resourceId(如 "Rage") - public float cost; // 消耗量(0 = 不消耗资源) - public float minRequired; // 使用此技能的最低资源要求 - } - - [CreateAssetMenu(menuName = "Boss/ResourceConfig")] - public class BossResourceConfigSO : ScriptableObject - { - public string resourceId; // 如 "Rage" / "PhaseCharge" - public string displayName; - public float maxValue; - public float startValue; // 初始值(通常 0) - - [Header("自动变化")] - public float passiveRate; // 每秒自动变化量(正=增长/负=衰减,0=不变) - public float onTakeDamageGain; // 每受到 1 点伤害积累量 - public float onSkillUseGain; // 每使用一次技能积累量 - - [Header("满值效果")] - public bool autoTriggerOnFull; - public BossSkillSO fullTriggerSkill; - public float resetValueAfterTrigger; - } -} -``` - ---- - -## 5. AttackPatternSO - -```csharp -namespace BaseGames.Boss -{ - /// - /// 单个攻击图案的数据。 - /// 存放伤害/速度等实际参数,BossSkillSO 不存参数。 - /// - [CreateAssetMenu(menuName = "Boss/AttackPattern")] - public class AttackPatternSO : ScriptableObject - { - [Header("输出")] - public DamageSourceSO DamageSource; // ⚠️ HitBox.Activate() 需要 DamageSourceSO(架构 06 §4) - public float KnockbackAngle; // 击退角度(度)(基础击退力由 DamageSourceSO 内配置) - - [Header("弹幕(若为弹幕类型)")] - public AssetReferenceGameObject ProjectilePrefab; - public int ProjectileCount = 1; - public float SpreadAngle = 0f; // 散射角(度) - public float ProjectileSpeed = 8f; - - [Header("范围攻击(若为 AoE 类型)")] - public float AoERadius; - public Vector2 AoEOffset; // 相对于 Boss 的偏移 - - [Header("时序")] - public float WindupDuration; // 预备动作时间 - public float ActiveDuration; // HitBox 激活时长 - public float RecoveryDuration; // 后摇时长(VulnerabilityWindow 通常在此段) - } -} -``` - ---- - -## 6. SkillSequenceSO - -```csharp -namespace BaseGames.Boss -{ - /// - /// 有序攻击序列(一个技能内的多段连段)。 - /// - [CreateAssetMenu(menuName = "Boss/SkillSequence")] - public class SkillSequenceSO : ScriptableObject - { - [Serializable] - public struct SequenceStep - { - public AttackPatternSO pattern; - public float delayBeforeStep; // 执行本步前等待的秒数 - } - - public SequenceStep[] steps; - - [Header("序列完成后的行为")] - public bool RepeatIfPlayerInRange; // 若玩家仍在范围内则重复(Survival 关卡用) - public float RepeatDelay; - [Range(0, 10)] - public int MaxRepeatCount; // 0 = 无限 - } -} -``` - ---- - -## 7. BossSkillExecutor - -```csharp -namespace BaseGames.Boss -{ - /// - /// 挂在 Boss GameObject 上。 - /// 接收 BossOrchestrator 的指令,执行指定 BossSkillSO。 - /// 管理 VulnerabilityWindow 计时和 WeakPointSystem 激活。 - /// - public class BossSkillExecutor : MonoBehaviour - { - [SerializeField] HitBox[] _hitBoxes; // Boss 身上的所有 HitBox - [SerializeField] WeakPointSystem _weakPointSystem; - [SerializeField] AnimancerComponent _animancer; - [SerializeField] private string _bossId; // Boss 资产唯一 ID - [SerializeField] private BossSkillEventChannelSO _onBossSkillStarted; // 发布:技能开始 - [SerializeField] private BossSkillEventChannelSO _onBossSkillEnded; // 发布:技能结束 - // ⚠️ PlayerController 无 Instance(Architecture 05 §2);Boss 居小场景持有玩家 Transform 引用 - [SerializeField] private Transform _playerTransform; // 由 Inspector 指定玩家 Transform - - BossSkillSO _currentSkill; - AnimancerState _currentState; - bool _isExecuting; - CancellationTokenSource _cts; - - public bool IsExecuting => _isExecuting; - - // ── 公共 API ─────────────────────────────────────────────── - public async UniTask ExecuteSkill(BossSkillSO skill, CancellationToken ct) - { - if (_isExecuting) return; - _isExecuting = true; - _currentSkill = skill; - _onBossSkillStarted?.Raise(new BossSkillEvent { BossId = _bossId, SkillId = skill.skillId }); - - // 播放技能动画(Animancer ClipTransition,直接从 BossSkillSO 引用) - _currentState = _animancer.Play(skill.skillAnimation); - - // 执行 SkillSequenceSO(若挂载 sequenceOnMiss) - if (skill.sequenceOnMiss != null) - await ExecuteSequence(skill.sequenceOnMiss, ct); - - _isExecuting = false; - _onBossSkillEnded?.Raise(new BossSkillEvent { BossId = _bossId, SkillId = skill.skillId }); - } - - async UniTask ExecuteSequence(SkillSequenceSO seq, CancellationToken ct) - { - int repeatCount = 0; - do - { - foreach (var step in seq.steps) - { - ct.ThrowIfCancellationRequested(); - await UniTask.WaitForSeconds(step.delayBeforeStep, cancellationToken: ct); - await ExecutePattern(step.pattern, ct); - } - repeatCount++; - if (seq.RepeatDelay > 0) - await UniTask.WaitForSeconds(seq.RepeatDelay, cancellationToken: ct); - } - while (seq.RepeatIfPlayerInRange - && repeatCount < seq.MaxRepeatCount - && IsPlayerInRange() - && !ct.IsCancellationRequested); - } - - async UniTask ExecutePattern(AttackPatternSO pattern, CancellationToken ct) - { - // 预备 - await UniTask.WaitForSeconds(pattern.WindupDuration, cancellationToken: ct); - - // 激活 HitBox(⚠️ 以架构 06_CombatModule §4 为准:Activate(DamageSourceSO, Transform)) - foreach (var hb in _hitBoxes) - hb.Activate(pattern.DamageSource, transform); - - await UniTask.WaitForSeconds(pattern.ActiveDuration, cancellationToken: ct); - - // 关闭 HitBox - foreach (var hb in _hitBoxes) hb.Deactivate(); - - // 后摇(VulnerabilityWindow 由动画事件配合 VulnerabilityWindow 数据触发) - await UniTask.WaitForSeconds(pattern.RecoveryDuration, cancellationToken: ct); - } - - // ── VulnerabilityWindow 激活(由 ExecuteSkill 协程驱动)───── - // 注意:新版 VulnerabilityWindow 改用绝对时间(TriggerDelay + Duration), - // 而非归一化时间,故弃用 Update 轮询,改为 UniTask 序列激活。 - async UniTask ActivateVulnerabilityWindows(BossSkillSO skill, CancellationToken ct) - { - foreach (var window in skill.vulnerabilityWindows) - { - // 等待触发延迟 - await UniTask.Delay( - TimeSpan.FromSeconds(window.TriggerDelay), - cancellationToken: ct); - - // 激活弱点 - bool isCustom = window.ActivateWeakPointHurtBox; - _weakPointSystem.SetActive(true, window.DamageMultiplier, isCustom); - window.OpenFeedback?.PlayFeedbacks(); - - // 持续弱点窗口 - await UniTask.Delay( - TimeSpan.FromSeconds(window.Duration), - cancellationToken: ct); - - // 关闭弱点 - _weakPointSystem.SetActive(false, 1f, isCustom); - window.CloseFeedback?.PlayFeedbacks(); - } - } - - bool IsPlayerInRange() => - _playerTransform != null && - Vector2.Distance(transform.position, _playerTransform.position) < 8f; - } -} -``` - ---- - -## 8. WeakPointSystem - -```csharp -namespace BaseGames.Boss -{ - /// - /// 管理 Boss 的专属弱点 HurtBox(如核心、眼睛等)。 - /// 弱点激活期间受到的伤害会乘以 DamageMultiplier。 - /// - public class WeakPointSystem : MonoBehaviour - { - [Serializable] - public struct WeakPoint - { - public HurtBox hurtBox; - public GameObject visualIndicator; // 弱点亮光/标记(可为 null) - } - - [SerializeField] WeakPoint[] _weakPoints; - [SerializeField] private string _bossId; // Boss 资产唯一 ID - [SerializeField] private StringEventChannelSO _onVulnerabilityWindowOpened; // 发布:弱点窗口开启 - - float _damageMultiplier = 1f; - - public void SetActive(bool active, float multiplier = 1f) - { - _damageMultiplier = multiplier; - foreach (var wp in _weakPoints) - { - wp.hurtBox.gameObject.SetActive(active); - if (wp.visualIndicator != null) - wp.visualIndicator.SetActive(active); - } - if (active) _onVulnerabilityWindowOpened?.Raise(_bossId); - } - - /// - /// 弱点 HurtBox 受击时,由 BossStats 调用此方法获取最终伤害系数。 - /// - public float GetDamageMultiplier() => _damageMultiplier; - } -} -``` - ---- - -## 9. BossOrchestrator 集成 - -`BossOrchestrator` 是 Behavior Designer 的宿主 MonoBehaviour,持有 `BossSkillExecutor` 引用: - -```csharp -// BossOrchestrator 片段(供 BD 节点调用) -public class BossOrchestrator : MonoBehaviour -{ - [SerializeField] BossSkillSO[] _phaseOneSkills; - [SerializeField] BossSkillSO[] _phaseTwoSkills; - [SerializeField] BossSkillExecutor _executor; - - int _currentPhase = 1; - CancellationTokenSource _cts; - - // BD Task 节点调用:ExecuteSkill(skillId) - public async UniTask ExecuteSkillById(string skillId) - { - var skills = _currentPhase == 1 ? _phaseOneSkills : _phaseTwoSkills; - var skill = System.Array.Find(skills, s => s.skillId == skillId); - if (skill == null) return; - - _cts?.Cancel(); - _cts = new CancellationTokenSource(); - await _executor.ExecuteSkill(skill, _cts.Token); - } - - public void EnterPhaseTwo() - { - _currentPhase = 2; - _cts?.Cancel(); // 打断当前技能 - } -} -``` - ---- - -## 10. 设计规则一览 - -| 规则 | 说明 | -|------|------| -| 每个技能 ≥1 VulnerabilityWindow | 后摇窗口 ≥0.5s(归一化时长约 0.15~0.4) | -| 伤害参数只在 AttackPatternSO | BossSkillSO 不存 BaseDamage 等参数 | -| 技能冷却由 BossOrchestrator 管理 | BossSkillExecutor 只执行,不维护冷却 | -| VulnerabilityWindow 在编辑器中校验 | 自定义 Validator Tool 检查 DurationNormalized ≥ 0.1 | -| Boss 阶段切换打断当前技能 | `_cts.Cancel()` 终止 UniTask 序列 | - ---- - -## 11. 事件频道 - -| 频道 SO | Payload | 发布者 | 订阅者 | -|--------|---------|--------|--------| -| `EVT_BossSkillStarted` | `(string bossId, string skillId)` | `BossSkillExecutor` | `BossHUD`(显示技能名)(震动通过 `CameraStateController.Instance.TriggerImpulse()` 直接调用,不订阅事件)| -| `EVT_BossSkillEnded` | `(string bossId, string skillId)` | `BossSkillExecutor` | `BossOrchestrator`(BD 决策推进) | -| `EVT_BossVulnerabilityWindowOpened` | `string bossId` | `WeakPointSystem` | `PlayerFeedback`(提示音效)、`HUDController` | -| `EVT_BossPhaseChanged` | `(string bossId, int phase)` | `BossBase.EnterPhase()` | `BossHUD`(相机切换通过 `CameraStateController.Instance` 直接调用,不订阅事件)| - ---- - -## 12. BossSkillSequenceEditorWindow — 技能序列可视化 - -> **痛点**:`SkillSequenceSO` 包含多个 `SequenceStep[]`(每步:AttackPatternSO + delayBeforeStep),但 Inspector 中只能逐字段查看数字,策划无法直觉感受技能序列的节奏感——哪段是攻击前摇、哪段是弱点窗口、总时长是否合理,全部要靠心算。本 EditorWindow 以 **甘特图(Gantt Chart)** 方式将时间线可视化。 - -### 12.1 功能规格 - -| 功能 | 说明 | -|------|------| -| 时间轴刻度 | 横轴为时间(秒),最大显示 `SkillSequenceSO.TotalDuration`,刻度精度 0.1s | -| 攻击阶段条 | 每个 `AttackPatternSO` 渲染为蓝色横条,长度 = `pattern.Duration` | -| 延迟段 | `delayBeforeStep` 渲染为灰色空隙 | -| 弱点窗口 | `VulnerabilityWindow`(`startNormalized`~`endNormalized`)渲染为绿色覆盖条 | -| 选中高亮 | 点击任一条可选中对应 `AttackPatternSO`,Inspector 同步 Ping | -| 验证警告 | `VulnerabilityWindow.DurationNormalized < 0.1` 时对应条变红 + Tooltip 提示 | -| SO 拖放 | 将 `SkillSequenceSO` 资产拖入窗口即可加载 | - -### 12.2 实现规范 - -```csharp -// 路径: Assets/Scripts/Editor/BossSkill/BossSkillSequenceWindow.cs -// 程序集: BaseGames.Editor(Editor Only) -#if UNITY_EDITOR -using UnityEditor; -using UnityEngine; -using System.Collections.Generic; - -namespace BaseGames.Editor.BossSkill -{ - public class BossSkillSequenceWindow : EditorWindow - { - [MenuItem("BaseGames/Tools/Boss Skill Sequence Viewer")] - public static void Open() => GetWindow("技能序列查看器"); - - // ── 状态 ────────────────────────────────────────────────────────────── - private SkillSequenceSO _target; - private Vector2 _scroll; - private float _zoom = 100f; // 像素/秒 - private const float TrackHeight = 28f; - private const float LabelWidth = 140f; - private const float HeaderHeight = 30f; - - // 颜色 - private static readonly Color ColDelay = new(0.3f, 0.3f, 0.3f, 0.5f); - private static readonly Color ColAttack = new(0.2f, 0.5f, 1.0f, 0.8f); - private static readonly Color ColVulnOk = new(0.2f, 0.8f, 0.3f, 0.6f); - private static readonly Color ColVulnWarn = new(1.0f, 0.3f, 0.3f, 0.7f); - - private void OnGUI() - { - DrawToolbar(); - if (_target == null) - { - EditorGUILayout.HelpBox("将 SkillSequenceSO 拖到此处或从 Toolbar 选择", MessageType.Info); - HandleDragDrop(); - return; - } - DrawTimeline(); - } - - private void DrawToolbar() - { - EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); - _target = (SkillSequenceSO)EditorGUILayout.ObjectField( - "技能序列", _target, typeof(SkillSequenceSO), false, - GUILayout.Width(300)); - GUILayout.Label($"缩放: {_zoom:0}px/s"); - _zoom = GUILayout.HorizontalSlider(_zoom, 30f, 300f, GUILayout.Width(100)); - EditorGUILayout.EndHorizontal(); - } - - private void DrawTimeline() - { - float totalSeconds = _target.TotalDuration; - float timelineWidth = totalSeconds * _zoom + LabelWidth + 20f; - - _scroll = EditorGUILayout.BeginScrollView(_scroll); - Rect viewRect = EditorGUILayout.GetControlRect(false, - HeaderHeight + _target.Steps.Length * (TrackHeight + 4f), - GUILayout.Width(timelineWidth)); - - DrawRuler(viewRect, totalSeconds); - - float trackY = viewRect.y + HeaderHeight; - float cursor = 0f; // 当前时间游标(秒) - - for (int i = 0; i < _target.Steps.Length; i++) - { - var step = _target.Steps[i]; - Rect trackRect = new(viewRect.x, trackY + i * (TrackHeight + 4f), - viewRect.width, TrackHeight); - - // 标签 - Rect labelRect = new(trackRect.x, trackRect.y, LabelWidth, TrackHeight); - GUI.Label(labelRect, step.Pattern?.name ?? "—", EditorStyles.miniLabel); - - // 延迟段 - if (step.DelayBeforeStep > 0f) - { - Rect delayRect = TimeToRect(trackRect, cursor, step.DelayBeforeStep); - EditorGUI.DrawRect(delayRect, ColDelay); - cursor += step.DelayBeforeStep; - } - - // 攻击段 - float patternDur = step.Pattern?.Duration ?? 0f; - if (patternDur > 0f) - { - Rect atkRect = TimeToRect(trackRect, cursor, patternDur); - EditorGUI.DrawRect(atkRect, ColAttack); - GUI.Label(atkRect, step.Pattern?.name ?? "", EditorStyles.centeredGreyMiniLabel); - - // 弱点窗口覆盖层 - DrawVulnerabilityWindows(atkRect, cursor, patternDur, step.Pattern); - cursor += patternDur; - } - } - - EditorGUILayout.EndScrollView(); - } - - private void DrawVulnerabilityWindows(Rect atkRect, float patternStart, - float patternDur, AttackPatternSO pattern) - { - if (pattern?.VulnerabilityWindows == null) return; - foreach (var vw in pattern.VulnerabilityWindows) - { - float wStart = vw.StartNormalized * patternDur; - float wDur = (vw.EndNormalized - vw.StartNormalized) * patternDur; - bool warn = vw.DurationNormalized < 0.1f; - Color col = warn ? ColVulnWarn : ColVulnOk; - - Rect wRect = TimeToRect(atkRect, patternStart + wStart, wDur); - EditorGUI.DrawRect(wRect, col); - if (warn) - GUI.Label(wRect, "⚠", new GUIStyle { fontSize = 10, - normal = { textColor = Color.white }, alignment = TextAnchor.MiddleCenter }); - } - } - - private Rect TimeToRect(Rect trackRect, float startSec, float durSec) - { - float x = trackRect.x + LabelWidth + startSec * _zoom; - float w = Mathf.Max(2f, durSec * _zoom); - return new Rect(x, trackRect.y + 2f, w, trackRect.height - 4f); - } - - private void DrawRuler(Rect viewRect, float totalSeconds) - { - float step = _zoom >= 80f ? 0.5f : 1f; - for (float t = 0; t <= totalSeconds; t += step) - { - float x = viewRect.x + LabelWidth + t * _zoom; - EditorGUI.DrawRect(new Rect(x, viewRect.y, 1f, HeaderHeight), Color.gray); - GUI.Label(new Rect(x + 2f, viewRect.y, 40f, 16f), $"{t:0.0}s", - EditorStyles.miniLabel); - } - } - - private void HandleDragDrop() - { - var evt = Event.current; - if (evt.type == EventType.DragUpdated || evt.type == EventType.DragPerform) - { - DragAndDrop.visualMode = DragAndDropVisualMode.Copy; - if (evt.type == EventType.DragPerform) - { - DragAndDrop.AcceptDrag(); - foreach (var obj in DragAndDrop.objectReferences) - if (obj is SkillSequenceSO seq) { _target = seq; break; } - } - evt.Use(); - } - } - } -} -#endif -``` - -> **打开方式**:菜单 `BaseGames → Tools → Boss Skill Sequence Viewer`,将 `SkillSequenceSO` 资产拖入即可。时间轴缩放通过 Toolbar 滑条控制;弱点窗口 `DurationNormalized < 0.1` 时变红警告,对应设计规则(§10 Validator 等价)。 - diff --git a/Docs/Design/47_BossSkillSystem.md b/Docs/Design/47_BossSkillSystem.md deleted file mode 100644 index 79e013bb..00000000 --- a/Docs/Design/47_BossSkillSystem.md +++ /dev/null @@ -1,875 +0,0 @@ -# 47 · Boss 技能系统框架(Boss Skill System Framework) - -> **命名空间** `BaseGames.Enemies.Boss.Skills` -> **所属文档集** [← 返回索引](./README.md) · [总览](./00_Overview.md) -> **依赖文档** -> - [04_CombatSystem](./04_CombatSystem.md) — `DamageInfo` / `HitBox` / `HurtBox` 底层 -> - [19_BossPatternLibrary](./19_BossPatternLibrary.md) — `AttackPatternSO` / `BossOrchestrator` / 阶段系统 -> - [21_SpellSystem](./21_SpellSystem.md) — `FormSkillSO` / 玩家技能(反制参照) -> - [05_ParrySystem](./05_ParrySystem.md) — 弹反流水线 -> - [54_PoiseSystem](./54_PoiseSystem.md) — 霸体 / 打断等级(`PoiseWindowConfig` / `BreakLevel`) - ---- - -## 目录 - -1. [系统定位与层次关系](#1-系统定位与层次关系) -2. [BossSkillSO — 技能数据层](#2-bossskillso--技能数据层) -3. [Boss 技能分类体系](#3-boss-技能分类体系) -4. [弱点与脆弱窗口系统](#4-弱点与脆弱窗口系统) -5. [玩家反制接口](#5-玩家反制接口) -6. [Boss 技能序列系统](#6-boss-技能序列系统) -7. [Boss 资源系统(愤怒 / 状态充能)](#7-boss-资源系统愤怒--状态充能) -8. [场景联动接口](#8-场景联动接口) -9. [平衡数据模板](#9-平衡数据模板) -10. [编辑器工作流](#10-编辑器工作流) -11. [事件频道扩展](#11-事件频道扩展) -12. [资产路径规范](#12-资产路径规范) - ---- - -## 1. 系统定位与层次关系 - -### 1.1 为什么需要 BossSkillSystem? - -`19_BossPatternLibrary` 的 `AttackPatternSO` 描述**单次攻击动作**(前摇/活跃帧/后摇/弹射物), -`BossSkillSystem` 在其上管理**更高阶的技能语义**: - -| 关注点 | AttackPatternSO | BossSkillSO | -|--------|----------------|-------------| -| 粒度 | 单个动作片段 | 一个完整技能(可包含多段动作)| -| 核心数据 | 时序 / 伤害 / 弹射物 | 分类 / 反制接口 / 脆弱窗口 / 序列编排 | -| 执行者 | `BossOrchestrator` | `BossSkillExecutor`(包装 Orchestrator)| -| 平衡视角 | 单次伤害值 | 技能风险/收益比、战斗节奏 | - -### 1.2 层次关系图 - -``` -┌─────────────────────────────────────────────────────────┐ -│ Boss 战斗管线 │ -│ │ -│ BossSkillSO ──────────────────────────────────────┐ │ -│ │ 技能语义 / 分类 / 反制接口 / 脆弱窗口 │ │ -│ ▼ │ │ -│ SkillSequenceSO (可选) │ │ -│ │ 多步骤序列(前置技 → 触发技) │ │ -│ ▼ │ │ -│ AttackPatternSO ←── 19_BossPatternLibrary │ │ -│ │ 单动作:时序 / 弹射物 / 预警 / 权重 │ │ -│ ▼ │ │ -│ BossOrchestrator ←── 19_BossPatternLibrary │ │ -│ │ 运行时选择 → 执行 │ │ -│ ▼ │ │ -│ HitBox / DamageInfo ←── 04_CombatSystem │ │ -│ │ │ -│ ◄──── WeakPointSystem (脆弱窗口) ─────────────────┘ │ -│ ◄──── PlayerInteractionContract (玩家反制接口) │ -│ ◄──── ArenaIntegration (场景联动) │ -└─────────────────────────────────────────────────────────┘ -``` - -### 1.3 设计原则 - -1. **分类先于设计** — 每个 Boss 技能必须在开发前确定其 `BossSkillType` 和 `InteractionTag` -2. **反制可读性** — 每个技能必须明确"玩家用什么技能/动作可以反制/利用" -3. **脆弱窗口强制存在** — 每个技能至少有一个 `VulnerabilityWindow`(后摇 ≥ 0.5s 或专属弱点) -4. **不与 AttackPatternSO 重复** — 伤害数值/时序数据保留在 AttackPatternSO,不在 BossSkillSO 中重复 - ---- - -## 2. BossSkillSO — 技能数据层 - -```csharp -[CreateAssetMenu(menuName = "Enemies/Boss/BossSkill")] -public class BossSkillSO : ScriptableObject -{ - // ───────────────────────────── - // 1. 基础信息(策划填写) - // ───────────────────────────── - - [Header("基础信息")] - public string skillId; // 唯一 ID,格式: {BossId}_{SkillName} - public string displayName; // 设计阶段展示名(不用于玩家 UI) - [TextArea(1, 4)] - public string designNote; // 设计意图(该技能考察玩家什么能力) - - // ───────────────────────────── - // 2. 分类标签 - // ───────────────────────────── - - [Header("分类")] - public BossSkillCategory category; // 主分类(见 §3) - public InteractionTag[] tags; // 可多选(见 §3.2) - public PhaseAvailability phaseAvail; // 在哪些阶段可用 - - // ───────────────────────────── - // 3. 核心攻击动作引用 - // ───────────────────────────── - - [Header("攻击动作")] - [Tooltip("构成此技能的 AttackPatternSO 序列(单个技能 = 数组长度 1;连击/多段 = 多个)")] - public AttackPatternSO[] attackPatterns; // 按执行顺序排列 - - // ───────────────────────────── - // 4. 脆弱窗口(见 §4) - // ───────────────────────────── - - [Header("脆弱窗口")] - public VulnerabilityWindowConfig vulnerabilityWindow; - - // ───────────────────────────── - // 5. 玩家反制接口(见 §5) - // ───────────────────────────── - - [Header("玩家反制接口")] - public PlayerCounterResponse[] counterResponses; // 被不同玩家技能反制时的反应 - - // ───────────────────────────── - // 6. 场景联动(见 §8) - // ───────────────────────────── - - [Header("场景联动")] - public ArenaEventTrigger[] arenaEvents; // 此技能触发的场景事件 - - // ───────────────────────────── - // 7. 资源消耗(Boss 资源系统) - // ───────────────────────────── - - [Header("Boss 资源")] - public BossResourceCost resourceCost; // 使用此技能消耗的 Boss 自身资源(见 §7) - public bool buildsRage; // 使用后是否积累愤怒值 - - // ───────────────────────────── - // 8. 霸体配置(新增) - // ───────────────────────────── - - [Header("霸体配置")] - [Tooltip("此技能执行期间的霸体窗口(None = 可被玩家攻击打断),见 54_PoiseSystem §8")] - public PoiseWindowConfig poiseWindow; // 见 54_PoiseSystem §8 -} -``` - ---- - -## 3. Boss 技能分类体系 - -### 3.1 主分类(BossSkillCategory) - -```csharp -public enum BossSkillCategory -{ - // ── 主动攻击 ── - Melee, // 近战物理攻击(覆盖正前方/扇形/冲刺) - Ranged, // 远程弹射物攻击 - Charge, // 冲刺突进类 - AoE, // 全场/大范围爆炸类 - Environmental, // 激活/改变场景(召唤陷阱/破坏平台/生成危险区域) - Summon, // 召唤小怪/分身/辅助体 - - // ── 特殊状态 ── - Buff, // Boss 自身增益(加速/护甲/无敌) - Debuff, // 对玩家施加负面状态(减速/沉默/中毒/禁锢) - Phase, // 阶段转换技(仅在 HP 阈值触发,不参与随机选择) - - // ── 被动 ── - Passive, // 持续生效的被动机制(不通过 BossOrchestrator 选取) - Reactive, // 响应玩家行为的反应技(玩家使用技能 → Boss 反制) -} -``` - -### 3.2 交互标签(InteractionTag) - -可多选的属性标记,用于确定技能与玩家系统的交互方式: - -```csharp -[Flags] -public enum InteractionTag -{ - None = 0, - Parryable = 1 << 0, // 可弹反(连接 ParrySystem) - PerfectParryOnly = 1 << 1, // 仅完美弹反有效 - DodgeWindow = 1 << 2, // 有明确的冲刺规避窗口 - Unblockable = 1 << 3, // 无法弹反 / 无法格挡 - CanBeReflected = 1 << 4, // 弹射物可被反弹回来 - ExposesWeakPoint = 1 << 5, // 使用后暴露弱点(触发 VulnerabilityWindow) - GrantsPlayerReso = 1 << 6, // 弹反/反制成功后为玩家提供资源(灵力/魄元) - ArenaHazard = 1 << 7, // 改变 Arena 状态(持续危险) - PhaseGate = 1 << 8, // 阶段转换技(不可被打断) -} -``` - -### 3.3 风险/收益分级 - -每个技能在设计阶段应确定其**威胁等级**与**反制收益**: - -| 等级 | 威胁(对玩家的压力)| 反制收益(成功反制的奖励)| 典型形态 | -|------|-----------------|------------------------|---------| -| Low | 低伤 / 可轻松规避 | 小量灵力 | 普通近战、单发弹射物 | -| Mid | 中伤 / 需要主动应对 | 中量灵力 + 脆弱窗口 | 连击、冲刺 | -| High | 高伤 / 必须精准反制 | 大量灵力 + 长脆弱窗口 | 超级技、召唤 | -| Lethal | 极高伤 / 全场 AoE | 击杀后阶段转换 / 大量灵力 | 阶段转换技 | - -```csharp -public enum SkillThreatLevel { Low, Mid, High, Lethal } -public enum CounterRewardLevel { None, Small, Medium, Large, Phase } -// 两者都作为 BossSkillSO 的设计标注字段(不参与运行时逻辑) -``` - ---- - -## 4. 弱点与脆弱窗口系统 - -### 4.1 VulnerabilityWindowConfig - -定义 Boss 在执行技能**后**何时、何处、如何暴露弱点: - -```csharp -[Serializable] -public struct VulnerabilityWindowConfig -{ - [Header("触发条件")] - public VulnTriggerType triggerType; // 何时触发(见下方枚举) - public float triggerDelay; // 从触发条件满足到窗口开启的延迟(秒) - - [Header("持续时间")] - public float duration; // 脆弱窗口持续时长(秒) - - [Header("弱点类型")] - public WeakPointType weakPointType; // 弱点位置/类型 - public float damageMultiplier;// 弱点期间受到伤害的倍率(默认 1.5) - public bool forceStagger; // 是否强制使 Boss 进入硬直动画 - public float staggerDuration; // 强制硬直时长(秒) - - [Header("视觉反馈")] - public FeedbackPresetSO openFeedback; // 窗口开启时的 VFX/SFX - public FeedbackPresetSO closeFeedback; // 窗口关闭时的 VFX/SFX - public Color highlightColor; // 弱点高亮颜色(材质颜色叠加) -} - -public enum VulnTriggerType -{ - OnAttackRecovery, // 攻击后摇阶段自动触发(最常用) - OnParriedSuccess, // 被成功弹反后触发 - OnCounterSkillHit, // 被特定玩家技能命中后触发 - OnPhaseTransition, // 阶段转换动画期间触发 - OnHazardBackfire, // 自己的场景危险伤到自己后触发 - OnSummonDefeated, // 召唤物被击败后触发 - Manual, // 由 Animator / Timeline 手动触发 -} - -public enum WeakPointType -{ - FullBody, // 全身都是弱点 - HeadOnly, // 仅头部 - BackOnly, // 仅背部(需要玩家绕后) - CoreExposed, // 核心部位暴露(通常在特定动画帧) - CustomPoint, // 自定义坐标(用 Transform 引用) -} -``` - -### 4.2 BossWeakPointTracker(运行时组件) - -```csharp -namespace BaseGames.Enemies.Boss.Skills -{ - /// - /// 挂载于 BossBase,追踪并管理当前活跃的脆弱窗口。 - /// 由 BossSkillExecutor 在技能执行过程中驱动。 - /// - public class BossWeakPointTracker : MonoBehaviour - { - [SerializeField] VoidEventChannelSO _onVulnerabilityOpened; - [SerializeField] VoidEventChannelSO _onVulnerabilityClosed; - - bool _isVulnerable; - float _damageMultiplierOverride = 1f; - WeakPointType _activeWeakPoint; - Coroutine _windowCoroutine; - - public bool IsVulnerable => _isVulnerable; - public float DamageMultiplierOverride => _damageMultiplierOverride; - - public void OpenWindow(VulnerabilityWindowConfig cfg) - { - if (_windowCoroutine != null) StopCoroutine(_windowCoroutine); - _windowCoroutine = StartCoroutine(RunWindow(cfg)); - } - - public void ForceCloseWindow() - { - if (_windowCoroutine != null) StopCoroutine(_windowCoroutine); - CloseWindow(); - } - - // ── HurtBox 伤害修正 Hook ── - // 当 BossHurtBox.ReceiveDamage 被调用时,检查 IsVulnerable - // 并将 DamageInfo 的 FinalDamage 乘以 DamageMultiplierOverride - // 此修正在 HurtBox 层完成,BossSkillSystem 无需感知具体伤害计算 - - IEnumerator RunWindow(VulnerabilityWindowConfig cfg) - { - yield return new WaitForSeconds(cfg.triggerDelay); - _isVulnerable = true; - _damageMultiplierOverride = cfg.damageMultiplier; - _activeWeakPoint = cfg.weakPointType; - cfg.openFeedback?.Play(transform.position); - _onVulnerabilityOpened.Raise(); - - yield return new WaitForSeconds(cfg.duration); - CloseWindow(); - cfg.closeFeedback?.Play(transform.position); - } - - void CloseWindow() - { - _isVulnerable = false; - _damageMultiplierOverride = 1f; - _onVulnerabilityClosed.Raise(); - } - } -} -``` - ---- - -## 5. 玩家反制接口 - -### 5.1 设计意图 - -每个 Boss 技能必须声明**玩家可用哪些动作/技能来反制它**,以及**反制成功后发生什么**。 -这保证了:策划在设计技能时同时设计反制路径;代码层提供一致的反制响应框架。 - -### 5.2 PlayerCounterResponse - -```csharp -[Serializable] -public struct PlayerCounterResponse -{ - [Header("反制条件")] - public CounterType counterType; // 玩家用什么触发反制 - public string requiredSkillId; // counterType = SpecificSkill 时填写技能 ID - - [Header("反制效果(对 Boss)")] - public float bossStaggerDuration; // 强制硬直时长(秒,0 = 不强制硬直) - public float bossDamageBonus; // 对 Boss 的额外伤害倍率(0 = 不额外加) - public bool openVulnWindow; // 是否触发 VulnerabilityWindow - public bool interruptSkill; // 是否打断 Boss 当前技能 - - [Header("反制收益(对玩家)")] - public int soulPowerGrant; // 给予玩家的灵力数量 - public int spiritPowerGrant; // 给予玩家的魄元数量 - public FeedbackPresetSO counterFeedback; // 成功反制的特效/音效反馈 -} - -public enum CounterType -{ - Parry, // 弹反成功 - PerfectParry, // 完美弹反 - DodgeThrough, // 冲刺穿越攻击(无敌帧通过) - SpecificSkill, // 使用特定技能(填 requiredSkillId) - WeakPointHit, // 命中暴露的弱点 - HazardBackfire, // 利用场景危险伤到 Boss - SummonKill, // 击败召唤物 -} -``` - -### 5.3 反制接口的运行时处理 - -```csharp -// BossSkillExecutor 在技能生命周期内注册/注销反制监听 -public class BossSkillExecutor : MonoBehaviour -{ - // 在 StartPattern 时调用: - void RegisterCounters(BossSkillSO skill) - { - foreach (var resp in skill.counterResponses) - { - CounterEventBus.Subscribe(resp.counterType, resp.requiredSkillId, - () => ApplyCounterResponse(resp)); - } - } - - // 在 EndPattern 时调用: - void UnregisterCounters(BossSkillSO skill) { /* 同步注销 */ } - - void ApplyCounterResponse(PlayerCounterResponse resp) - { - if (resp.interruptSkill) _orchestrator.AbortCurrentPattern(); - if (resp.bossStaggerDuration > 0) ApplyStagger(resp.bossStaggerDuration); - if (resp.openVulnWindow) _weakPointTracker.OpenWindow(/* 从技能配置取 */); - if (resp.soulPowerGrant > 0) _onGrantPlayerSoulPower.Raise(resp.soulPowerGrant); - if (resp.spiritPowerGrant > 0)_onGrantPlayerSpiritPower.Raise(resp.spiritPowerGrant); - resp.counterFeedback?.Play(transform.position); - } -} -``` - ---- - -## 6. Boss 技能序列系统 - -### 6.1 序列的定义 - -"技能序列"指一组技能按**条件/状态**依次执行,形成更复杂的战术模式: - -- **线性序列**:技能 A → 等待 → 技能 B(连招/二段技) -- **分支序列**:技能 A → 判断玩家位置 → 技能 B 或技能 C -- **条件序列**:技能 A 成功命中 → 追加技能 B(否则取消) - -### 6.2 SkillSequenceSO - -```csharp -[CreateAssetMenu(menuName = "Enemies/Boss/SkillSequence")] -public class SkillSequenceSO : ScriptableObject -{ - public string sequenceId; - public SequenceNode[] nodes; // 序列中的节点列表(按执行顺序) -} - -[Serializable] -public class SequenceNode -{ - [Header("节点信息")] - public string nodeId; - public SequenceNodeType nodeType; - - // ── SkillNode 类型 ── - public BossSkillSO skill; // 要执行的技能 - - // ── WaitNode 类型 ── - public float waitDuration; // 等待时长(秒) - - // ── BranchNode 类型 ── - public SequenceBranchCondition branchCondition; - public string trueNodeId; // 条件为真时跳转到的节点 ID - public string falseNodeId; // 条件为假时跳转到的节点 ID - - // ── AbortCondition(所有节点通用)── - [Tooltip("满足此条件时提前终止序列")] - public SequenceAbortCondition abortCondition; -} - -public enum SequenceNodeType -{ - SkillNode, // 执行一个 BossSkillSO - WaitNode, // 等待固定时间 - BranchNode, // 条件分支 - ArenaEventNode,// 触发场景事件(不执行攻击) -} - -[Serializable] -public struct SequenceBranchCondition -{ - public BranchConditionType type; - [Range(0f,1f)] public float hpThreshold; // HPBelow 时使用 - public float range; // InRange 时使用 -} - -public enum BranchConditionType -{ - PlayerInRange, - PlayerAbove, - HPBelow, - LastSkillParried, // 上一个技能被弹反 - LastSkillHit, // 上一个技能命中了玩家 -} -``` - -### 6.3 BossSequenceRunner - -```csharp -/// -/// 运行 SkillSequenceSO 的协程控制器,挂载于 BossBase。 -/// 由 BehaviorDesigner 的 Action Task RunBossSequence 调用。 -/// -public class BossSequenceRunner : MonoBehaviour -{ - public bool IsRunning { get; private set; } - - public void RunSequence(SkillSequenceSO sequence, Transform player, - System.Action onComplete = null) - { - if (IsRunning) return; - StartCoroutine(ExecuteSequence(sequence, player, onComplete)); - } - - public void AbortSequence() { StopAllCoroutines(); IsRunning = false; } - - IEnumerator ExecuteSequence(SkillSequenceSO seq, Transform player, - System.Action onComplete) - { - IsRunning = true; - var nodeMap = seq.nodes.ToDictionary(n => n.nodeId); - var node = seq.nodes[0]; // 从第一个节点开始 - - while (node != null) - { - // 检查提前终止 - if (CheckAbort(node.abortCondition)) break; - - switch (node.nodeType) - { - case SequenceNodeType.SkillNode: - yield return ExecuteSkillNode(node.skill, player); - node = GetNextNode(nodeMap, node, isSequential: true); - break; - - case SequenceNodeType.WaitNode: - yield return new WaitForSeconds(node.waitDuration); - node = GetNextNode(nodeMap, node, isSequential: true); - break; - - case SequenceNodeType.BranchNode: - bool cond = EvaluateBranch(node.branchCondition, player); - string nextId = cond ? node.trueNodeId : node.falseNodeId; - node = string.IsNullOrEmpty(nextId) ? null : nodeMap.GetValueOrDefault(nextId); - break; - - case SequenceNodeType.ArenaEventNode: - TriggerArenaEvent(node); - node = GetNextNode(nodeMap, node, isSequential: true); - break; - } - } - - IsRunning = false; - onComplete?.Invoke(); - } - // ... 内部辅助方法省略(由具体 Boss 实现扩展) -} -``` - ---- - -## 7. Boss 资源系统(愤怒 / 状态充能) - -### 7.1 设计意图 - -部分 Boss 需要"积累状态"才能释放最强技能,或受到一定伤害后进入特殊形态。 -`BossResourceSystem` 提供一个轻量级、可配置的通用资源层,不强制每个 Boss 都使用。 - -### 7.2 BossResourceConfigSO - -```csharp -[CreateAssetMenu(menuName = "Enemies/Boss/ResourceConfig")] -public class BossResourceConfigSO : ScriptableObject -{ - public string resourceId; // 如 "Rage" / "PhaseCharge" - public string displayName; - public float maxValue; - public float startValue; // 初始值(通常 0) - - [Header("自动变化")] - public float passiveRate; // 每秒自动变化量(正=增长/负=衰减,0=不自动) - public float onTakeDamageGain; // 每受到 1 点伤害积累量 - public float onSkillUseGain; // 每使用一次技能积累量 - - [Header("满值效果")] - public bool autoTriggerOnFull; // 是否满值时自动触发效果 - public BossSkillSO fullTriggerSkill; // 触发的技能 - public float resetValueAfterTrigger; // 触发后重置到的值 -} - -public struct BossResourceCost -{ - public string resourceId; // 对应 BossResourceConfigSO.resourceId - public float cost; // 消耗量(0 = 不消耗资源) - public float minRequired;// 使用此技能的最低资源要求 -} -``` - -### 7.3 BossResourceTracker(运行时) - -```csharp -public class BossResourceTracker : MonoBehaviour -{ - BossResourceConfigSO _config; - float _current; - - public float Current => _current; - public float Ratio => _current / _config.maxValue; - public bool IsFull => _current >= _config.maxValue; - - public void Gain(float amount) - { - _current = Mathf.Clamp(_current + amount, 0, _config.maxValue); - if (IsFull && _config.autoTriggerOnFull) TriggerFullEffect(); - } - - public bool TryConsume(BossResourceCost cost) - { - if (cost.resourceId != _config.resourceId) return true; // 不相关资源直接通过 - if (_current < cost.minRequired) return false; - _current -= cost.cost; - return true; - } -} -``` - ---- - -## 8. 场景联动接口 - -### 8.1 设计意图 - -Boss 技能可以**触发场景变化**(破坏平台、激活陷阱、改变重力等),这些变化通过接口解耦,不在 BossSkillSO 中硬编码场景引用。 - -### 8.2 IArenaInteractable - -```csharp -/// -/// 场景中可被 Boss 技能交互的对象实现此接口。 -/// 无需引用 Boss;Boss 通过 ArenaEventBus 广播,场景监听并响应。 -/// -public interface IArenaInteractable -{ - string ArenaObjectId { get; } // 唯一标识(在 BossSkillSO.arenaEvents 中引用) - void OnBossArenaEvent(ArenaEventData data); -} -``` - -### 8.3 ArenaEventTrigger & ArenaEventData - -```csharp -[Serializable] -public struct ArenaEventTrigger -{ - public string targetArenaObjectId; // 要影响的场景物件 ID(空 = 广播给所有) - public ArenaEventType eventType; - public float delay; // 从技能触发到场景事件的延迟(秒) - public ArenaEventParams parameters; -} - -public enum ArenaEventType -{ - DestroyPlatform, // 破坏指定平台 - ActivateHazard, // 激活陷阱(如喷火管) - DeactivateHazard, // 关闭陷阱 - SpawnHazardArea, // 生成持续危险区域(熔岩/毒液水坑) - ShakeArena, // 场景震动 - ToggleLighting, // 切换场景光照(Boss 技能令场景变暗等) - SpawnPlatform, // 生成新平台(阶段 2 变化) - TriggerCutscene, // 触发小型过场(无需外部管理) -} - -[Serializable] -public struct ArenaEventParams -{ - public float duration; // 效果持续时间(0 = 永久或由物件自行管理) - public float intensity; // 强度(如震动幅度、危险区域半径) - public bool revertsOnPhaseEnd; // 阶段结束时是否恢复 -} - -[Serializable] -public struct ArenaEventData -{ - public ArenaEventType type; - public ArenaEventParams parameters; - public string sourceSkillId; // 来源技能 ID(供物件做条件判断) -} -``` - -### 8.4 ArenaEventBus(单例) - -```csharp -public static class ArenaEventBus -{ - static readonly Dictionary> _registry = new(); - - public static void Register(IArenaInteractable obj) - { - if (!_registry.TryGetValue(obj.ArenaObjectId, out var list)) - _registry[obj.ArenaObjectId] = list = new List(); - list.Add(obj); - } - - public static void Unregister(IArenaInteractable obj) { /* ... */ } - - public static void Dispatch(ArenaEventTrigger trigger) - { - var data = new ArenaEventData - { - type = trigger.eventType, - parameters = trigger.parameters, - sourceSkillId = "" // 由调用方填入 - }; - - if (string.IsNullOrEmpty(trigger.targetArenaObjectId)) - { - // 广播给所有注册物件 - foreach (var list in _registry.Values) - foreach (var obj in list) obj.OnBossArenaEvent(data); - } - else if (_registry.TryGetValue(trigger.targetArenaObjectId, out var targets)) - { - foreach (var obj in targets) obj.OnBossArenaEvent(data); - } - } -} -``` - ---- - -## 9. 平衡数据模板 - -### 9.1 BossSkillBalanceSheet(SO 内联数据,用于策划填写) - -```csharp -[Serializable] -public struct BossSkillBalanceNote -{ - // ── 设计阶段填写(不参与运行时逻辑)── - [Header("威胁评估")] - public SkillThreatLevel threatLevel; - public CounterRewardLevel counterReward; - - [Header("节奏设计")] - [Tooltip("此技能在战斗中的频率意图(低/中/高)")] - public SkillFrequencyHint frequencyHint; - [Tooltip("此技能是否作为阶段标志性技能")] - public bool isSignatureSkill; - - [Header("学习曲线")] - [Tooltip("玩家第几次遭遇此 Boss 时通常能学会应对此技能")] - public int expectedLearnEncounter; - [Tooltip("正确应对此技能所需的前置技能/知识")] - [TextArea(1,3)] - public string prerequisiteKnowledge; -} - -public enum SkillFrequencyHint { Rare, Occasional, Frequent } -``` - -### 9.2 平衡时序模板 - -策划在填写 `AttackPatternSO` 的时序数据时,参考以下基准: - -| 技能类型 | 预警时长参考 | 前摇参考 | 后摇参考(脆弱窗口)| -|---------|-----------|---------|-----------------| -| 轻型近战 | 0.3-0.5s | 0.15-0.2s | 0.3-0.5s | -| 重型近战 | 0.7-1.0s | 0.3-0.5s | 0.6-1.0s | -| 冲刺 | 0.5-0.8s | 0.2s | 0.4-0.6s(撞墙后)| -| 单发弹射 | 0.5-0.7s | 0.2s | 0.3-0.5s | -| 多发弹幕 | 0.8-1.2s | 0.3s | 0.5-0.8s | -| 召唤技 | 1.0-1.5s | 0.5s | 1.0-2.0s(召唤期间脆弱)| -| 阶段技 | 1.5-2.5s(无法打断)| 0.5-1.0s | 全段脆弱 | - -### 9.3 难度缩放协议 - -每个 `BossSkillSO` 对应的 `AttackPatternSO` 不直接存储难度缩放值; -缩放通过 `DifficultyScalerSO`(见 [29_DifficultySystem](./29_DifficultySystem.md))统一处理: - -``` -DifficultyScalerSO - ├─ damageMultiplier → 作用于 DamageSourceSO.BaseDamage - ├─ telegraphMultiplier → 作用于 AttackPatternSO.telegraphDuration(Easy 加长 / Hard 缩短) - ├─ recoveryMultiplier → 作用于 AttackPatternSO.recoveryDuration(Easy 加长脆弱 / Hard 缩短) - └─ phaseTransitionDelay → 阶段转换动画时长(Easy 加长逃跑时间) -``` - ---- - -## 10. 编辑器工作流 - -### 10.1 创建一个 Boss 技能的完整步骤 - -``` -Step 1 — 在 Inspector 中填写 BossSkillSO - ├─ 填写 skillId: {BossId}_{SkillName}(如 "SpiderGuard_WebShot") - ├─ 选择 category 和 tags - ├─ 选择 threatLevel 和 counterReward(平衡笔记) - └─ 填写 designNote(一两句话说明该技能的设计意图) - -Step 2 — 创建对应 AttackPatternSO(19_BossPatternLibrary §2) - ├─ 填写时序数据(参考 §9.2 时序模板) - ├─ 配置 TelegraphConfig(预警类型 + 资产) - ├─ 配置 DamageSourceSO(复用或新建) - └─ 将 AttackPatternSO 拖入 BossSkillSO.attackPatterns[] - -Step 3 — 配置脆弱窗口(VulnerabilityWindowConfig) - ├─ 选择 triggerType(通常 OnAttackRecovery) - ├─ 设置 duration 和 damageMultiplier - └─ 拖入 openFeedback / closeFeedback 预设 - -Step 4 — 配置玩家反制接口(PlayerCounterResponse[]) - ├─ 为每种有效反制方式添加一个 Response 条目 - ├─ 设置对 Boss 的影响(stagger / vulnWindow) - └─ 设置对玩家的资源奖励 - -Step 5 — 将 BossSkillSO 添加到 BossPhaseConfigSO.patterns[] - └─ 注意:BossPhaseConfigSO 中存放 AttackPatternSO; - BossSkillSO 是对 AttackPatternSO 的上层包装, - BossSkillExecutor 通过 BossSkillSO 间接驱动 BossOrchestrator - -Step 6 — 在 Editor PlayMode 中使用 BossSkillTestWindow 测试 - ├─ 选择 Boss Prefab - ├─ 点击技能名称强制触发该技能 - └─ 查看脆弱窗口和反制效果是否正常 -``` - -### 10.2 BossSkillTestWindow(编辑器工具) - -```csharp -// Editor/BossSkillTestWindow.cs -// 依赖:仅在 UnityEditor 命名空间下可用(UNITY_EDITOR 宏保护) -public class BossSkillTestWindow : EditorWindow -{ - GameObject _bossPrefab; - BossSkillSO _selectedSkill; - - // 功能: - // 1. 在 PlayMode 中选择场景内 Boss,列出其所有 BossSkillSO - // 2. 点击"强制触发"→ 调用 BossSkillExecutor.ForceExecuteSkill(skill) - // 3. 实时显示当前脆弱窗口状态(时长倒计时 + 伤害倍率) - // 4. 显示最近 10 次技能触发记录(时间戳 + 玩家是否成功反制) -} -``` - ---- - -## 11. 事件频道扩展 - -以下事件频道为 `BossSkillSystem` 专有,在 `00_Overview §7` 频道清单基础上补充: - -| 频道 SO 名称 | 类型 | 数据 | 说明 | -|------------|------|------|------| -| `BossSkill_OnSkillStarted` | `StringEventChannelSO` | skillId | 技能开始执行时广播 | -| `BossSkill_OnSkillEnded` | `StringEventChannelSO` | skillId | 技能结束时广播 | -| `BossSkill_OnVulnWindowOpened` | `VoidEventChannelSO` | — | 脆弱窗口开启 | -| `BossSkill_OnVulnWindowClosed` | `VoidEventChannelSO` | — | 脆弱窗口关闭 | -| `BossSkill_OnCounterSuccess` | `StringEventChannelSO` | counterType | 玩家反制成功 | -| `BossSkill_OnGrantSoulPower` | `IntEventChannelSO` | 灵力数量 | 反制奖励灵力 | -| `BossSkill_OnGrantSpiritPower` | `IntEventChannelSO` | 魄元数量 | 反制奖励魄元 | -| `BossSkill_OnArenaEventTriggered` | `StringEventChannelSO` | eventType | 场景联动事件触发 | -| `BossSkill_OnSequenceStarted` | `StringEventChannelSO` | sequenceId | 技能序列开始 | -| `BossSkill_OnSequenceEnded` | `StringEventChannelSO` | sequenceId | 技能序列结束 | - ---- - -## 12. 资产路径规范 - -``` -Assets/ScriptableObjects/Enemies/Boss/ - ├── {BossId}/ - │ ├── Skills/ - │ │ ├── BSK_{BossId}_{SkillName}.asset ← BossSkillSO - │ │ └── SEQ_{BossId}_{SequenceName}.asset ← SkillSequenceSO(可选) - │ ├── Phases/ - │ │ ├── BPC_{BossId}_Phase1.asset ← BossPhaseConfigSO(来自 19) - │ │ └── ... - │ ├── Patterns/ - │ │ └── AP_{BossId}_{PatternName}.asset ← AttackPatternSO(来自 19) - │ └── Resources/ - │ └── BRC_{BossId}_Rage.asset ← BossResourceConfigSO(可选) - └── Shared/ - └── DamageSource/ - └── DS_Boss_{Type}.asset ← 多 Boss 复用的 DamageSourceSO -``` - -命名约定: -- `BSK_` — BossSkillSO -- `SEQ_` — SkillSequenceSO -- `BPC_` — BossPhaseConfigSO -- `AP_` — AttackPatternSO -- `BRC_` — BossResourceConfigSO -- `DS_` — DamageSourceSO