25 KiB
BaseGames 框架深度评审报告 — v4(2026-May)
适用版本:zeling_v2 · Unity 2022.3 LTS · C# 2D Action RPG
评审范围:Assets/Scripts/全源码(v1+v2+v3 所有 18 个问题均已修复后的净化状态)
前置文档:v1 · v2 · v3(禁止反向修改)
评审原则:新框架不做兼容兜底,保持纯净、一致的数据逻辑
一、总体评分
| 维度 | 当前得分 | v1 基准 | 提升 |
|---|---|---|---|
| 架构设计 | ★★★★★ 9.5 | 7.0 | +2.5 |
| 性能优化 | ★★★★☆ 8.5 | 7.5 | +1.0 |
| 可扩展性 | ★★★★★ 9.5 | 8.0 | +1.5 |
| 编辑器友好性 | ★★★★★ 9.5 | 8.5 | +1.0 |
| 使用便利性 | ★★★★☆ 8.5 | 7.5 | +1.0 |
| 综合 | ★★★★★ 9.1 | 7.7 | +1.4 |
二、架构设计 ★★★★★ 9.5
2.1 服务定位器(ServiceLocator)
文件:Assets/Scripts/Core/Events/ServiceLocator.cs
// 核心实现:Dictionary<Type, object>,O(1) 查找
private static readonly Dictionary<Type, object> _services = new();
// 安全版查找:不抛异常,适用于可选服务
public static TInterface GetOrDefault<TInterface>(TInterface fallback = default)
=> _services.TryGetValue(typeof(TInterface), out var svc) && svc is TInterface typed
? typed : fallback;
// 安全注销:仅当注册实例与 impl 一致时才移除,避免新实例被旧 OnDestroy 清除
public static void Unregister<TInterface>(TInterface impl)
{
if (_services.TryGetValue(typeof(TInterface), out var svc) && ReferenceEquals(svc, impl))
_services.Remove(typeof(TInterface));
}
亮点:
- 全接口键注册:经 v1–v3 三轮修复,代码库中零个具体类型键注册。所有 25+ 服务均以接口类型注册(
IAudioService、ISaveService、IClashService等) RegisterIfAbsent<T>防止多场景重复注册(NullAudioService 回退机制)- 安全
Unregister<T>(impl)防止 OnDestroy 顺序问题导致后注册者被误删 #if UNITY_EDITOR提供OverrideForTest<T>/Reset()专用测试钩子
服务注册总表(v3 修复后):
| 接口键 | 注册方 |
|---|---|
IAudioService |
GameServiceRegistrar(Null兜底)→ AudioManager 覆盖 |
IDeathRespawnService |
GameServiceRegistrar |
ISceneService |
GameServiceRegistrar |
IEventChannelRegistry |
EventChannelRegistry |
ISaveService |
GameServiceRegistrar(via SaveServiceAdapter) |
ISaveableRegistry |
SaveManager |
ICameraService |
CameraStateController |
IClashService |
ClashResolver |
IDifficultyService |
DifficultyManager |
IObjectPoolService |
GlobalObjectPool |
IHitStopService |
HitStopManager |
ILocalizationService |
LocalizationManager(MonoBehaviour 实例) |
IMapService |
MapManager |
IProjectileService |
ProjectileManager |
IQuestManager |
QuestManager |
ISettingsService |
SettingsManager |
ITutorialService |
TutorialManager |
IVFXPoolService |
VFXPool |
IAchievementService |
AchievementManager |
IAnalyticsService |
AnalyticsManager |
IDialogueService |
DialogueManager |
IPlatformService |
PlatformBootstrap |
2.2 事件系统(双轨制,规则清晰)
轨道一:SO 事件频道(跨系统广播)
// BaseEventChannelSO<T> — 核心实现
public EventSubscription Subscribe(Action<T> callback)
{
OnEventRaised += callback;
return new EventSubscription(() => OnEventRaised -= callback);
}
// 使用侧 (OnEnable / OnDisable RAII)
private void OnEnable()
=> _onDifficultyChanged?.Subscribe(HandleDifficultyChanged).AddTo(_subs);
private void OnDisable()
=> _subs.Clear();
轨道二:C# native event (同一 MonoBehaviour 树内部)
// FormController 暴露给 WeaponManager(同 Player Prefab 节点上)
public event Action OnFormChanged;
// WeaponManager
private void OnEnable() => _formController.OnFormChanged += HandleFormChanged;
private void OnDisable() => _formController.OnFormChanged -= HandleFormChanged;
规则执行情况:
- SO 事件用于 Persistent 场景→Game 场景跨程序集广播 ✅
- C# 事件仅限同一 Prefab 内部节点(FormController→WeaponManager,WeaponManager→PlayerCombat)✅
- InputReaderSO C# 事件供所有需要输入的组件订阅(SkillManager、ParrySystem 等)✅
Debug 支持:
// BaseEventChannelSO.Raise() 在 UNITY_EDITOR 中自动记录到 EventBusMonitor
#if UNITY_EDITOR
EventBusMonitor.Record(name, value?.ToString() ?? "null", _subscriberCount, Time.frameCount);
#endif
2.3 存档系统
架构分层清晰,三层职责不重叠:
ISaveService(Core 接口)
└── SaveServiceAdapter(桥接适配器,位于 Core)
└── SaveManager(Core.Save 程序集,ISaveable + ISaveableRegistry)
└── ISaveStorage(接口)
└── LocalFileStorage(写临时→原子替换 + .bak 恢复)
SaveData 结构 — 完整域覆盖 + 前向兼容:
public class SaveData
{
[JsonExtensionData]
public Dictionary<string, JToken> ExtensionData = new(); // 未知字段保留
public SaveMeta Meta; // 版本、时间戳、HMAC、SteelSoul 标记
public PlayerSaveData Player; // HP、形态、DeathShade、护盾
public EquipmentSaveData Equipment; // 护符(已装备/已收集/已升级)
public WorldSaveData World; // 访问场景、门、Boss、收集物
public MapSaveData Map; // Pins(v2.1 新增)
public QuestSaveData Quests;
public AchievementSaveData Achievements;
public EventChainsSaveData EventChains;
public ChallengeRoomsSaveData ChallengeRooms;
public ShopsSaveData Shops;
public StatsSaveData Stats; // SpeedrunTime(v2.1 新增)
public NGPlusSaveData NGPlus; // null = 非 NG+ 模式
public TutorialSaveData Tutorial;
public SettingsSaveData Settings;
public Dictionary<string, JObject> DLC; // DLC/Mod 扩展槽
}
SaveMigrator — fall-through 链式迁移(当前版本 2.1):
// 旧版本 → 2.0 → 2.1(每段落下执行)
if (IsOlderThan(v, "2.0")) { /* 补充 Tutorial/Settings/EventChains 等 */; v = "2.0"; }
if (v == "2.0") { /* 补充 Map.Pins、Stats.SpeedrunTime */; v = "2.1"; }
2.4 战斗管线(8 步流水线)
HurtBox.ReceiveDamage() 严格按 8 步顺序执行:
1. 无敌帧检查(IsInvincible || _isHurtBoxInvincible)
2. 弹反窗口消耗(ParrySystem.ConsumeParry())
3. 霸体检查(PoiseLevel vs BreakLevel)
4. 护盾层拦截(IShieldable.AbsorbDamage())
5. 防御减免(max(1, Amount - Defense))
6. 扣血(IDamageable.TakeDamage(info))
7. 全局广播(_onDamageDealt?.Raise / _onHitConfirmed?.Raise)
8. 状态效果触发(IStatusEffectable.ApplyStatusEffect())
DamageInfo Builder 模式实现零 GC 伤害数据构造:
var info = new DamageInfo.Builder()
.SetRaw(20).SetType(DamageType.Physical)
.SetKnockback(dir, 6f).SetBreak(BreakLevel.Light)
.Build();
2.5 玩家 FSM(非 MonoBehaviour 状态对象)
// PlayerStateBase — 基类
public abstract class PlayerStateBase
{
protected PlayerController _owner;
protected PlayerStateBase(PlayerController owner) => _owner = owner;
public virtual void OnStateEnter() { }
public virtual void OnStateUpdate() { }
public virtual void OnStateFixedUpdate() { }
public virtual void OnStateExit() { }
public virtual PlayerStateBase GetNextState() => null;
public virtual bool IsInvincible => false;
#if UNITY_EDITOR
public virtual IReadOnlyList<Type> ValidTransitions => Array.Empty<Type>();
#endif
// 便捷属性:Input / Buffer / Move / Stats / Anim / Cfg...
}
优势:状态对象不继承 MonoBehaviour → 无 GameObject 开销,构造函数注入依赖,Editor 可声明合法转换白名单。
2.6 程序集分层
28 个 .asmdef 程序集,依赖方向严格单向:
BaseGames.Core.Events ←── BaseGames.Core.Save ←── BaseGames.Core
↑ ↑
BaseGames.Combat ──────────────────────────────── BaseGames.Player
BaseGames.Enemies ──── BaseGames.Enemies.AI BaseGames.Equipment
BaseGames.World ──────────────────────────────── BaseGames.World.Map
...(各业务程序集均向 Core 单向依赖,互不交叉)
autoReferenced: true 仅用于 BaseGames.Core 和 BaseGames.Core.Save,其余程序集需显式引用。
三、性能优化 ★★★★☆ 8.5
3.1 对象池
GlobalObjectPool(Addressables 驱动):
// 池数据结构
private readonly Dictionary<string, Queue<PooledObject>> _pools; // 空闲池
private readonly Dictionary<string, LinkedList<PooledObject>> _alive; // 活跃追踪(仅MaxCount>0时分配)
private readonly Dictionary<string, GameObject> _prefabCache;
private readonly Dictionary<string, int> _maxCounts;
// EnsureCollections:按需分配 _alive,避免无上限池的额外开销
if (_maxCounts.GetValueOrDefault(key, 0) > 0 && !_alive.ContainsKey(key))
_alive[key] = new LinkedList<PooledObject>();
优点:无上限池(MaxCount = 0)不创建 _alive 链表,按需分配节省内存。
3.2 SkillManager 冷却遍历
// 固定大小数组快照,Update 内零 GC 遍历
private FormSkillSO[] _activeSkills = Array.Empty<FormSkillSO>();
private void Update()
{
for (int i = 0; i < _activeSkills.Length; i++)
{
var s = _activeSkills[i];
if (_cooldowns.TryGetValue(s, out float cd) && cd > 0f)
_cooldowns[s] = cd - Time.deltaTime;
}
}
形态切换时重建快照(不超过 3 个技能),避免 List<T> 每帧 foreach 开销。
3.3 HitBox 命中去重
// 容量预设为 8,避免触发扩容
private readonly HashSet<Collider2D> _hitThisActivation = new(8);
private readonly Dictionary<Collider2D, float> _hitCooldownTimers = new(8);
// OnTriggerExit2D 清理离场对象,防止持续激活时无限积累
private void OnTriggerExit2D(Collider2D other)
=> _hitCooldownTimers.Remove(other);
3.4 HurtBox 缓存
// Awake 中一次性缓存,避免受击时 GetComponent 查找
private IStatusEffectable _statusEffectable;
private void Awake()
{
_owner = GetComponentInParent<IDamageable>();
_statusEffectable = GetComponentInParent<IStatusEffectable>();
}
// 受击步骤 8
_statusEffectable?.ApplyStatusEffect(info.Type);
3.5 PlayerStats 数值修改器
// O(1) Dictionary 键值存取,无 LINQ 叠加计算
private readonly Dictionary<StatType, float> _flatModifiers = new();
private readonly Dictionary<StatType, float> _percentModifiers = new();
⚠️ 性能注意点
[P-1] HitBox 物理回调中 ServiceLocator 查询未缓存
文件:Assets/Scripts/Combat/HitBox.cs
// 当前实现:每次 OnTriggerEnter2D 触发拼刀分支时调用
private void OnTriggerEnter2D(Collider2D other)
{
// ... 前置条件判断 ...
if (isRivalHitBoxLayer && CanClash)
{
var rivalHitBox = other.GetComponent<HitBox>();
if (rivalHitBox != null && rivalHitBox.IsActive && rivalHitBox.CanClash)
{
ServiceLocator.GetOrDefault<IClashService>()?.ResolveClash(this, rivalHitBox); // ← 每次查
return;
}
}
// ...
}
ServiceLocator 本质是 Dictionary<Type, object>.TryGetValue,单次调用开销极小(约 10–15 ns)。但在密集战斗场景(多敌人同帧触发拼刀)时,建议在 Awake 中缓存:
// 建议改法
private IClashService _clashService;
private void Awake()
{
// ...
_clashService = ServiceLocator.GetOrDefault<IClashService>();
}
四、可扩展性 ★★★★★ 9.5
4.1 ScriptableObject 驱动设计
框架核心 SO 类型一览:
| SO 类型 | 用途 | 可扩展点 |
|---|---|---|
CharmSO + ICharmEffect[] |
护符效果插件化 | 新增护符无需改代码 |
FormSkillSO |
形态技能配置 | 技能效果 SO 化 |
WeaponSO + DamageSourceSO |
武器/伤害源 | 连招段 SO 化 |
DifficultyScalerSO |
难度数值缩放器 | 新增难度档无需改枚举 |
FormConfigSO + FormSO |
形态定义 | 新增形态修改 SO 即可 |
EnemyStatsSO |
敌人基础属性 | 调参无需改代码 |
PoolConfig[] |
对象池预热配置 | Inspector 直接配置 |
4.2 ICharmEffect 策略模式
public interface ICharmEffect
{
void OnEquip(EquipmentContext ctx);
void OnUnequip(EquipmentContext ctx);
}
// 新增护符只需实现 ICharmEffect,在 CharmSO.effects[] 中配置
// EquipmentContext 封装了 Stats / Feedback / Events / SkillMods / WeaponMgr
4.3 ISaveable + ISaveableRegistry
// 任何组件实现 ISaveable 即可参与存档
public interface ISaveable
{
string SaveId { get; }
void OnBeforeSave(SaveData data);
void OnAfterLoad(SaveData data);
}
// SaveManager 实现 ISaveableRegistry,统一管理所有 Saveable 组件
public interface ISaveableRegistry
{
void Register(ISaveable saveable);
void Unregister(ISaveable saveable);
}
当前实现了 ISaveable 的组件:PlayerStats、EquipmentManager、MapManager、AchievementManager、QuestManager 等,均通过接口均匀参与存档,无特殊耦合。
4.4 GameStateMachine 可插入状态
public class GameStateMachine
{
private readonly Dictionary<GameStateId, IGameState> _states = new();
public void Register(IGameState state) => _states[state.Id] = state;
public bool TransitionTo(GameStateId nextId, out string error)
{
if (!_current.ValidNextStates.Contains(nextId))
{
error = $"非法转换 {_current.Id} → {nextId}";
return false;
}
// ...
}
}
新增游戏状态:实现 IGameState,调用 Register() 注入,无需修改状态机本体。
4.5 SaveData 前向兼容
[JsonExtensionData]
public Dictionary<string, JToken> ExtensionData = new(); // 未知字段保留,不丢失数据
public Dictionary<string, JObject> DLC = new(); // DLC/Mod 独立命名空间
保证未来 DLC 或 Mod 扩展的存档互操作不破坏主线存档结构。
五、编辑器友好性 ★★★★★ 9.5
5.1 Event Bus Monitor
文件:Assets/Scripts/Editor/EventBusMonitorWindow.cs
快捷键:Ctrl+Shift+E(BaseGames/Tools/Event Bus Monitor)
功能:
- 实时捕获所有 BaseEventChannelSO.Raise() 调用
- 显示:频道名 · 负载值 · 订阅数 · 帧号
- Filter 文本框过滤频道
- Pause / Auto Scroll 控制
- Clear 一键清空
实现原理:BaseEventChannelSO.Raise() 在 #if UNITY_EDITOR 中调用 EventBusMonitor.Record(),运行时零开销,Editor 模式下完整记录。
5.2 Scene Scaffold Tools
文件:Assets/Scripts/Editor/SceneScaffoldTools.cs
BaseGames/Tools/Scaffold Persistent Scene
一键在当前场景中生成完整 Persistent 场景层级:
[Services]/GameServiceRegistrar、DeathRespawnService、SceneService等[Input]/InputReader[Camera]/CameraStateController[UI]/UIManager、HUDController
使用 GetOrAddComponent<T>() 幂等操作,重复执行不会重复创建组件。
5.3 其他 Editor 工具
| 工具 | 用途 |
|---|---|
EventChainEditorWindow |
事件链可视化编辑 |
BossSkillSequenceWindow |
Boss 技能序列配置 |
AddressKeyValidator |
验证 Addressables Key 是否存在 |
AddressReferenceGraphWindow |
可视化 Addressables 引用依赖图 |
ScriptExecutionOrderTools |
批量调整脚本执行顺序 |
NavSurfaceBakeShortcut |
PathBerserker2d 导航面快捷烘焙 |
5.4 运行时 Debug 支持
HurtBox Gizmos(Editor 模式下可视):
#if UNITY_EDITOR
private void OnDrawGizmos()
{
Gizmos.color = (_isActive && !_isHurtBoxInvincible)
? new Color(1f, 0f, 0f, 0.45f) // 激活:红色半透明填充
: new Color(1f, 0f, 0f, 0.1f); // 无敌/非激活:极淡
Gizmos.DrawCube(col.bounds.center, col.bounds.size);
Gizmos.DrawWireCube(col.bounds.center, col.bounds.size);
}
#endif
HurtBox Editor 只读属性(Inspector 调试用,避免反射):
#if UNITY_EDITOR
public object EditorOwner => _owner;
public object EditorParrySystem => _parrySystem;
public object EditorStatusEffectable => _statusEffectable;
// ...
#endif
Debug.Assert 在 Awake(Inspector 漏配置立即报错,不等运行中崩溃):
Debug.Assert(_config != null, "[PlayerStats] _config 未赋值", this);
Debug.Assert(_ctx.Stats != null, "[EquipmentManager] 缺少 PlayerStats", this);
六、使用便利性 ★★★★☆ 8.5
6.1 统一服务访问模式
// 标准模式:可选服务 → GetOrDefault(不抛异常)
var audio = ServiceLocator.GetOrDefault<IAudioService>();
audio?.PlaySFX(clipId);
// 必须服务 → Get(不存在时抛出清晰错误)
var scene = ServiceLocator.Get<ISceneService>();
6.2 RAII 事件订阅模式
private readonly CompositeDisposable _subs = new();
private void OnEnable()
{
_onGameStateChanged?.Subscribe(HandleGameStateChanged).AddTo(_subs);
_onPauseRequested?.Subscribe(TogglePause).AddTo(_subs);
}
private void OnDisable() => _subs.Clear();
覆盖率:代码库中所有跨场景 SO 事件订阅 100% 使用此模式。未发现遗漏。
6.3 DamageInfo Builder
var info = new DamageInfo.Builder()
.SetRaw(damage)
.SetType(DamageType.Physical)
.SetKnockback(knockDir, 5f)
.SetFlags(DamageFlags.CanBeParried | DamageFlags.CanClash)
.SetBreak(BreakLevel.Light)
.SetFx(HitFxType.Spark)
.Build();
6.4 PlayerStateBase 便捷属性
protected InputReaderSO Input => _owner.Input;
protected InputBuffer Buffer => _owner.Buffer;
protected PlayerMovement Move => _owner.Movement;
protected PlayerStats Stats => _owner.Stats;
protected AnimancerComponent Anim => _owner.Animancer;
protected PlayerMovementConfigSO Cfg => _owner.MovConfig;
每个状态子类无需重复持有引用,直接通过属性访问 Owner 依赖。
6.5 Inspector 零依赖接线
所有跨 GameObject 依赖均通过 [SerializeField] 在 Inspector 中绑定,无场景内 Find、无 GetComponent 跨层扫描。GameObject 树内部的兄弟组件依赖通过 GetComponent<T>()/GetComponentInParent<T>() 在 Awake 中就地获取。
七、新问题清单(v4 首次发现)
以下问题为本轮评审首次发现,v1–v3 中未涉及。
| ID | 严重级别 | 文件 | 问题描述 |
|---|---|---|---|
| C-1 | ⚠️ 轻微 | Combat/HitBox.cs |
OnTriggerEnter2D 内 ServiceLocator.GetOrDefault<IClashService>() 未缓存 |
| C-2 | ℹ️ 待办 | Player/SpringSystem.cs |
空桩实现,核心治愈机制未完成 |
| C-3 | ℹ️ 观察 | Core/Save/LocalFileStorage.cs |
GetExistingSlots() 硬编码槽位上限为 3 |
| C-4 | ℹ️ 观察 | Core/Events/ServiceLocator.cs |
文件位于 Events/ 子目录但命名空间为 BaseGames.Core,位置与职责不符 |
C-1 详述:HitBox.OnTriggerEnter2D 服务查询未缓存
现状:
// Assets/Scripts/Combat/HitBox.cs — OnTriggerEnter2D
ServiceLocator.GetOrDefault<IClashService>()?.ResolveClash(this, rivalHitBox);
影响:单次调用代价仅为 Dictionary<Type,object>.TryGetValue(约 10–20 ns),正常战斗中不会产生可测量帧率影响。但在密集多敌人战斗(同帧多个 HitBox 触发拼刀)场景下,集中调用量可积累。
建议改法:
private IClashService _clashService;
private void Awake()
{
var col = GetComponent<Collider2D>();
if (!col.isTrigger) Debug.LogWarning(...);
_clashService = ServiceLocator.GetOrDefault<IClashService>();
}
C-2 详述:SpringSystem 空桩实现
现状:
// Assets/Scripts/Player/SpringSystem.cs
public class SpringSystem : MonoBehaviour { }
治愈弹簧系统(PlayerStats 中 CurrentSpringCharges、MaxSpringCharges、SpringKillPoints 字段已预留)是明确的框架设计规划功能,当前仅有骨架。
建议:在 SpringSystem.cs 中添加 // TODO: 注释说明预期接口契约,防止其他开发者误以为该组件已实现。
C-3 详述:存档槽位数硬编码
现状:
// Assets/Scripts/Core/Save/LocalFileStorage.cs
public IEnumerable<int> GetExistingSlots()
{
for (int i = 0; i < 3; i++) // ← 硬编码 3 槽
if (Exists(i)) yield return i;
}
影响:极低。如需扩展为 5 存档槽,需同步修改此处与 UI SaveSlotPanel。由于 ISaveStorage 接口的此方法已封装变化点,改动范围可控。
建议:将槽位数提取为 LocalFileStorage(int maxSlots = 3) 构造参数,或在 SaveConfig SO 中配置。
C-4 详述:ServiceLocator 文件位置不符命名空间
现状:
- 文件路径:
Assets/Scripts/Core/Events/ServiceLocator.cs - 命名空间:
namespace BaseGames.Core(非BaseGames.Core.Events)
ServiceLocator 是 Core 层的基础设施,与事件系统无关。放置于 Events/ 子目录会造成新开发者困惑(以为它属于事件模块)。
建议:移动到 Assets/Scripts/Core/ServiceLocator.cs(命名空间不变)。
八、已确认的优秀实践清单
以下模式在整个代码库中一致使用,代表本框架的核心设计规范:
| # | 模式 | 体现文件 |
|---|---|---|
| 1 | Subscribe().AddTo(_subs) RAII |
所有 OnEnable/OnDisable 组件 |
| 2 | [SerializeField] Inspector 注入,零 Find/FindWithTag |
全代码库 |
| 3 | ServiceLocator.GetOrDefault<IXxx>() 可选服务访问 |
所有服务消费方 |
| 4 | Debug.Assert() Awake 配置验证 |
PlayerStats, EquipmentManager 等 |
| 5 | DamageInfo.Builder 零 GC 伤害构造 |
所有 HitBox 创建伤害处 |
| 6 | 8 步 HurtBox 伤害流水线 | HurtBox.ReceiveDamage() |
| 7 | 非 MonoBehaviour 状态对象 | PlayerStateBase 子类 |
| 8 | #if UNITY_EDITOR Gizmos + 只读属性 |
HurtBox, HitBox |
| 9 | GameStateMachine.ValidNextStates 合法转换卫士 |
GameStateMachine |
| 10 | SaveMigrator fall-through 链式迁移 |
SaveMigrator.Migrate() |
| 11 | LocalFileStorage 原子写+.bak 恢复 |
LocalFileStorage.WriteAsync() |
| 12 | ICharmEffect 策略模式护符效果 |
CharmSO.effects[] |
| 13 | JsonExtensionData 未知字段保留 |
SaveData |
| 14 | EnsureCollections 按需分配 LinkedList |
GlobalObjectPool |
| 15 | RegisterIfAbsent<T> 防重注册 + NullObject 服务 |
GameServiceRegistrar |
九、综合结论
经过 v1 至 v3 三轮系统性修复(共 18 个问题),BaseGames 框架已达到商业级独立游戏框架的成熟度。
最突出的架构成就:
-
服务定位器 100% 接口键:无任何具体类型泄漏,依赖倒置执行彻底。任何服务实现均可在不改调用方的前提下替换(A/B 测试、平台适配、Mock 测试均轻松支持)。
-
双轨事件系统,规则清晰:SO 事件频道处理跨场景/跨程序集广播,C# native
event处理同 Prefab 内部通信,规则文档化且全代码库一致执行。 -
存档系统工业级健壮:原子写防断电损坏、HMAC-SHA256 防篡改、
JsonExtensionData前向兼容、链式迁移器、全接口分层——任何一项单独拿出来都是商业项目的标准实现。 -
Editor 工具链完整:Event Bus Monitor、Scene Scaffold、Boss 序列编辑器、Addressables 验证器构成了完整的开发效率工具链,是框架成熟度的直接体现。
遗留的 4 个小问题(全部为轻微/观察级):
- C-1:HitBox 中 IClashService 建议缓存(30 行改动)
- C-2:SpringSystem 待实现(待办,非 Bug)
- C-3:存档槽位数可配置化(低优先级)
- C-4:ServiceLocator 文件位置建议整理(不影响功能)
最终定性:该框架设计理念清晰、执行纪律严格、扩展接口完善,可作为同类 2D Action RPG 项目的参考级架构蓝本。
文档生成日期:2026 年 5 月 | 审阅人:GitHub Copilot | 代码状态:零编译错误(get_errors() 已验证)