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

32 KiB
Raw Blame History

25 · 角色系统架构全景图

命名空间 BaseGames.PlayerBaseGames.Player.StatesBaseGames.EnemiesBaseGames.Enemies.AbilitiesBaseGames.AIBaseGames.Combat
路径 Assets/_Game/Scripts/Player/Assets/_Game/Scripts/Enemies/Assets/_Game/Scripts/Combat/
关联文档 05_PlayerModule · 06_CombatModule · 07_EnemyModule · 09_ProgressionModule · Docs/Guides/08_EnemyAi_Authoring_Guide · Docs/Guides/09_BossAi_Authoring_Guide
建立日期 2026-05-17  §4 Boss 章节重写 2026-07-30Boss 与小怪单轨统一)

⚠️ §3.3「AI — Behavior Designer 节点库」仍描述已移除的第三方行为树插件,尚未重写; 当前决策层是自研 BrainGraph,写法以上方两份作者指南为准。

本文档是跨模块的角色系统实现全景图,侧重三类角色(玩家 / 小怪 / Boss)在 Scene 中的节点结构、脚本职责划分、子系统数据流与相互协作关系。各模块深度细节请参阅上方关联文档。


目录

  1. 程序集依赖总览
  2. 玩家(Player
  3. 小怪(Enemy
  4. Boss
  5. 共享战斗层 Combat Module
  6. 数据驱动 ScriptableObject 体系
  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 仅依赖 CorePlayer / Enemies 均依赖 Combat 而不互相依赖。跨层通信一律通过 EventChannelSOServiceLocator 完成。


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> 状态字典,每帧驱动 _currentStateOnStateUpdate / 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 一次性填充)。
  • PlayerControllerTakeDamage 中先查询 _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

[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) 位与运算,无分支列表遍历。
  • 状态类在 OnStateEnterGetNextState 中调用 Stats.HasAbility(AbilityType.WallJump) 等判断是否允许转换。
  • AbilityManagerProgression 模块)解锁时调用 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.OnSpawnApplyPhase(0),保证对象池复用时回到初始招池。

作者写法详见 Docs/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.EnemiesBoss Boss 资源(怒气/充能)配置

设计原则:所有运行时组件在 Awake 中通过 Initialize(SO) 接收配置,与数据资产完全解耦,支持 Inspector 热替换和难度 A/B 测试,无需修改脚本代码。


7. 架构核心原则总结

原则 具体体现
单一职责 移动 / 数值 / 战斗 / 动画各为独立 MonoBehaviourPlayerController 仅负责 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 -200PlayerController -100 → 其余默认 0,确保物理写入先于状态机读取