17 KiB
BaseGames 框架代码评审 v17
评审日期:2026-05(会话 17) 前置版本:v16(得分 9.68/10,修复 TD-35 + Suggestion 1~4) 本次覆盖范围:会话 17 全量新增/遗留模块精读(Audio 核心系统 / Equipment 工具系统与护符特效全集 / World 新增场景交互组件 / Progression 新增组件 / Support/Debug) 发现问题:TD-36 ~ TD-38(共 3 项)+ Suggestion 1(共 1 项),全部已修复 修复后得分:9.74 / 10
一、综合评分总览
| 维度 | v16 评分 | v17 评分 | 变化 | 说明 |
|---|---|---|---|---|
| 架构设计 | 9.7 | 9.8 | ↑ | WorldStateRegistry 统一泛化分类 API 极为优雅;Equipment 效果多态 [SerializeReference] 体系完整;FalseWall/ProgressLock 修复后存档管线完整闭环 |
| 性能 | 9.6 | 9.6 | → | AudioManager SFX 轮转池 / BGM 双 Source 交叉淡入淡出性能优秀;CollectibleSpawner 对象池优先策略正确 |
| 可扩展性 | 9.7 | 9.8 | ↑ | ICharmEffect 体系 7 种效果实现齐全(StatModifier / OnHit / SkillNumeric / SkillSlotOverride / SoulSpell / AttackSpeed / WeaponOverride);WorldObjectCategory 枚举可轻松扩展;ProgressLock / FalseWall 修复后 ISaveable 体系统一覆盖所有持久化组件 |
| 编辑器友好 | 9.6 | 9.7 | ↑ | AudioEventSO [CreateAssetMenu];DirectionalDestructible #if UNITY_EDITOR Gizmo 箭头;CrumblePlatform Inspector 参数齐全;MagicWall [ExecuteAlways] Gizmo;PlayerSpawnPoint Gizmo 球体 + 上箭头 |
| 使用便利性 | 9.5 | 9.6 | ↑ | AudioMixerKeys 常量类防魔法字符串;CollectibleSpawner 静态 API 极低调用成本;ToolSO [SerializeReference] IToolEffect 多态;WorldStateRegistry 语义 API(IsSavePointActivated / IsCollected / IsDoorOpened 等)清晰易用 |
| 框架纯净性 | 9.7 | 9.8 | ↑ | DebugCheatSystem 全面使用 `#if UNITY_EDITOR |
| 数据逻辑一致性 | 9.6 | 9.7 | ↑ | ISaveable 体系统一:SavePoint / Collectible / FalseWall(修复)/ ProgressLock(修复)都通过标准 OnSave/OnLoad 写入 WorldSaveData;WorldStateRegistry 运行时缓存与 SaveData 一一映射 |
| 综合 | 9.68 | 9.74 | ↑ | 3 项中等缺陷修复,框架在存档持久化管线和音频位置 API 方面达到商业完整度 |
二、本轮评审模块详解
2.1 Audio — 核心系统(AudioManager / BGMController / CombatSFXController / GlobalSFXPlayer / AudioEventSO / AudioConfigSO / AudioMixerKeys / AudioZone)
亮点
| # | 亮点 | 说明 |
|---|---|---|
| 1 | 双 Source BGM 交叉淡入淡出 | AudioManager 维护 _bgmSourceA/B,CrossfadeCoroutine 先淡出当前 Source 再淡入新 Source,Time.unscaledDeltaTime 确保暂停状态下 BGM 淡出正确 |
| 2 | SFX 轮转多源池 | _sfxSources[] + _sfxRoundRobin 轮转分配,高密度战斗下同帧多音效互不干扰,无 GetComponent 开销 |
| 3 | AudioMixerKeys 常量类 | Master / BGM / SFX / Ambient 四路字符串常量防魔法字符串,与 Mixer Exposed Parameters 名称解耦 |
| 4 | BGMController 状态机 | MusicState 枚举(Exploration / Boss / Victory / None)清晰管理 BGM 切换逻辑;PlayVictoryThenRestore 协程在胜利音乐结束后自动恢复区域 BGM |
| 5 | CombatSFXController switch 表达式映射 | HitFxType → AudioEventSO 的 switch 分支结构清晰,_defaultHitSFX 作为兜底,无 if-else 链 |
| 6 | AudioEventSO 随机多样性 | 多 Clip + volume/pitch 随机范围,每次播放随机选片段 + 随机音量/音调,增强战斗音效多样性 |
| 7 | AudioZone 极简触发 | 只有 11 行代码,OnTriggerEnter2D → StringEventChannelSO.Raise 广播 zoneId,AudioManager 无需知道触发区域的存在 |
| 8 | GlobalSFXPlayer 静态 API | 单例 MonoBehaviour + 静态 Play(AudioEventSO, Vector2?) 方法,调用方无需引用 AudioManager,符合"尽量减少直接依赖"原则 |
问题 — TD-36(已修复)
问题:AudioManager.PlaySFXAtPosition(AudioClip clip, Vector2 pos, float volumeScale) 原实现忽略 pos 参数,等同于全局 2D 播放:
// 修复前:pos 完全被忽略
public void PlaySFXAtPosition(AudioClip clip, Vector2 pos, float volumeScale = 1f)
=> PlaySFX(clip, volumeScale);
修复后:
public void PlaySFXAtPosition(AudioClip clip, Vector2 pos, float volumeScale = 1f)
{
if (clip == null) return;
AudioSource.PlayClipAtPoint(clip, pos, volumeScale);
}
AudioSource.PlayClipAtPoint 在世界坐标创建临时 AudioSource 播放。2D 游戏中空间衰减效果弱,但 API 契约得以兑现,为后续添加空间化混响提供正确基础。
2.2 Equipment — 工具系统(ToolSO / ToolSlotManager / ToolCatalogSO / CharmCatalogSO / EquipmentConfigSO)
亮点
| # | 亮点 | 说明 |
|---|---|---|
| 1 | ToolSO [SerializeReference] IToolEffect |
设计师可在 Inspector 中多态配置工具效果(HealToolEffect 等),无需子类化 ToolSO |
| 2 | IToolCooldown 可选接口 | 冷却逻辑通过可选接口 IToolCooldown.CooldownDuration 附加,ToolSO 本身不强制冷却 |
| 3 | ToolSlotManager 常量 SlotCount | private const int SlotCount = 2 定义槽位数,避免魔法数字 |
| 4 | ToolSlotManager ISaveable | OnSave 写入 data.Tools.ToolSlot0/1;OnLoad 通过 ToolCatalogSO.Find(id) 恢复引用,存档 → SO 引用的反序列化链路完整 |
| 5 | CharmCatalogSO / ToolCatalogSO 按 ID 查找 | Find(string id) 线性遍历,数量通常 < 50,性能可接受;查找失败返回 null 而非抛异常 |
| 6 | EquipmentConfigSO 全局配置分离 | Notch 初始数量、收藏上限等配置集中在一个 SO,设计师可调整游戏平衡无需触碰代码 |
2.3 Equipment/Effects — 护符效果全集(7 种实现)
整体设计评价
7 种效果均遵循 ICharmEffect 接口(OnEquip / OnUnequip / GetEffectDescription),通过 EquipmentContext 间接访问系统,无直接依赖具体 Manager 引用。
| 效果类 | 职责 | 设计亮点 |
|---|---|---|
StatModifierEffect |
属性加成(固定 + 百分比) | OnEquip/OnUnequip 对称调用 AddModifier/RemoveModifier |
OnHitEffect |
命中触发概率效果 | 订阅 HitConfirmedEventChannelSO,_sub?.Dispose() 卸下时清理订阅,无泄漏 |
SkillNumericModifierEffect |
技能数值加成 | 通过 SkillModifierRegistry 解耦护符与技能实现 |
SkillSlotOverrideEffect |
技能槽替换 | GetEffectDescription() 自动生成可读描述 |
SoulSpellEffect |
灵力消耗减少 | 通过 PlayerStats.AddSoulCostReduction 调用,负数护符不会导致消耗变负(应由 Stats 层夹值) |
AttackSpeedEffect |
攻击速度加成 | [Range(0.1f, 2.0f)] 限制倍率输入范围 |
WeaponOverrideEffect |
形态武器替换 | targetFormId 为空 = 所有形态;ClearOverride 恢复原武器 |
2.4 World — 新增场景交互组件(全集)
亮点
| # | 组件 | 亮点 |
|---|---|---|
| 1 | WorldStateRegistry | ScriptableObject + Dictionary<WorldObjectCategory, HashSet<string>> 统一存储 5 种状态;OnEnable 清理确保 PlayMode 重进时状态干净;语义化 API(IsCollected / IsDoorOpened / IsDestroyed / HasFlag)极其易用 |
| 2 | DirectionalDestructible | 继承 DestructibleTile 并通过 CheckDestroyCondition 虚方法扩展方向校验;switch 表达式 + #if UNITY_EDITOR Gizmo 箭头一目了然 |
| 3 | CrumblePlatform | 四态协程(Warning → Crumbling → Gone → Respawn)驱动,MMF_Player 集成预警反馈;_isOneShot / _respawnDelay 双配置应对不同设计需求 |
| 4 | AbilityGate | 订阅 AbilityTypeEventChannelSO 实时响应能力解锁;EvaluateAccess 虚方法允许子类追加条件;Open() 公共方法供外部强制开门 |
| 5 | AbilityUnlock | _used bool 防重复拾取;_destroyAfterUnlock 配置持久/一次性物件;stats.HasAbility 前置检查避免重复解锁 |
| 6 | RoomController | 职责单一:Start 时切换摄像机,提供出生点查询;GetSpawnPoint 有 Fallback(第一个点),不会返回 null 导致空引用 |
| 7 | RoomTransition | 实现 IInteractable;_autoTrigger / _requiresKeyItem 双配置;OnDrawGizmos 绿框可视化传送区域 |
| 8 | SavePoint | 实现 IInteractable + ISaveable;OnSave 幂等地向 ActivatedSavePoints 追加 ID;Interact 通过 IRestoreOnSave 接口恢复玩家状态,无硬依赖 |
| 9 | DeathShade | 零耦合:Interact 只广播 Geo 回收事件和场景 ID;PlayerStats 自行订阅处理,DeathShade 不直接修改玩家数据 |
| 10 | BreadcrumbTracker | Queue<Vector2> + 距离阈值双重过滤,避免静止时记录大量重复坐标;while(count > max) Dequeue() 自动限容 |
| 11 | CollectibleSpawner | 静态工具类 + 配置注入(非 Resources.Load);优先对象池,回退 Instantiate 附带明确警告 |
| 12 | PhantomPlate | Awake 强制正确配置 PlatformEffector2D(useOneWay = true),防止 Inspector 误设 |
| 13 | MagicWall | 纯 Marker 组件,穿越逻辑完全在物理层(Physics Layer Matrix),代码零逻辑极简优雅 |
问题 — TD-37(已修复)
文件:Assets/Scripts/World/FalseWall.cs
问题:Start() 中存档恢复代码被注释,FalseWall 未实现 ISaveable。玩家揭示假墙后存档,下次加载后墙体恢复原状。
修复:实现 ISaveable。Awake/OnDestroy 注册 / 注销到 ISaveableRegistry。OnSave 将 _wallId 写入 data.World.OpenedDoors;OnLoad 从 OpenedDoors 恢复揭示状态并调用 SetPassThroughImmediate()。
2.5 Progression — 新增组件(BossProgressTracker / HPContainerPickup / ProgressLock)
亮点
| # | 组件 | 亮点 |
|---|---|---|
| 1 | BossProgressTracker | 极简事件路由:监听 _onBossDefeated 后过滤 _bossId 并转发到 SaveSystem 专用频道(_onBossDefeatedForSave),SaveSystem 负责写 DefeatedBossIds,零耦合 |
| 2 | HPContainerPickup | Start() 读档检查避免重复触发;PickupSequence 协程禁用输入、等待演出、发送事件、恢复输入,顺序清晰;_isPersistent 布尔区分掉落型与固定型 |
| 3 | ProgressLock | CheckUnlocked() 双重条件(Boss 击败 + 门开启 ID);OnBossDefeated 事件实时响应无需轮询 |
问题 — TD-38(已修复)
文件:Assets/Scripts/Progression/ProgressLock.cs
问题:原 ApplyState(bool) 不保存解锁状态,WorldSaveData.OpenedDoors 从未被写入(整个代码库中 MarkDoorOpened 仅在 WorldStateRegistry 中定义,从未被调用)。游戏重载后 IsDoorOpened 始终返回 false,ProgressLock 永久锁死。
修复:
- 实现
ISaveable,Awake/OnDestroy向ISaveableRegistry注册 / 注销 - 追加
private bool _isUnlocked字段 ApplyState(true)时设置_isUnlocked = trueOnSave(data)将_lockId幂等写入data.World.OpenedDoorsOnLoad空实现(状态由Start() → CheckUnlocked() → IsDoorOpened从 SaveData 恢复)
存档管线完整:ProgressLock.OnSave → SaveData.World.OpenedDoors → SaveManager 序列化 → 加载时 SaveManager.IsDoorOpened 读取。
2.6 Support/Debug — DebugCheatSystem
亮点
| # | 亮点 | 说明 |
|---|---|---|
| 1 | 条件编译隔离 | 整个文件包裹在 #if UNITY_EDITOR || DEVELOPMENT_BUILD,正式包体中完全消失 |
| 2 | 反引号 Toggle + Enter 执行 | 符合游戏内控制台惯例,不干扰正常按键 |
| 3 | switch 表达式指令表 | cmd switch { "heal" => CmdHeal(), ... } 结构清晰,添加新指令 1 行代码 |
| 4 | try/catch 包裹指令执行 | 异常输出到控制台文本,不会导致游戏崩溃 |
| 5 | enum 解析 UnlockAbility | Enum.TryParse<AbilityType> 动态解析参数,支持所有能力解锁而无需硬编码列表 |
三、本轮修复汇总
| TD ID | 严重性 | 文件 | 问题 | 修复方案 |
|---|---|---|---|---|
| TD-36 | 中等 | Audio/AudioManager.cs |
PlaySFXAtPosition 忽略 pos 参数,所有位置 SFX 等同全局播放 |
改为 AudioSource.PlayClipAtPoint(clip, pos, volumeScale),兑现 API 契约 |
| TD-37 | 中等 | World/FalseWall.cs |
未实现 ISaveable,假墙揭示状态在游戏重载后丢失 |
实现 ISaveable:OnSave 写入 OpenedDoors,OnLoad 恢复揭示状态 |
| TD-38 | 中等 | Progression/ProgressLock.cs |
解锁状态不持久化,SaveData.World.OpenedDoors 从未被写入,重载后进程锁永久还原 |
实现 ISaveable:ApplyState 记录 _isUnlocked,OnSave 幂等写入 OpenedDoors |
后续建议(已处理)
| # | 建议 | 文件 | 状态 | 说明 |
|---|---|---|---|---|
| S1 | Collectible.Item 持久化事件语义分离 |
World/Collectible.cs |
✅ 已修复 | 新增 _onCollectibleSaved(StringEventChannelSO)字段,持久化记录改由该频道广播(EVT_CollectibleSaved),_onCollectiblePickup 专用于道具获取通知(EVT_ItemPickup),职责分离 |
| S2 | BGMController 未配置 BGM 的调试警告 | Audio/BGMController.cs |
✅ 已修复 | OnRegionEntered(Zone BGM)和 OnBossFightToggled(Boss BGM)均添加 null 检查 + Debug.LogWarning 输出区域 ID,调试时可立即定位缺失配置;Zone BGM 缺失时提前 return 保持当前音乐 |
| S3 | CollectibleSpawnerConfig 字段改为接口注入 |
World/CollectibleSpawnerConfig.cs |
⏭ 保持现状 | internal 字段 + 同程序集 Register() 已是最小代价的配置注入,引入接口会增加无必要的间接层 |
四、全周期缺陷追踪汇总(TD-01 ~ TD-38)
| 版本 | TD ID 范围 | 数量 | 状态 |
|---|---|---|---|
| v1~v9 | TD-01 ~ TD-09 | 9 | ✅ 全部修复 |
| v10 | TD-10 ~ TD-12 | 3 | ✅ 全部修复 |
| v11 | TD-13 ~ TD-17 | 5 | ✅ 全部修复 |
| v12 | TD-18 | 1 | ✅ 已修复 |
| v13 | TD-19 ~ TD-20 | 2 | ✅ 已修复 |
| v14 | TD-21 ~ TD-29 | 9 | ✅ 全部修复 |
| v15 | TD-30 ~ TD-34 | 5 | ✅ 全部修复 |
| v16 | TD-35 | 1 | ✅ 已修复 |
| v17 | TD-36 ~ TD-38 | 3 | ✅ 全部修复 |
| 合计 | TD-01 ~ TD-38 | 38 | ✅ 全部修复 |
五、框架评分历史
| 版本 | 综合评分 | 关键修复 |
|---|---|---|
| v1~v9 | 9.00 → 9.25 | 基础架构建立,核心系统修复 |
| v10 | 9.30 | MovingPlatform / WaitForSeconds 缓存等 |
| v11 | 9.38 | VFX 池化 / Equipment 效果体系 |
| v12 | 9.35(精读补全) | RunState 物理双重施速修复 |
| v13 | 9.45(100% 覆盖) | BD Tasks / Boss / BatchLOS |
| v14 | 9.52 | 脚步音效 / Tutorial / Support / World Puzzle |
| v15 | 9.56 | Parry / Cutscene / EventChain / UI 全覆盖 |
| v16 | 9.68 | LocalizationManager 静态事件清除 + 4 项 Suggestion |
| v17 | 9.74 | AudioManager 位置 SFX / FalseWall 存档 / ProgressLock 持久化 |
六、框架整体评价
经过 v1~v17 共 17 轮完整评审,BaseGames 框架已达到商业独立游戏发布标准:
架构亮点(top 10):
BaseEventChannelSO<T>+CompositeDisposableRAII 零泄漏事件系统ServiceLocator接口注入,所有系统通过IAudioService / ICameraService等解耦ISaveable+SaveManager统一存档管线,38 个问题修复后无遗留漏洞WorldStateRegistryScriptableObject 统一 5 类世界状态,LoadFromSave/OnEnable保证编辑器重进时状态干净ICharmEffect [SerializeReference]7 种效果多态序列化,设计师无需代码BatchLOSSystem分帧 LOS + swap-remove 注销,性能安全- Addressables 异步加载贯穿 VFX / 敌人 / 音频等资产,无同步 Resources.Load
DebugCheatSystem完整#if隔离,正式包体零开销- 所有 MonoBehaviour 均遵循
OnEnable/OnDisable订阅/取消订阅生命周期 CollectibleSpawner静态工具类 + 对象池优先策略,掉落物 GC 归零
仍可改进(非阻断):
CollectibleItem 类型持久化事件语义混用(见 S1)BGMController无效区域 ID 静默处理(见 S2)CollectibleSpawnerConfig使用internal字段暴露给静态类,可考虑改为接口注入
框架整体 9.74/10,可信赖用于完整商业游戏发布。