30 KiB
25 · 角色系统架构全景图
命名空间
BaseGames.Player、BaseGames.Player.States、BaseGames.Enemies、BaseGames.Enemies.AI、BaseGames.Boss、BaseGames.Combat
路径Assets/_Game/Scripts/Player/、Assets/_Game/Scripts/Enemies/、Assets/_Game/Scripts/Combat/
关联文档 05_PlayerModule · 06_CombatModule · 07_EnemyModule · 09_ProgressionModule · 23_BossSkillModule
建立日期 2026-05-17
本文档是跨模块的角色系统实现全景图,侧重三类角色(玩家 / 小怪 / Boss)在 Scene 中的节点结构、脚本职责划分、子系统数据流与相互协作关系。各模块深度细节请参阅上方关联文档。
目录
- 程序集依赖总览
- 玩家(Player)
- 2.1 Scene 节点结构
- 2.2 FSM 状态机
- 2.3 数值体系 PlayerStats
- 2.4 形态系统 Form System
- 2.5 武器系统 Weapon System
- 2.6 技能系统 Skill System
- 2.7 能力解锁 AbilityType Flags
- 2.8 装备护符系统 Equipment / Charm
- 小怪(Enemy)
- 3.1 Scene 节点结构
- 3.2 状态机(POCO 状态)
- 3.3 AI — Behavior Designer 节点库
- Boss
- 4.1 继承关系
- 4.2 Scene 节点结构
- 4.3 技能执行流 BossSkillSO + BossSkillExecutor
- 4.4 阶段切换
- 共享战斗层 Combat Module
- 5.1 核心接口
- 5.2 HitBox → HurtBox 伤害流水线
- 5.3 状态效果系统
- 数据驱动 ScriptableObject 体系
- 架构核心原则总结
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 1(Overlay):叠加层动画(灵泉/法术)
│ │── InputBuffer ← 输入缓冲(RequireComponent by PlayerController)
│ │── Rigidbody2D ← Dynamic,FreezeRotation,插值关闭
│ └── CapsuleCollider2D ← isTrigger=false,碰撞体
│
├── HurtBox ← 受击盒(isTrigger=true,Layer=PlayerHurtBox)
│ └── HurtBox.cs GetComponentInParent<IDamageable>() 注入
│
├── [WeaponSocket] ← HitBox 挂载点(WeaponManager 实例化目标)
│ └── WeaponHitBoxInstance ← 由 WeaponSO.hitBoxPrefab 动态实例化/销毁
│ └── HitBox ← Layer=PlayerHitBox,isTrigger=true
│
├── GroundCheck ← 地面检测 Transform(BoxOverlapNonAlloc 起点)
│
└── 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 生命周期开销,也不产生 GC(Dictionary 在
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
FormSkillSO(CreateAssetMenu: BaseGames/Skills/FormSkill)
│
├── Identity skillId / displayNameKey / icon
├── Resource resourceType(SoulPower | SpiritPower)/ baseCost / cooldown
├── Animation castAnimation(ClipTransition)/ castLockDuration(施放锁定秒数)
├── Effect effectType(MeleeAoE / Projectile / BarrierAura / WraithDash /
│ GroundDive / DragonKick / ShadowDecoy / DelayedExplosion)
├── Projectile projectileConfig / isHoming / holdForContinuous
├── Dash dashForce / dashDuration / isInvincibleDuringDash
├── Explosion explosionDelay / explosionRadius
├── Feedback castFeedback(FeedbackPresetSO)
└── hitBoxPrefab → 近战/爆炸技能命中盒(SkillHitBoxInstance + HitBox)
SkillManager(BaseGames.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) 位与运算,无分支列表遍历。- 状态类在
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
EquipmentManager(ISaveable)
├── _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=true,Layer=EnemyHurtBox
│
├── HitBox_Melee isTrigger=true,Layer=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 查询视线(批量 Raycast,Burst 加速) |
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
└── EnemyBase(IDamageable / ILOSRequester)
└── BossBase
├── 多阶段切换:EnterPhase(int) + BossPhaseEventChannelSO 广播
├── IsHPBelow(float ratio) → BD_IsHPBelow 轮询条件
└── override Die() → _onBossFightEnded.Raise(true)
└── [具体 Boss 类]
override EnterPhase → 额外过渡逻辑(动画/无敌帧/BD 子树切换)
4.2 Scene 节点结构
[Boss_XxxGuardian]
│ BossBase(具体 Boss 脚本,继承 BossBase)
│ ├── EnemyStats / EnemyMovement / EnemyCombat / EnemyFeedback
│ ├── BossSkillExecutor 接收 BD/Orchestrator 指令,执行 BossSkillSO 协程
│ ├── WeakPointSystem 弱点激活窗口管理(BossSkillExecutor 技能后开启)
│ ├── TelegraphSystem 攻击预兆提示(激光标线 / 范围高亮 / 地面警示)
│ ├── BehaviorTree 含 BD_EnterPhase 的行为树资产
│ └── AnimancerComponent
│
├── HurtBox 主体受击盒(Layer=EnemyHurtBox)
│
├── WeakPoint_Head 弱点受击盒
│ └── HurtBox(弱点专属,WeakPointSystem 控制激活/关闭)
│
├── HitBox_Slam 近战攻击 HitBox(Layer=EnemyHitBox)
├── HitBox_Sweep 横扫 HitBox(可与 BossSkillSO 绑定)
│
└── BossArenaBlocker(可选) 竞技场封锁触发器(Boss 战开始时封门)
4.3 技能执行流 BossSkillSO + BossSkillExecutor
BossSkillSO(CreateAssetMenu: BaseGames/Boss/BossSkill)
├── skillId / displayName
├── BossSkillType(枚举)
├── AttackPatternSO
│ ├── DamageSource(DamageSourceSO)
│ ├── KnockbackAngle
│ ├── 弹幕参数:ProjectilePrefab / Count / SpreadAngle / Speed
│ ├── AoE 参数:Radius / Offset
│ └── 时序:WindupDuration / ActiveDuration / RecoveryDuration
├── VulnerabilityWindowDuration 技能结束后弱点暴露时长(秒)
└── SkillSequenceSO(可选) 组合技序列(按顺序或随机排列多个 BossSkillSO)
BossSkillExecutor.ExecuteSkill(BossSkillSO)
│ if _isExecuting → return
│
└── StartCoroutine(ExecuteSkillCoroutine)
├── _onBossSkillStarted.Raise(BossSkillEvent)
├── TelegraphSystem.ShowTelegraph(skill.pattern) 预兆提示激活
├── WaitForSeconds(WindupDuration) 前摇等待
├── 激活 HitBox[] / ProjectileManager.Spawn(pattern) 伤害输出
├── WaitForSeconds(ActiveDuration) 活跃帧等待
├── 关闭 HitBox / 清理弹幕
├── WeakPointSystem.OpenWindow(VulnerabilityWindowDuration) 弱点窗口
├── WaitForSeconds(RecoveryDuration) 后摇等待
└── _onBossSkillEnded.Raise(BossSkillEvent)
WaitForSeconds 缓存:
static Dictionary<float, WaitForSeconds> _wfsCache
[RuntimeInitializeOnLoadMethod] ClearWFSCache()
→ 避免协程每次 new WaitForSeconds 的 GC 分配
InterruptCurrentSkill():阶段切换时强制停止当前协程并调用 FinishExecution(),确保状态一致性。
4.4 阶段切换
BD_IsHPBelow(每帧轮询)
└── BossBase.IsHPBelow(ratio) = CurrentHP / MaxHP < ratio
→ true 时 BD 子树激活 BD_EnterPhase
BD_EnterPhase → BossBase.EnterPhase(phase)
├── _currentPhase = phase
├── BossPhaseEventChannelSO _onBossPhaseChanged.Raise(BossPhaseEvent{BossId, Phase})
│ → Boss HP 条分段动画(UI)
│ → 音乐系统切换 BGM 段落
│ → VFX 场景效果触发
└── [子类重写 EnterPhase]
├── BossSkillExecutor.InterruptCurrentSkill() 打断当前技能
├── 切换 BD 行为树活跃子树(换招式库)
├── 播放阶段过渡动画(Animancer)
└── 短暂无敌帧(保护阶段切换不被秒杀)
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 状态效果系统
StatusEffectManager(IStatusEffectable)
├── ApplyStatusEffect(DamageType type)
│ → 查找或创建对应 StatusEffect 实例并激活
│
├── FireEffect 持续 DoT(Tick 扣 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 集合 |
AttackPatternSO |
BaseGames.Enemies(Boss) |
攻击图案:伤害/弹幕/AoE/时序 |
BossSkillSO |
BaseGames.Enemies(Boss) |
Boss 技能全量配置 |
SkillSequenceSO |
BaseGames.Enemies(Boss) |
Boss 组合技序列(有序/随机) |
BossResourceConfigSO |
BaseGames.Enemies(Boss) |
Boss 阶段资源(HP 阈值/技能池) |
设计原则:所有运行时组件在 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,确保物理写入先于状态机读取 |