33 KiB
33 KiB
47 · Boss 技能系统框架(Boss Skill System Framework)
命名空间
BaseGames.Enemies.Boss.Skills
所属文档集 ← 返回索引 · 总览
依赖文档
- 04_CombatSystem —
DamageInfo/HitBox/HurtBox底层- 19_BossPatternLibrary —
AttackPatternSO/BossOrchestrator/ 阶段系统- 21_SpellSystem —
FormSkillSO/ 玩家技能(反制参照)- 05_ParrySystem — 弹反流水线
- 54_PoiseSystem — 霸体 / 打断等级(
PoiseWindowConfig/BreakLevel)
目录
- 系统定位与层次关系
- BossSkillSO — 技能数据层
- Boss 技能分类体系
- 弱点与脆弱窗口系统
- 玩家反制接口
- Boss 技能序列系统
- Boss 资源系统(愤怒 / 状态充能)
- 场景联动接口
- 平衡数据模板
- 编辑器工作流
- 事件频道扩展
- 资产路径规范
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 设计原则
- 分类先于设计 — 每个 Boss 技能必须在开发前确定其
BossSkillType和InteractionTag - 反制可读性 — 每个技能必须明确"玩家用什么技能/动作可以反制/利用"
- 脆弱窗口强制存在 — 每个技能至少有一个
VulnerabilityWindow(后摇 ≥ 0.5s 或专属弱点) - 不与 AttackPatternSO 重复 — 伤害数值/时序数据保留在 AttackPatternSO,不在 BossSkillSO 中重复
2. BossSkillSO — 技能数据层
[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)
public enum BossSkillCategory
{
// ── 主动攻击 ──
Melee, // 近战物理攻击(覆盖正前方/扇形/冲刺)
Ranged, // 远程弹射物攻击
Charge, // 冲刺突进类
AoE, // 全场/大范围爆炸类
Environmental, // 激活/改变场景(召唤陷阱/破坏平台/生成危险区域)
Summon, // 召唤小怪/分身/辅助体
// ── 特殊状态 ──
Buff, // Boss 自身增益(加速/护甲/无敌)
Debuff, // 对玩家施加负面状态(减速/沉默/中毒/禁锢)
Phase, // 阶段转换技(仅在 HP 阈值触发,不参与随机选择)
// ── 被动 ──
Passive, // 持续生效的被动机制(不通过 BossOrchestrator 选取)
Reactive, // 响应玩家行为的反应技(玩家使用技能 → Boss 反制)
}
3.2 交互标签(InteractionTag)
可多选的属性标记,用于确定技能与玩家系统的交互方式:
[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 | 击杀后阶段转换 / 大量灵力 | 阶段转换技 |
public enum SkillThreatLevel { Low, Mid, High, Lethal }
public enum CounterRewardLevel { None, Small, Medium, Large, Phase }
// 两者都作为 BossSkillSO 的设计标注字段(不参与运行时逻辑)
4. 弱点与脆弱窗口系统
4.1 VulnerabilityWindowConfig
定义 Boss 在执行技能后何时、何处、如何暴露弱点:
[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(运行时组件)
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
[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 反制接口的运行时处理
// 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
[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
/// <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
[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(运行时)
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
/// <summary>
/// 场景中可被 Boss 技能交互的对象实现此接口。
/// 无需引用 Boss;Boss 通过 ArenaEventBus 广播,场景监听并响应。
/// </summary>
public interface IArenaInteractable
{
string ArenaObjectId { get; } // 唯一标识(在 BossSkillSO.arenaEvents 中引用)
void OnBossArenaEvent(ArenaEventData data);
}
8.3 ArenaEventTrigger & ArenaEventData
[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(单例)
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 内联数据,用于策划填写)
[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)统一处理:
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(编辑器工具)
// 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_— BossSkillSOSEQ_— SkillSequenceSOBPC_— BossPhaseConfigSOAP_— AttackPatternSOBRC_— BossResourceConfigSODS_— DamageSourceSO