Files
zeling_v2/Docs/Architecture/25_CharacterArchitectureOverview.md
T
joywayerandClaude Opus 5 6d3816d7ef docs(enemy): Boss AI 单轨落地——新增作者指南,清理描述已删除系统的长期文档
新增 Docs/Guides/09_BossAi_Authoring_Guide.md:
定制路径写法([AiDefinition] + AiScript)、建态原语(AiStateFragments +
BossFragments + IBossControl)、阶段=换招池、一招怎么配(EnemyAbilitySO +
EnemyAttackSO + 动画事件)、开战触发、死亡归物理层,
以已落地的 ChaoFengAi 为逐段范例,附 Boss 专属陷阱与已知缺口。
08 号指南加交叉引用。

删除两份整份描述已删除且从未实现的体系的文档:
Docs/Architecture/23_BossSkillModule.md、Docs/Design/47_BossSkillSystem.md。
删前已确认全库无 Markdown 链接残留,索引行与关联文档行改指新指南或 spec。

修订:25_CharacterArchitectureOverview §4 Boss 章节按实际代码重写;
07_EnemyModule 加过时提示、§11 改重定向;02_EventSystem 修正 Boss 事件发送方;
19_BossPatternLibrary 加废止提示(设计意图仍有效,类型名已废);
AssetFolderSpec 删旧 Boss 技能编辑器条目;三处 README/索引同步。
Docs_Dev/ 历史记录保留不动。

spec 标记为已实施,并记录实施期偏差(无 Boss 骨架、Telegraph 枚举保留、
阶段直查不走黑板、BossSkillEvent 一并删除)与八项已知未完成项,
其中 EnemyHurtState 无受击动画永久卡死为最高优先级、且与 Boss 轨无关。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 17:14:32 +08:00

652 lines
32 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 25 · 角色系统架构全景图
> **命名空间** `BaseGames.Player`、`BaseGames.Player.States`、`BaseGames.Enemies`、`BaseGames.Enemies.Abilities`、`BaseGames.AI`、`BaseGames.Combat`
> **路径** `Assets/_Game/Scripts/Player/`、`Assets/_Game/Scripts/Enemies/`、`Assets/_Game/Scripts/Combat/`
> **关联文档** [05_PlayerModule](05_PlayerModule.md) · [06_CombatModule](06_CombatModule.md) · [07_EnemyModule](07_EnemyModule.md) · [09_ProgressionModule](09_ProgressionModule.md) · [`Docs/Guides/08_EnemyAi_Authoring_Guide`](../Guides/08_EnemyAi_Authoring_Guide.md) · [`Docs/Guides/09_BossAi_Authoring_Guide`](../Guides/09_BossAi_Authoring_Guide.md)
> **建立日期** 2026-05-17  **§4 Boss 章节重写** 2026-07-30Boss 与小怪单轨统一)
>
> ⚠️ §3.3「AI — Behavior Designer 节点库」仍描述已移除的第三方行为树插件,尚未重写;
> 当前决策层是自研 `BrainGraph`,写法以上方两份作者指南为准。
本文档是跨模块的**角色系统实现全景图**,侧重三类角色(玩家 / 小怪 / Boss)在 Scene 中的节点结构、脚本职责划分、子系统数据流与相互协作关系。各模块深度细节请参阅上方关联文档。
---
## 目录
1. [程序集依赖总览](#1-程序集依赖总览)
2. [玩家(Player](#2-玩家-player)
- 2.1 [Scene 节点结构](#21-scene-节点结构)
- 2.2 [FSM 状态机](#22-fsm-状态机)
- 2.3 [数值体系 PlayerStats](#23-数值体系-playerstats)
- 2.4 [形态系统 Form System](#24-形态系统-form-system)
- 2.5 [武器系统 Weapon System](#25-武器系统-weapon-system)
- 2.6 [技能系统 Skill System](#26-技能系统-skill-system)
- 2.7 [能力解锁 AbilityType Flags](#27-能力解锁-abilitytype-flags)
- 2.8 [装备护符系统 Equipment / Charm](#28-装备护符系统-equipment--charm)
3. [小怪(Enemy](#3-小怪-enemy)
- 3.1 [Scene 节点结构](#31-scene-节点结构)
- 3.2 [状态机(POCO 状态)](#32-状态机poco-状态)
- 3.3 [AI — Behavior Designer 节点库](#33-ai--behavior-designer-节点库)
4. [Boss](#4-boss)
- 4.1 [继承关系](#41-继承关系)
- 4.2 [Scene 节点结构](#42-scene-节点结构)
- 4.3 [一招的数据流](#43-一招的数据流)
- 4.4 [阶段切换](#44-阶段切换)
5. [共享战斗层 Combat Module](#5-共享战斗层-combat-module)
- 5.1 [核心接口](#51-核心接口)
- 5.2 [HitBox → HurtBox 伤害流水线](#52-hitbox--hurtbox-伤害流水线)
- 5.3 [状态效果系统](#53-状态效果系统)
6. [数据驱动 ScriptableObject 体系](#6-数据驱动-scriptableobject-体系)
7. [架构核心原则总结](#7-架构核心原则总结)
---
## 1. 程序集依赖总览
```
BaseGames.Core
BaseGames.Core.Events
BaseGames.Core.Save
BaseGames.Combat ──────────────────────────────────────────────┐
BaseGames.Combat.StatusEffects │
│ │
↓ ↓
BaseGames.Player BaseGames.Enemies │
BaseGames.Player.States BaseGames.Enemies.AI │
BaseGames.Skills BaseGames.Enemies.Navigation │
BaseGames.Equipment BaseGames.Enemies.Boss.Patterns │
BaseGames.Parry │
└────────────────────────────────────────────────────────┘
```
**原则**:下层程序集不得引用上层。`BaseGames.Combat` 仅依赖 `Core``Player` / `Enemies` 均依赖 `Combat` 而不互相依赖。跨层通信一律通过 `EventChannelSO``ServiceLocator` 完成。
---
## 2. 玩家(Player
### 2.1 Scene 节点结构
```
[Player] ← 根节点(空 GameObject,逻辑锚点)
├── PLY_Player ← 核心行为节点(所有主要组件挂于此)
│ │
│ │── PlayerController ← FSM 协调器(DefaultExecOrder=-100
│ │ IDamageable + IPoiseSource
│ │── PlayerMovement ← 物理移动封装(DefaultExecOrder=-200
│ │ Rigidbody2D 操作 / 土狼时间 / 地面检测
│ │── PlayerStats ← 数值管理(HP/灵魂/灵气/灵泉/LingZhu/能力Flags
│ │ ISaveable + IRestoreOnSave + IRewardTarget
│ │── PlayerCombat ← 连击段 DamageSource 切换 / HitBox 激活接口
│ │── FormController ← 三形态状态机(天魂/地魂/命魂)
│ │── WeaponManager ← 依形态切换 ActiveWeapon + 实例化 HitBoxPrefab
│ │── SkillManager ← 技能槽管理 / 冷却计时 / 资源消耗
│ │── SpringSystem ← 灵泉治疗充能系统
│ │── ParrySystem ← 弹反时序窗口(见 CombatModule §10
│ │── ShieldComponent ← 护盾吸收(IShieldable
│ │── PlayerWallDetector ← 蹬墙感应(贴墙方向 / 触发 WallSlide 条件)
│ │── StatusEffectManager ← IStatusEffectable(火 / 毒 / 硬直效果接收)
│ │── EquipmentManager ← 护符槽管理(Notch 容量体系)
│ │── SkillModifierRegistry ← 护符注入的技能修改器(冷却/费用调节)
│ │── AnimancerComponent ← 动画驱动
│ │ Layer 0:全身状态动画(移动/攻击/受伤/死亡)
│ │ Layer 1Overlay):叠加层动画(灵泉/法术)
│ │── InputBuffer ← 输入缓冲(RequireComponent by PlayerController
│ │── Rigidbody2D ← DynamicFreezeRotation,插值关闭
│ └── CapsuleCollider2D ← isTrigger=false,碰撞体
├── HurtBox ← 受击盒(isTrigger=trueLayer=PlayerHurtBox
│ └── HurtBox.cs GetComponentInParent<IDamageable>() 注入
├── [WeaponSocket] ← HitBox 挂载点(WeaponManager 实例化目标)
│ └── WeaponHitBoxInstance ← 由 WeaponSO.hitBoxPrefab 动态实例化/销毁
│ └── HitBox ← Layer=PlayerHitBoxisTrigger=true
├── GroundCheck ← 地面检测 TransformBoxOverlapNonAlloc 起点)
└── SkillHitBox_Slot ← 技能命中盒挂载点(SkillHitBoxInstance 实例化)
```
> **命名规范**:根节点用 `[Player]`(方括号标识逻辑组节点),具体 GameObject 用 `PLY_` 前缀(PascalCase),子功能节点用 `[WeaponSocket]` 括号命名,检测点用描述性名称 `GroundCheck`。
---
### 2.2 FSM 状态机
`PlayerController` 持有 `Dictionary<Type, PlayerStateBase>` 状态字典,每帧驱动 `_currentState``OnStateUpdate / OnStateFixedUpdate`,并在 `TransitionTo<T>()` 时依次调用 `OnStateExit / OnStateEnter`
```
PlayerStateBase(抽象 POCO,不继承 MonoBehaviour
│ OnStateEnter / OnStateUpdate / OnStateFixedUpdate / OnStateExit
│ virtual bool IsInvincible → DashState override true
│ #if UNITY_EDITOR ValidTransitions → 转换白名单(调试用)
├── IdleState 落地重置 AirJumps;出口:Run / Jump / Dash / Attack / Spring
├── RunState 水平位移 + 朝向;出口同 Idle + WallSlide
├── JumpState 可变跳跃(松键截断 Y 速度)+ 土狼时间消耗
├── FallState FallMultiplier 下落加速;贴墙→WallSlide
├── DashState IsInvincible=true;地面冲刺 dashDistance / 冷却 dashCooldown
├── AerialDashState 空中冲刺消耗 _aerialDashCount;落地重置次数
├── WallSlideState wallSlideSpeed 减速下滑;Space → WallJump
├── WallJumpState 弹离墙壁 X+Y 分量;wallJumpLockTime 方向锁定
├── AttackState 3 段连击(Combo Window 计时);每段调 SetComboSegmentSource
├── AirAttackState 空中攻击,HitBox 朝正前方激活
├── UpAttackState 上劈,HitBox 朝正上方激活
├── DownAttackState 下劈 + OnDownHitConfirmed → trampolineForce 蹦跳反弹
├── HurtState hurtDuration 硬直;Initialize(DamageInfo) 注入伤害信息
├── DeadState 冻结 Rigidbody2D;触发 _onPlayerDied EventChannelSO
├── ParryState 弹反时序窗口(与 ParrySystem 协作)
├── SpringState 治疗动画;高优先级保护窗口防打断
└── SwimState 液体中自由移动(需解锁 AbilityType.Swim
```
**关键设计决策**
- 状态为 POCO 类,不持有 MonoBehaviour 生命周期开销,也不产生 GCDictionary 在 `Awake` 一次性填充)。
- `PlayerController``TakeDamage` 中先查询 `_currentState.IsInvincible`,后查 `_stats.IsInvincible`(无敌帧窗口),双层保护互不依赖。
- `#if UNITY_EDITOR` 转换白名单在 Editor 模式下帮助捕获非法状态跳转,运行时零开销剔除。
---
### 2.3 数值体系 PlayerStats
```
PlayerStats
├── ISaveable → 存读档(SaveManager 调用)
├── IRestoreOnSave → 存档触发时恢复(如灵泉数量)
└── IRewardTarget → RewardSO 颁奖接口(避免 Quest 直接依赖 Player 程序集)
运行时字段
├── HP / MaxHP TakeDamage / Heal / InvincibleTimer
├── SoulPower / Max 法术资源(攻击命中积累)
├── SpiritPower / Max 灵气资源(自动回复,SpiriRegenTimer 驱动)
├── SpringCharges / Max 灵泉治疗充能槽(SpringSystem 消耗)
├── LingZhu 货币(击杀 / 收集 / 购买)
├── AbilityType _unlockedAbilities [Flags] uint 位图,HasAbility(flag) O(1) 查询
├── 护符数值修改器
│ ├── _flatModifiers[StatType] 固定值加成(如 +5 MaxHP)
│ └── _percentModifiers[StatType] 百分比加成(如 +20% SoulPower
├── AnimatorSpeedMultiplier 1f + _animatorSpeedBonus(护符注入攻速)
├── SoulCostReduction 法术费用减免(护符叠加)
└── 难度缩放
订阅 DifficultyChangedEventChannel
→ 按当前 HP 比例重算 MaxHP(保持 HP 比例不跳变)
```
---
### 2.4 形态系统 Form System
```
FormConfigSO(资产,Inspector 拖入 FormController
└── FormSO[] forms
├── FormSO { formId="Form_Sky", formType=Sky, defaultWeapon=WeaponSO_SkyBlade }
├── FormSO { formId="Form_Earth", formType=Earth, defaultWeapon=WeaponSO_EarthHammer }
└── FormSO { formId="Form_Death", formType=Death, defaultWeapon=WeaponSO_DeathScythe }
FormController.SwitchForm(FormType)
├─ 1. IntEventChannelSO _onFormChanged.Raise(index)
│ → SaveSystem 持久化 ActiveFormId
│ → UI HUD 更新形态图标
├─ 2. C# event OnFormChanged
│ → WeaponManager.HandleFormChanged() ← OnEnable 订阅,OnDisable 退订
└─ 3. VoidEventChannelSO _onSkillSetChanged.Raise()
→ SkillHUD 刷新当前形态技能组图标
```
**护符 Override 流**:护符装备时调用 `WeaponManager.SetOverride(formId, weaponSO)` 覆盖指定形态的默认武器;卸下时调用 `ClearOverride(formId)` 还原,无需修改 `FormSO` 资产。
---
### 2.5 武器系统 Weapon System
```
WeaponSO(纯数据 SO
├── 连击动画 attack1/2/3/air/up/downClip Animancer ClipTransition
├── 伤害来源 attack1/2/3/air/up/downSource DamageSourceSO,各段独立配置)
├── hitBoxPrefab → WeaponHitBoxInstance + HitBox 碰撞体
└── soulPowerGain 命中后灵魂增加量(覆盖默认值)
WeaponManager(运行时)
│ FormController.OnFormChanged ──►
│ ApplyWeapon(FormSO)
│ ├── 检查 _overrides[formId](护符 Override 优先)
│ └── SetDirectWeapon(WeaponSO)
│ ├── Destroy 旧 HitBox GameObject
│ ├── Instantiate 新 hitBoxPrefab 到 [WeaponSocket]
│ └── OnWeaponChanged.Invoke(newWeapon)
│ └── PlayerCombat 订阅:刷新 _currentHitBoxInstance 引用
PlayerCombat
├── SetComboSegmentSource(comboIndex) ← AttackState 每段开始时调用
│ comboIndex 0/1/2 → attack1/2/3Source → HitBoxInstance.SetDamageSource
├── EnableWeaponHitBox(AttackDirection) ← 动画事件 / State 调用
│ GetSourceByDir → Activate(dir, source, ownerTransform)
└── OnDownHitConfirmed ← DownAttackState 订阅
命中时向上施加 trampolineForce
```
---
### 2.6 技能系统 Skill System
```
FormSkillSOCreateAssetMenu: BaseGames/Skills/FormSkill
├── Identity skillId / displayNameKey / icon
├── Resource resourceTypeSoulPower | SpiritPower/ baseCost / cooldown
├── Animation castAnimationClipTransition/ castLockDuration(施放锁定秒数)
├── Effect effectTypeMeleeAoE / Projectile / BarrierAura / WraithDash /
│ GroundDive / DragonKick / ShadowDecoy / DelayedExplosion
├── Projectile projectileConfig / isHoming / holdForContinuous
├── Dash dashForce / dashDuration / isInvincibleDuringDash
├── Explosion explosionDelay / explosionRadius
├── Feedback castFeedbackFeedbackPresetSO
└── hitBoxPrefab → 近战/爆炸技能命中盒(SkillHitBoxInstance + HitBox
SkillManagerBaseGames.Skills 程序集)
├── FormSkillSO[] _slots 按 SkillSlotNames 枚举索引
├── SkillModifierRegistry _mods 护符注入的冷却/费用修改器
├── float[] _cooldownTimers 每槽独立冷却计时
└── ExecuteSkill(slotIndex)
├── 检查冷却 / 资源(baseCost - SoulCostReduction
├── PlayerStats.SpendResource(type, cost)
├── Animancer.Play(castAnimation) + 锁定输入 castLockDuration 秒
└── 依 effectType 分支执行:投射物 / AoE HitBox / Dash / 爆炸延迟 …
```
---
### 2.7 能力解锁 AbilityType Flags
```csharp
[Flags] enum AbilityType : uint
{
None = 0,
// 移动
WallCling = 1u << 0, // 贴墙悬挂
WallJump = 1u << 1, // 墙跳
Dash = 1u << 2, // 地面冲刺
AirDash = 1u << 3, // 空中二段冲刺
DoubleJump = 1u << 4, // 二段跳
SuperJump = 1u << 5, // 聚气超跳
Swim = 1u << 6, // 液体游泳
Dive = 1u << 7, // 下劈
// 法术
Spell1/2/3 = 1u << 8~10,
// 形态
SpiritForm = 1u << 11,
SpiritDash = 1u << 12,
// 战斗
Parry = 1u << 13,
ChargeAttack = 1u << 14,
DownSlash = 1u << 15,
// 互动
Interact = 1u << 16,
FastTravel = 1u << 17,
// 强化
InvincibleDash = 1u << 18, // Dash 无敌帧强化
}
```
- `PlayerStats.HasAbility(flag)` → O(1) 位与运算,无分支列表遍历。
- 状态类在 `OnStateEnter``GetNextState` 中调用 `Stats.HasAbility(AbilityType.WallJump)` 等判断是否允许转换。
- `AbilityManager`Progression 模块)解锁时调用 `PlayerStats.UnlockAbility(flag)` 并写入存档。
---
### 2.8 装备护符系统 Equipment / Charm
```
CharmSO
├── notchCost Notch 槽位消耗量
└── ICharmEffect[] effects
├── OnEquip(EquipmentContext)
│ 可修改:PlayerStats 数值 / WeaponManager Override / SkillModifierRegistry
└── OnUnequip(EquipmentContext)
还原所有修改(避免副作用残留)
EquipmentContext(注入包,Awake 时构建)
├── PlayerStats
├── PlayerFeedback
├── SkillModifierRegistry
└── WeaponManager
EquipmentManagerISaveable
├── _currentNotchCapacity 初始值来自 EquipmentConfigSO,可解锁扩容
├── _usedNotches 缓存值(避免每次 LINQ Sum)
├── TryEquipCharm(charm) 容量检查 → fx.OnEquip → 事件广播
├── UnequipCharm(charm) fx.OnUnequip → 事件广播
└── 事件频道
├── CharmEventChannelSO _onCharmEquipped
├── CharmEventChannelSO _onCharmUnequipped
└── VoidEventChannelSO _onEquipmentChanged → UI 刷新
```
---
## 3. 小怪(Enemy
### 3.1 Scene 节点结构
```
[Enemy_SpiderGuard] ← 根节点(挂 EnemyBase 或具体子类)
│ EnemyBase(或 RangedEnemy / FlyingEnemy
│ ├── EnemyStats 运行时 HP / Defense / AttackCooldown
│ │ Initialize(EnemyStatsSO) 注入;难度缩放订阅
│ ├── EnemyMovement Rigidbody2D 封装
│ │ MoveHorizontal / FaceTarget / Knockback / JumpTo
│ ├── EnemyCombat 攻击范围 / 伤害触发(近战/弹幕调度)
│ ├── EnemyFeedback 受击闪烁 / 音效 / HitStop / 受击特效
│ ├── EnemyPoiseComponent IPoiseSource 实现(霸体等级声明)
│ ├── AnimancerComponent
│ ├── BehaviorTree Opsive Behavior Designer 资产绑定
│ └── EnemyNavAgent IPathAgent 实现(PathBerserker2D waypoint/jump 寻路)
├── HurtBox isTrigger=trueLayer=EnemyHurtBox
├── HitBox_Melee isTrigger=trueLayer=EnemyHitBox
│ └── HitBox.cs
└── BodyContactDamage(可选) 碰撞体直接造成接触伤害
```
**子类扩展**
- `RangedEnemy`:额外持有 `ProjectileManager` 引用 / 弹幕发射逻辑覆盖
- `FlyingEnemy`:禁用地面检测,使用独立飞行移动逻辑
---
### 3.2 状态机(POCO 状态)
```
EnemyStateType(枚举)+ Dictionary<EnemyStateType, IEnemyState>
IEnemyState
├── StateType 枚举值(字典键)
├── Enter(EnemyBase owner)
└── Exit(EnemyBase owner)
具体状态
├── EnemyControlledState 正常 AI 驱动(Behavior Tree 运行中)
├── EnemyHurtState 受击硬直(播放受击动画,短暂停止 BD)
├── EnemyStaggerState 霸体破防强硬直(较长,期间 BD 暂停)
└── EnemyDeadState 死亡(IsAlive=false / 关闭碰撞体 / 播放死亡动画 / 掉落战利品)
```
**TakeDamage 伤害判定流**
```
EnemyBase.TakeDamage(DamageInfo)
├── if Dead → return
├── EnemyStats.TakeDamage(finalDamage)
├── EnemyFeedback.OnHit(info) 受击视觉/音效反馈
├── if HP <= 0 → Die()
└── else
├── 比较 info.PoiseBreak vs EnemyPoiseComponent.CurrentPoiseLevel
├── PoiseBreak ≥ CurrentPoise → ForceState(Stagger)
├── PoiseBreak < CurrentPoise → ForceState(Hurt)(仅 Feedback,不打断 BD
└── 霸体完全抵抗 → 仅 Feedback(受击特效,BD 不中断)
```
---
### 3.3 AI — Behavior Designer 节点库
所有 BD 节务类位于 `Assets/_Game/Scripts/Enemies/AI/`,前缀 `BD_`
| 分类 | 任务类 | 功能 |
|------|--------|------|
| **感知** | `BD_IsPlayerVisible` | BatchLOSSystem 查询视线(批量 RaycastBurst 加速) |
| | `BD_IsPlayerInRange` | `EnemyStats.SqrDistanceToPlayer < range²`(避免 sqrt |
| | `BD_IsHPBelow` | `EnemyStats.CurrentHP / MaxHP < ratio` |
| | `BD_IsNearEdge` | 边缘检测(防坠落巡逻) |
| | `BD_IsGrounded` | 地面状态查询 |
| | `BD_IsStateMatch` | 查询当前 EnemyStateType |
| **移动** | `BD_MoveTo` | 目标点直线移动(EnemyNavAgent |
| | `BD_MoveToPlayer` | 追击玩家位置 |
| | `BD_Patrol` | 往返巡逻(PatrolPoints[] 路径点) |
| | `BD_JumpTo` | 抛物线跳跃到目标位置 |
| | `BD_TeleportTo` | 瞬移(Boss 阶段过渡用) |
| | `BD_StopMovement` | 停止水平速度 |
| | `BD_FaceTarget` | 朝向玩家/目标 |
| **战斗** | `BD_Attack` | 触发近战攻击(EnemyCombat |
| | `BD_CanAttack` | 攻击冷却检查(AttackCooldownTimer <= 0 |
| | `BD_SpawnProjectile` | 生成投射物(ProjectileManager |
| | `BD_TelegraphAttack` | 激活 TelegraphSystem 预兆提示 |
| **特殊** | `BD_SummonMinions` | 召唤小怪(Boss 用) |
| | `BD_EnterPhase` | 调用 BossBase.EnterPhase(phase) |
| | `BD_SetAlert` | 设置警觉状态标记(影响巡逻/追击切换) |
| **动画** | `BD_PlayAnimation` | Animancer 播放指定 Clip |
| | `BD_WaitForAnimation` | 等待当前动画播放完毕 |
| **时序** | `BD_Wait` | 等待固定秒数 |
| | `BD_WaitRandom` | 等待随机时长(min~max |
`BatchLOSSystem`:单例系统,每帧收集所有 `ILOSRequester` 的视线查询,批量执行 Raycast(可选 Burst Job),结果写回各敌人缓存,避免 N 个敌人同帧各自独立 Physics2D.Raycast 的性能峰值。
---
## 4. Boss
### 4.1 继承关系
```
MonoBehaviour
└── EnemyBaseIDamageable / ILOSRequester
└── BossBase+ BaseGames.AI.IBossControl
├── 阶段:CurrentPhase / EnterPhase(int) + BossPhaseEventChannelSO 广播
├── 阶段过渡:BeginPhaseTransition(targetPhase, invincibleDuration)
│ → IsPhaseTransitioning(期间 IsInvincible 为真)→ EnterPhase
├── IsHPBelow(float ratio):供 AI 图的条件边读取
├── 竞技场锚点:AnchorAt(i) / DistanceToAnchor(i)(转发 BossArenaAnchors
└── override Die() → 中止阶段过渡 + _onBossFightEnded.Raise(true)
└── [具体 Boss 类]
override EnterPhase / OnBeginPhaseTransition → 额外过渡演出
```
**Boss 没有独立的技能体系。** 招式即敌人能力(`EnemyAbilitySO` + `EnemyAbilityBase`),
选招即 `EnemyAttackSelector`,决策即 `EnemyAiBrain` 上的 `AiScript` 图。
Boss 专属的只有 `IBossControl` 这一个决策面加几个旁挂 MonoBehaviour。
---
### 4.2 Scene 节点结构
```
[ENM_XxxBoss]
│ 具体 Boss 脚本(继承 BossBase,实现 IBossControl
│ ├── EnemyStats / EnemyMovement / EnemyLocomotion / EnemyFeedback
│ ├── GroundNavigator(或 FlyingNavigator
│ ├── PhysicsPerceptionSystem 感知(追逐区 / 视野 / 攻击范围)
│ ├── EnemyAiBrain 决策层(_definitionId 指向 [AiDefinition] 的 AiScript
│ ├── BossPhaseAbilityGate 阶段 = 换招池:按阶段启停能力组件
│ ├── BossArenaAnchors(可选) 竞技场定点走位的锚点集合
│ ├── EnemyAbilityBase × N 每招一个组件,各自引用一份 EnemyAbilitySO
│ ├── BossResource(可选) 怒气 / 充能;是否已满经 IBossControl.ResourceFull 读取
│ └── AnimancerComponent
├── HurtBox 主体受击盒(Layer=EnemyHurtBox
│ └── 弱点受击盒同样是 HurtBox,靠 _damageMultiplier 表达倍率,无独立弱点系统
├── ContactDamageZone 身体接触伤害(常驻,Layer=EnemyHitBox
├── HitBox_XXX × N 各近战招的判定盒(默认禁用,由动画事件按时机开关)
├── XxxMuzzle × N 弹体发射点
└── [场景侧] BossFightTrigger 房间入口触发区:发开战事件 + AiSignal.Engaged
```
---
### 4.3 一招的数据流
```
EnemyAbilitySOCreateAssetMenu: BaseGames/Enemies/Enemy Ability,前缀 ABL_
├── abilityId / designNote
├── attackSequence: EnemyAttackSO[] 一招内的多段
├── cooldown 从执行结束起计
├── interruptOnHurt / interruptOnStagger 中断规则
├── category / weight / priority 选招器用
├── rangeRadius / rangeOffset 招式自管的圆形触发范围
└── requiresGrounded / requiresLineOfSight 选招硬门
EnemyAttackSO(前缀 EATK_)—— 一段动作的时间轴
├── clip: ClipTransitionAnimancer
├── hitBoxSlot / hitBoxEnterT / hitBoxExitT 判定开关的归一化时机
├── damageSource: DamageSourceSO
├── projectileConfig / projectileCount / projectileFireT
└── hasPoiseWindow / poiseLevel / poiseStartT / poiseEndT
执行链:
AI 图的攻击态 OnEnter
└── IAiContext.Combat.UseBestAttack()
└── EnemyAttackSelector 从 category==Attack 且 CanUse 的能力里选一个
CanUse 含 enabled、冷却、射程、grounded / LOS 硬门;
WeightedRandomAntiRepeat 模式对上次选中的招施加权重折扣)
└── EnemyAbilityBase.Execute():逐段播 clip
判定 / 生成 / 音效 / 无敌帧全部由 clip 上的动画事件驱动
EnemyAnimationEvents.HandleEvent
```
---
### 4.4 阶段切换
```
AI 图的条件边(如 x.Vitals.HpBelow(0.5f)
└── 转入阶段过渡态(BossFragments.PhaseTransition
OnEnterLocomotion.Stop() + Combat.InterruptAbilities()
+ IBossControl.BeginPhaseTransition(targetPhase, invincibleDuration)
BossBase.BeginPhaseTransition
├── IsPhaseTransitioning = true → IsInvincible 随之为真
├── OnBeginPhaseTransition(targetPhase) [子类重写:浮空 / 过渡动画]
├── 等待 invincibleDuration
├── EnterPhase(targetPhase)
│ ├── _currentPhase = phase
│ ├── BossPhaseAbilityGate.ApplyPhase(phase)
│ │ → 按阶段表设置各能力组件的 enabled
│ │ → 被禁用的能力 CanUse 为 false,自动从选招器候选消失
│ └── _onBossPhaseChanged.Raise(BossPhaseEvent{BossId, Phase})
│ → Boss HP 条分段动画 / BGM 段落切换 / 场景 VFX
└── IsPhaseTransitioning = false
→ AI 图上 .To(下一态).When(x => !x.Boss.IsPhaseTransitioning) 放行
```
**阶段不是 SO 上的字段,也不是选招器里的过滤条件——阶段就是"哪些能力组件当前启用"。**
`BossBase.OnSpawn``ApplyPhase(0)`,保证对象池复用时回到初始招池。
作者写法详见 [`Docs/Guides/09_BossAi_Authoring_Guide.md`](../Guides/09_BossAi_Authoring_Guide.md)。
---
## 5. 共享战斗层 Combat Module
### 5.1 核心接口
| 接口 | 实现者 | 用途 |
|------|--------|------|
| `IDamageable` | `PlayerController` / `EnemyBase` | HurtBox 统一调用入口,解耦具体角色类型 |
| `IPoiseSource` | `PlayerController`(返回 None/ `EnemyPoiseComponent` | 霸体等级声明,TakeDamage 中比较 |
| `IShieldable` | `ShieldComponent`(玩家专属) | 伤害先经护盾吸收,剩余量才走 TakeDamage |
| `IStatusEffectable` | `StatusEffectManager` | 状态效果施加入口,Combat 程序集不直接引用 StatusEffects |
| `IBreakable` | 可破坏机关 / 障碍物 | HitBox 命中非 HurtBox 对象时的分支处理 |
| `IPathAgent` | `EnemyNavAgent` | EnemyBase 通过接口引用导航,避免对 Navigation 程序集的直接依赖 |
---
### 5.2 HitBox → HurtBox 伤害流水线
```
HitBox.OnTriggerEnter2D(Collider2D other)
├── 1. Layer 白名单过滤(仅命中 EnemyHurtBox / PlayerHurtBox
├── 2. _alreadyHit HashSet 防重复命中(同次激活期间)
├── 3. other.GetComponentInParent<HurtBox>() 获取受击盒
└── HurtBox.ReceiveDamage(DamageInfo)
├── 1. IsAlive 检查
├── 2. IDamageable.IsInvincible 检查
│ (冲刺无敌帧 / DeadState / Stats.InvincibleTimer
├── 3. IShieldable.AbsorbDamage(amount)
│ 护盾优先吸收,返回穿透量
├── 4. IPoiseSource 霸体等级比较
│ DamageInfo.PoiseBreakLevel vs GetCurrentPoiseLevel()
├── 5. DamageInfo.FinalDamage 计算(基础 - Defense
├── 6. IStatusEffectable.ApplyStatusEffect(DamageType)
Fire → FireEffect / Poison → PoisonEffect
├── 7. IDamageable.TakeDamage(info)
│ → PlayerController / EnemyBase 状态机转换
└── 8. HitConfirmedEventChannelSO.Raise(HitInfo)
→ HUD 命中提示 / 灵魂增加 / HitStop 触发
```
---
### 5.3 状态效果系统
```
StatusEffectManagerIStatusEffectable
├── ApplyStatusEffect(DamageType type)
│ → 查找或创建对应 StatusEffect 实例并激活
├── FireEffect 持续 DoTTick 扣 HP/ 视觉火焰 VFX
├── PoisonEffect 持续 DoT + 移动速度降低(可叠加层数)
└── StaggerEffect 触发 EnemyStaggerState / 破霸体
StatusEffectEventChannelSO 广播效果开始/结束(UI 状态图标)
```
---
## 6. 数据驱动 ScriptableObject 体系
| SO 类型 | 所属程序集 | 用途 |
|---------|-----------|------|
| `PlayerStatsSO` | `BaseGames.Player` | 玩家初始数值、最大值配置 |
| `PlayerMovementConfigSO` | `BaseGames.Player` | 移动速度/加速/跳跃力/冲刺参数等 |
| `PlayerAnimationConfigSO` | `BaseGames.Player` | 各状态动画 ClipTransition 集合 |
| `FormConfigSO` / `FormSO` | `BaseGames.Player` | 形态列表 + 各形态默认武器 |
| `WeaponSO` | `BaseGames.Player` | 武器连击动画/伤害来源/HitBox Prefab |
| `FormSkillSO` | `BaseGames.Skills` | 技能全量配置(动画/资源/效果/HitBox) |
| `CharmSO` / `EquipmentConfigSO` | `BaseGames.Equipment` | 护符效果列表 + Notch 容量初始值 |
| `DamageSourceSO` | `BaseGames.Combat` | 伤害值/类型/霸体破防等级/击退参数 |
| `ProjectileConfigSO` | `BaseGames.Combat` | 投射物速度/碰撞层/生命时长/弹幕参数 |
| `EnemyStatsSO` | `BaseGames.Enemies` | 敌人 HP/Defense/速度/攻击冷却 |
| `EnemyAnimationConfigSO` | `BaseGames.Enemies` | 敌人动画 Clip 集合 |
| `EnemyAbilitySO` | `BaseGames.Enemies.Abilities` | 一招的全量配置(id/冷却/权重/射程/中断规则);小怪与 Boss 共用 |
| `EnemyAttackSO` | `BaseGames.Enemies.Abilities` | 一段动作的时间轴(clip + 判定/弹体/霸体的归一化时机) |
| `AiRecipeSO` | `BaseGames.Enemies` | 小怪 AI 配方(未发现层 + 交战层两个可插拔模块)。Boss 不用配方,走 `[AiDefinition]` 定制图 |
| `BossResourceConfigSO` | `BaseGames.Enemies`Boss) | Boss 资源(怒气/充能)配置 |
**设计原则**:所有运行时组件在 `Awake` 中通过 `Initialize(SO)` 接收配置,与数据资产完全解耦,支持 Inspector 热替换和难度 A/B 测试,无需修改脚本代码。
---
## 7. 架构核心原则总结
| 原则 | 具体体现 |
|------|---------|
| **单一职责** | 移动 / 数值 / 战斗 / 动画各为独立 MonoBehaviour`PlayerController` 仅负责 FSM 协调,不持有业务逻辑 |
| **事件驱动** | `EventChannelSO`(广播,跨程序集零直接引用)+ C# event(点对点,同程序集高频回调)双轨并行 |
| **数据与逻辑分离** | 所有配置数据存入 ScriptableObject;运行时组件只持有 SO 引用,数值修改在 SO 层完成 |
| **接口隔离** | `IDamageable / IPoiseSource / IShieldable / IStatusEffectable / IBreakable / IPathAgent` 六大接口隔离具体实现 |
| **状态不继承 MB** | `PlayerStateBase` 为 POCO 类,生命周期由 `PlayerController` 驱动,无 GC 开销 |
| **能力位图** | `AbilityType [Flags] uint` 支持任意组合查询,O(1) 位与运算,无枚举列表遍历 |
| **难度热切换** | `EnemyStats` / `PlayerStats` 均订阅 `DifficultyChangedEventChannel`,保持 HP 比例重算,运行时切换零重置 |
| **护符副作用隔离** | `ICharmEffect.OnEquip / OnUnequip` 配对调用,每个效果负责自身还原,`EquipmentManager` 不持有修改记录 |
| **GC 意识** | `WaitForSeconds` 协程缓存、`_alreadyHit HashSet` 复用、`SqrDistanceToPlayer`(避免 sqrt)、`BatchLOSSystem` 批量 Raycast |
| **执行顺序管控** | `PlayerMovement -200``PlayerController -100` → 其余默认 0,确保物理写入先于状态机读取 |