在上一节中,我们成功实现了通过 UIManager
获取每一个页面的 ViewBase
派生类实例,彻底告别了 GetComponent、拖引用、硬查找这些低效方式。我们的框架第一次拥有了一个真正意义上的“统一面板入口”。
但随着使用深入,一个令人不安的问题浮出水面:
我们的控件(ButtonCustom、ToggleCustom)是在各自的
Init()
方法中通过transform.parent
向上查找父级的ViewBase
,那如果控件的Init()
比ViewBase
先执行了,会发生什么?
答案是——会直接找不到 ViewBase,事件无法转发,控件注册失败。
这不是小问题,这是灾难级的初始化顺序隐患。于是本节我们要解决的就是它:
如何确保初始化顺序永远是:ViewBase → PanelBase → EventUIBase?
✅ 为什么 Init() 的顺序如此重要?
我们前面做了这么多“自动化”的工作:让控件自动注册、让 View 自动绑定 Panel、让框架自动反射 Init()……所有这些自动,其实都是建立在“谁先初始化”这个前提上的。
如果初始化顺序不正确,比如控件先于 View 执行了 Init,那就意味着:
-
无法找到目标父级 ViewBase
-
无法注册事件转发
-
无法通过 View 查询控件
-
所有事件和数据都将中断
所以,我们需要的不只是“自动初始化”,而是**“自动且有序”**的初始化流程。
✅ 解决方案:按类型定义初始化优先级
我们给所有 Base
的子类打上“初始化优先级”,越小越先执行:
类型层级 | 类名 | 优先级 |
---|---|---|
视图层 | ViewBase | 0(最早) |
逻辑层 | PanelBase | 1 |
控件层 | EventUIBase | 2(最晚) |
实现方式其实很朴素,就是在 UIManager
中维护一张优先级表:
private static readonly Dictionary<Type, int> InitOrder = new()
{
{ typeof(ViewBase), 0 },
{ typeof(PanelBase), 1 },
{ typeof(EventUIBase), 2 }
};
然后我们在执行反射 Init()
的时候,统一按照这个顺序来排序执行。
✅ 完整升级版 UIManager.Init()
以下是改造之后的 Init()
方法:
private static void Init()
{
Base[] allBases = Resources.FindObjectsOfTypeAll<Base>();
var ordered = allBases.OrderBy(b => GetPriority(b.GetType()));
foreach (var item in ordered)
{
if (!IsValid(item)) continue;
var initMethod = item.GetType().GetMethod("Init", BindingFlags.Instance | BindingFlags.NonPublic);
initMethod?.Invoke(item, null);
}
}
private static int GetPriority(Type t)
{
return InitOrder.TryGetValue(t, out var priority) ? priority : 3;
}
private static bool IsValid(Base b)
{
return b != null &&
b.gameObject != null &&
b.gameObject.scene.IsValid() &&
!b.hideFlags.HasFlag(HideFlags.NotEditable) &&
!b.hideFlags.HasFlag(HideFlags.HideAndDontSave);
}
我们做了三件事:
-
用
InitOrder
表定义优先级; -
用
OrderBy()
按优先级排序所有 Base 派生类; -
统一执行它们的 Init 方法,确保从 View → Logic → UI 控件。
从此以后,哪怕你在场景中放置顺序错乱、激活状态不同,甚至 prefab 嵌套得再复杂,也不会再出现初始化找不到引用的问题了。
✅ 总结
-
自动化不是全部,顺序才是稳定性的关键;
-
引入 InitOrder,确保初始化流程可靠、可控;
-
框架启动再复杂,也能保证每个引用都正确可用。
🔜 下一节预告
现在,我们已经能手动创建 View 和 Panel,并稳定完成注册和调用。但很快你就会觉得——
“每次都复制粘贴模板代码、改类名、查拼写,好烦啊……”
是的,框架再好用,流程再优雅,如果创建脚本还得手写,那开发者的尊严就无处安放。
下一节,我们就来编写一个编辑器工具:一键生成符合框架规范的 View 与 Panel 脚本,让你输入类名和作者名,其他交给代码来完成!
真正做到:
🧠 脑子想好 → ⌨️ 输入类名 → ✅ 自动生成 → 🧱 开始开发
敬请期待——第九节:脚本模板自动生成器。
阅读指引:
UI框架从0到1第一节:【事件转发起步】让按钮自己“走上来”找逻辑处理-CSDN博客
UI框架从0到1第二节:【全控件适配】把 Toggle、InputField 全都拉进事件系统-CSDN博客
UI框架从0到1第三节:【一键挂载】用编辑器扩展解放双手,告别手动拖脚本-CSDN博客
UI框架从0到1第四节:【页面逻辑拆分】让每一个面板都拥有自己的灵魂-CSDN博客
UI框架从0到1第五节:【事件消息体】不仅知道你点了啥,还知道你改了啥-CSDN博客
UI框架从0到1第六节:【轻量 MVVM】用属性驱动 UI,彻底抛弃命令式调用-CSDN博客
UI框架从0到1第七节:【全局访问 View】UIManager 登场,掌控所有界面入口-CSDN博客
UI框架从0到1第八节:【初始化优先级】三大层级定序,杜绝找不到父级的尴尬-CSDN博客