refactor(enemy): 清全库行为树残留,删无人使用的 Boss 技能事件频道
Task 21 Step 2 全库扫描后的收尾: 注释与 Tooltip 里的旧决策层措辞逐处改为只描述功能—— EnemyControlledState / RangedEnemy / FlyingEnemy / SensorSlotNames / EnemyPoiseComponent / EnemyAnimationConfigSO / EnemyAiBrain。 EnemyQuotaManager 的序列化字段 _maxActiveBehaviorTrees 改名 _maxActiveBrains (它裁剪的实际是 EnemyAiBrain),带 FormerlySerializedAs。复核确认目前 无任何场景 / 预制体挂载该组件,故无值可丢;属未雨绸缪。 BossSkillEventChannelSO + BossEvents.BossSkillEvent + EVT_BossSkill.asset 删除: 唯一发送方 BossSkillExecutor 已随旧轨删除,复核确认全项目零发送方、零订阅方, 资产 guid 在场景 / 预制体 / Addressables 分组中均无引用。 自检:编译 0 error;EditMode 260/260 通过; SO 校验 0 error 0 warning;层矩阵 25/25 正确。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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:
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8e2f5df884c7f624da572ab81162da4d
|
|
||||||
NativeFormatImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
mainObjectFileID: 11400000
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -1,15 +1,5 @@
|
|||||||
namespace BaseGames.Core.Events
|
namespace BaseGames.Core.Events
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Boss 技能事件负载。
|
|
||||||
/// </summary>
|
|
||||||
[System.Serializable]
|
|
||||||
public struct BossSkillEvent
|
|
||||||
{
|
|
||||||
public string BossId;
|
|
||||||
public string SkillId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Boss 阶段切换事件负载。
|
/// Boss 阶段切换事件负载。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace BaseGames.Core.Events
|
|
||||||
{
|
|
||||||
[CreateAssetMenu(menuName = "BaseGames/Events/BossSkill")]
|
|
||||||
public class BossSkillEventChannelSO : BaseEventChannelSO<BossSkillEvent> { }
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8230eab2acba8c24499b2d20df81adb7
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
@@ -60,7 +60,6 @@ namespace BaseGames.Editor
|
|||||||
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_ShieldRestored");
|
CreateAsset<VoidEventChannelSO> ("Combat", "EVT_ShieldRestored");
|
||||||
|
|
||||||
// ── Boss ──────────────────────────────────────────────────────────
|
// ── Boss ──────────────────────────────────────────────────────────
|
||||||
CreateAsset<BossSkillEventChannelSO> ("Boss", "EVT_BossSkill");
|
|
||||||
CreateAsset<BossPhaseEventChannelSO> ("Boss", "EVT_BossPhase");
|
CreateAsset<BossPhaseEventChannelSO> ("Boss", "EVT_BossPhase");
|
||||||
CreateAsset<BossPhaseEventChannelSO> ("Boss", "EVT_BossPhaseChanged");
|
CreateAsset<BossPhaseEventChannelSO> ("Boss", "EVT_BossPhaseChanged");
|
||||||
CreateAsset<StringEventChannelSO> ("Boss", "EVT_BossDefeated");
|
CreateAsset<StringEventChannelSO> ("Boss", "EVT_BossDefeated");
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace BaseGames.Enemies
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 把 BrainGraph 决策层挂到敌人上:按 id 取共享 AiGraph、构造 AiRuntime、逐帧推进。
|
/// 把 BrainGraph 决策层挂到敌人上:按 id 取共享 AiGraph、构造 AiRuntime、逐帧推进。
|
||||||
/// 取代旧的行为树组件。
|
/// 是敌人唯一的决策层组件(小怪与 Boss 共用)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DisallowMultipleComponent]
|
[DisallowMultipleComponent]
|
||||||
[RequireComponent(typeof(EnemyBase))]
|
[RequireComponent(typeof(EnemyBase))]
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace BaseGames.Enemies
|
|||||||
public AnimationClip Investigate;
|
public AnimationClip Investigate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 按字段名返回 Clip(BD_PlayAnimation 使用)。
|
/// 按字段名返回 Clip(AI 状态片段与能力模块按名取动画时使用)。
|
||||||
/// 支持字段名和常见别名(如 Attack_Melee / Idle 等)。
|
/// 支持字段名和常见别名(如 Attack_Melee / Idle 等)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AnimationClip GetClipByName(string name)
|
public AnimationClip GetClipByName(string name)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace BaseGames.Enemies
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 敌人霸体组件(架构 06_CombatModule §13)。
|
/// 敌人霸体组件(架构 06_CombatModule §13)。
|
||||||
/// Awake 时自动注入到同节点 HurtBox(无需手动 Inspector 赋值)。
|
/// Awake 时自动注入到同节点 HurtBox(无需手动 Inspector 赋值)。
|
||||||
/// EnemyBase 或 BehaviorDesigner 任务可调用 SetPoiseLevel / ResetPoiseLevel 切换超甲状态。
|
/// EnemyBase、能力模块或动画事件可调用 SetPoiseLevel / ResetPoiseLevel 切换超甲状态。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequireComponent(typeof(EnemyBase))]
|
[RequireComponent(typeof(EnemyBase))]
|
||||||
public class EnemyPoiseComponent : MonoBehaviour, IPoiseSource
|
public class EnemyPoiseComponent : MonoBehaviour, IPoiseSource
|
||||||
@@ -31,7 +31,7 @@ namespace BaseGames.Enemies
|
|||||||
|
|
||||||
// ── 外部 API ──────────────────────────────────────────────────────────
|
// ── 外部 API ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
/// <summary>进入/退出超甲时由 EnemyBase 或 BehaviorDesigner 任务调用。</summary>
|
/// <summary>进入/退出超甲时由 EnemyBase、能力模块或动画事件调用。</summary>
|
||||||
public void SetPoiseLevel(PoiseLevel level) => _currentPoiseLevel = level;
|
public void SetPoiseLevel(PoiseLevel level) => _currentPoiseLevel = level;
|
||||||
|
|
||||||
/// <summary>恢复到默认霸体等级(超甲结束时调用)。</summary>
|
/// <summary>恢复到默认霸体等级(超甲结束时调用)。</summary>
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ using BaseGames.Core.Events;
|
|||||||
namespace BaseGames.Enemies
|
namespace BaseGames.Enemies
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 敌人 BehaviorTree 配额管理器(架构 07_EnemyModule §13)。
|
/// 敌人决策层配额管理器(架构 07_EnemyModule §13)。
|
||||||
/// 每 REBALANCE_INTERVAL 帧,按到玩家距离排序已注册敌人,
|
/// 每 REBALANCE_INTERVAL 帧,按到玩家距离排序已注册敌人,
|
||||||
/// 仅对最近的 _maxActiveBehaviorTrees 个敌人启用 BT。
|
/// 仅对最近的 _maxActiveBrains 个敌人启用 <see cref="EnemyAiBrain"/>,其余暂停决策与感知。
|
||||||
/// 挂载在场景管理器 GameObject 上(每场景一个实例)。
|
/// 挂载在场景管理器 GameObject 上(每场景一个实例)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EnemyQuotaManager : MonoBehaviour
|
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")]
|
[Tooltip("PlayerController.Start() 广播此频道,替代 FindWithTag")]
|
||||||
[SerializeField] private TransformEventChannelSO _onPlayerSpawned;
|
[SerializeField] private TransformEventChannelSO _onPlayerSpawned;
|
||||||
|
|
||||||
@@ -107,11 +109,11 @@ namespace BaseGames.Enemies
|
|||||||
if (enemy == null) { _registered.RemoveAt(i); continue; }
|
if (enemy == null) { _registered.RemoveAt(i); continue; }
|
||||||
|
|
||||||
var brain = enemy.Brain;
|
var brain = enemy.Brain;
|
||||||
bool active = i < _maxActiveBehaviorTrees;
|
bool active = i < _maxActiveBrains;
|
||||||
|
|
||||||
if (brain != null && brain.enabled != active)
|
if (brain != null && brain.enabled != active)
|
||||||
{
|
{
|
||||||
// 禁用 brain 即停其 Update/Tick,等价于旧的禁用行为树。
|
// 禁用 brain 即停其 Update/Tick,敌人保持静止但仍存在于场景中。
|
||||||
brain.enabled = active;
|
brain.enabled = active;
|
||||||
// 同步暂停/恢复感知系统,避免远处敌人无效 tick
|
// 同步暂停/恢复感知系统,避免远处敌人无效 tick
|
||||||
enemy.SensorHub?.SetSuspended(!active);
|
enemy.SensorHub?.SetSuspended(!active);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace BaseGames.Enemies
|
|||||||
/// 飞行敌人基类。
|
/// 飞行敌人基类。
|
||||||
///
|
///
|
||||||
/// 导航由 <see cref="Navigation.FlyingNavigator"/> 实现(IEnemyNavigator),
|
/// 导航由 <see cref="Navigation.FlyingNavigator"/> 实现(IEnemyNavigator),
|
||||||
/// AI 行为逻辑由挂载的 Behavior Designer 树驱动。
|
/// AI 决策由挂载的 <see cref="EnemyAiBrain"/>(BrainGraph)驱动。
|
||||||
/// 本类仅负责:
|
/// 本类仅负责:
|
||||||
/// <list type="bullet">
|
/// <list type="bullet">
|
||||||
/// <item>Rigidbody2D 无重力初始化</item>
|
/// <item>Rigidbody2D 无重力初始化</item>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ namespace BaseGames.Enemies.Perception
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 视线检测(BatchLOS):敌我之间无遮挡时持续为 true。
|
/// 视线检测(BatchLOS):敌我之间无遮挡时持续为 true。
|
||||||
/// 由 PhysicsPerceptionSystem 自研批量计算(OverlapCircle + 单次 Raycast),BD_IsPlayerVisible 读取结果。
|
/// 由 PhysicsPerceptionSystem 批量计算(OverlapCircle + 单次 Raycast),AI 图的可见性条件读取结果。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string LOS = "los";
|
public const string LOS = "los";
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace BaseGames.Enemies
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 远程敌人。重写 <see cref="EnemyBase.SpawnProjectile"/> 以从对象池生成抛射物。
|
/// 远程敌人。重写 <see cref="EnemyBase.SpawnProjectile"/> 以从对象池生成抛射物。
|
||||||
/// Behavior Designer 任务通过调用 SpawnProjectile(payload) 触发发射。
|
/// 能力模块或动画事件通过调用 SpawnProjectile(payload) 触发发射。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RangedEnemy : EnemyBase
|
public class RangedEnemy : EnemyBase
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace BaseGames.Enemies.States
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 受控状态(默认行为)。
|
/// 受控状态(默认行为)。
|
||||||
/// 敌人处于 AI 正常控制下(Behavior Designer 运行中),无特殊 Enter/Exit 动画逻辑。
|
/// 敌人处于 AI 正常控制下(决策层运行中),无特殊 Enter/Exit 动画逻辑。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class EnemyControlledState : IEnemyState
|
public sealed class EnemyControlledState : IEnemyState
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
|
||||||
{
|
|
||||||
/// <summary>高层技能分类(平衡框架用)。</summary>
|
|
||||||
public enum BossSkillCategory
|
|
||||||
{
|
|
||||||
Melee, Ranged, Charge, AoE, Environmental, Summon,
|
|
||||||
Buff, Debuff, Phase, Passive, Reactive
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>具体技能类型(战斗设计用)。</summary>
|
|
||||||
public enum BossSkillType
|
|
||||||
{
|
|
||||||
// 基础攻击
|
|
||||||
MeleeSlash, // 近战斩击
|
|
||||||
ChargeAttack, // 冲刺撞击
|
|
||||||
LeapSlam, // 跳起落地震
|
|
||||||
ProjectileVolley, // 弹幕齐射
|
|
||||||
LaserBeam, // 激光扫射
|
|
||||||
|
|
||||||
// 阶段技能
|
|
||||||
PhaseTransition, // 阶段切换(无攻击,触发动画/护盾等)
|
|
||||||
SummonMinion, // 召唤小兵
|
|
||||||
|
|
||||||
// 特殊
|
|
||||||
ArenaTrap, // 改变战斗区域地形
|
|
||||||
SpeedBurst, // 速度爆发(数帧内免疫/高速)
|
|
||||||
DefensiveShell, // 防御壳(需攻击弱点)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>互动标签:定义玩家可以对该技能执行哪些反制操作。</summary>
|
|
||||||
[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
|
|
||||||
{
|
|
||||||
/// <summary>弱点出现时机类型。</summary>
|
|
||||||
public enum VulnTriggerType
|
|
||||||
{
|
|
||||||
OnAttackRecovery, // 攻击后摇
|
|
||||||
OnParriedSuccess, // 弹反成功
|
|
||||||
OnCounterSkillHit, // 计算技能命中
|
|
||||||
OnPhaseTransition, // 阶段切换时
|
|
||||||
OnHazardBackfire, // 场地尃8个
|
|
||||||
OnSummonDefeated, // 召唤物被击败
|
|
||||||
Manual, // BossSkillExecutor 手动触发
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>弱点位置类型。</summary>
|
|
||||||
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(供场景物件做条件判断)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 场景中可被 Boss 技能交互的对象实现此接口。
|
|
||||||
/// Boss 通过 ArenaEventBus 广播,场景物件监听并响应。
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 单个攻击图案的数据。
|
|
||||||
/// 存放伤害/速度等实际参数,BossSkillSO 不存参数。
|
|
||||||
/// </summary>
|
|
||||||
[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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 有序攻击序列(一个技能内的多段连段)。
|
|
||||||
/// </summary>
|
|
||||||
[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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 挂在 Boss GameObject 上。
|
|
||||||
/// 接收 BossOrchestrator 的指令,执行指定 BossSkillSO。
|
|
||||||
/// 管理 VulnerabilityWindow 计时和 WeakPointSystem 激活。
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 管理 Boss 的专属弱点 HurtBox(如核心、眼睛等)。
|
|
||||||
/// 弱点激活期间受到的伤害会乘以 DamageMultiplier。
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 弱点 HurtBox 受击时,由 BossStats 调用此方法获取最终伤害系数。
|
|
||||||
/// </summary>
|
|
||||||
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<BossSkillSequenceWindow>("技能序列查看器");
|
|
||||||
|
|
||||||
// ── 状态 ──────────────────────────────────────────────────────────────
|
|
||||||
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 等价)。
|
|
||||||
|
|
||||||
@@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 挂载于 BossBase,追踪并管理当前活跃的脆弱窗口。
|
|
||||||
/// 由 BossSkillExecutor 在技能执行过程中驱动。
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
/// <summary>
|
|
||||||
/// 运行 SkillSequenceSO 的协程控制器,挂载于 BossBase。
|
|
||||||
/// 由 BehaviorDesigner 的 Action Task RunBossSequence 调用。
|
|
||||||
/// </summary>
|
|
||||||
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
|
|
||||||
/// <summary>
|
|
||||||
/// 场景中可被 Boss 技能交互的对象实现此接口。
|
|
||||||
/// 无需引用 Boss;Boss 通过 ArenaEventBus 广播,场景监听并响应。
|
|
||||||
/// </summary>
|
|
||||||
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<string, List<IArenaInteractable>> _registry = new();
|
|
||||||
|
|
||||||
public static void Register(IArenaInteractable obj)
|
|
||||||
{
|
|
||||||
if (!_registry.TryGetValue(obj.ArenaObjectId, out var list))
|
|
||||||
_registry[obj.ArenaObjectId] = list = new List<IArenaInteractable>();
|
|
||||||
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
|
|
||||||
Reference in New Issue
Block a user