首先是GameFramework的Editor程序集找不到GameFramework命名空间,需要在它的asmdef文件添加对GameFramework和UnityGameFramework.Runtime程序集的引用。
UnityGameFramework.Runtime程序集也要引用GameFramework程序集。
然后UnityWebRequest在2018没有PostWWWForm函数,先改成Post。然后发现没有自动重新编译。发现PackageManager里没有Visual Studio的插件,Unity Registry里也没有。
查资料知道Visual Studio的插件在2019才出现。2018 不靠 Registry 装 VS 支持,是外部 IDE 模式 + VSTU(Visual Studio Tools for Unity)。
让我在Unity Hub安装Microsoft Visual Studio Community 2017。我有Visual Studio2022,不想装2017,于是换Unity2022。
最根的非Behavior类:GameFrameworkModule
是不继承MonoBehavior的管理器的基类。很多XXManager都是继承它和一个同名接口。
有抽象方法Update,由所有模块的管理器GameFrameworkEntry调用。
最根的Behavior类:GameFrameworkComponent
我们发现GameFrameworkComponent继承MonoBehavior,一大堆XXComponent继承它。那么很明显GameFrameworkComponent就是MonoBehavior型管理器的基类。
XXComponent-XXManager架构
关于GameFramework:UI
我追踪一个UI面板的关闭方法,调用了UIComponent.CloseUIForm,里面调用了UIManager.CloseUIForm,UIComponent持有IUIManager成员。点进它的方法,进入的是接口的声明,定义要去同名实现里。
IUIManager接口,只有一个实现,UIManager,由5个脚本写成。里面使用异步加载,有正在加载的UI集合,有加载完就要销毁的UI集合。

有一个UIGroup,它下面有IUIForm。
我想看释放UI资源的具体实现,一直找到这里。这里让类型参数必须是一个接口。但是后面是用Activator.CreateInstance反射创建对象。接口怎么创建对象?
注意这里获得moduleName,从第一个字符(也就是从1数第2个)截取,其实是把接口的I去掉得到类型名。
public static T GetModule<T>() where T : class
{
Type interfaceType = typeof(T);
if (!interfaceType.IsInterface)
{
throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName));
}
if (!interfaceType.FullName.StartsWith("GameFramework.", StringComparison.Ordinal))
{
throw new GameFrameworkException(Utility.Text.Format("You must get a Game Framework module, but '{0}' is not.", interfaceType.FullName));
}
string moduleName = Utility.Text.Format("{0}.{1}", interfaceType.Namespace, interfaceType.Name.Substring(1));
Type moduleType = Type.GetType(moduleName);
if (moduleType == null)
{
throw new GameFrameworkException(Utility.Text.Format("Can not find Game Framework module type '{0}'.", moduleName));
}
return GetModule(moduleType) as T;
}
这个框架有大量的接口,唯一的实现就是去掉I的同名类。包括UIManager、ObjectPoolManager都继承GameFrameworkModule和同名接口。接口定义必须实现的函数、属性。GameFrameworkModule方便上层统一调用。
ObjectPoolManager是生成各种对象池的管理器。
UIManager销毁UI是把它放进一个回收队列,在Update里处理。Update里调用了ObjectPool.Unspawn(这里写的是接口IObjectPool,要去实现类里找方法实现)。里面调用了GF封装的Object.Unspawn。
对象的封装
被管理的对象都继承ObjectBase,而ObjectPool缓存的是Object<T>,Object<T>里面有具体被管理对象。
资源的编辑器异步加载:EditorResourceComponent
我想到,我们想看资源加载的调用链,直接用VS代码搜索相关的API如AssetDatabase.LoadAssetAtPath,加打印,那么它就会把调用栈全打印出来。而不用从上层跳转十八层去找底层API。于是找到了EditorResourceComponent。但是打印并没有很长的调用链。
在EditorResourceComponent里看到,它是在Update里取出链表,加载,加载后执行加载成功回调。那么请求资源方就是往链表里加节点,并且把使用的函数加入回调。
往链表里加节点是LoadAsset做的,查找它的引用,发现它被7个重载引用。

真正被框架调用的只有最后一个重载。调用它的有UI、实体、Item、声音、数据模块。

然后我们看UI模块对资源模块的调用,在UIManager.OpenUIForm,同样一个干活的函数被7个重载调用,还被UIComponent调用。
所以加载一个UI的调用链是:
UIComponent.OpenUIForm>UIManager.OpenUIForm>EditorResourceComponent.LoadAsset放入链表,Update里处理。
那么就在LoadAsset里打印,能看见完整调用栈

拿到加载资源的完整调用栈了,接下来怎么读?从上往下还是从下往上?
资源的编辑器异步加载:ResourceManager
IResourceManager接口有两个实现,一个是编辑器资源加载,另一个是ResourceManager。ResourceManager由27个partial脚本组成。

在DefaultResourceHelper找到了使用UnityWebRequest异步加载的代码。
框架Update调用链
BaseComponent.Update>GameFrameworkEntry.Update遍历GameFrameworkModule链表,调用Update>
其他Component未实现Update函数。GameEntry也不参与。
把GameFrameworkModule加入GameFrameworkEntry:
XComponent持有XManager,在Awake里为自己持有的管理器注册加入GameFrameworkEntry的链表。

状态机系统
Procedure系统
在检查器显示了一堆流程,还有一个入口流程选项。这些应该是CustomEditor写的,于是代码搜索CustomEditor。找到了在\Assets\GameFramework\Scripts\Editor\Inspector

这些流程的名字是获取ProcedureBase的所有子类得到的。有多少子类这里就显示多少名字。

这个检查器不会显示默认显示的字段,因为它继承GameFrameworkInspector,这个检查器就没有执行绘制默认检查器。所以它完全按OnInspectorGUI里的代码绘制。

这里用SerializedProperty包裹流程列表和入口流程。

初始时通过名字找到要包裹的属性。

SerializedProperty有一系列API得到它包裹的任何类型的值,包括字符串值、数组长度、数组任意元素。
private void ReadAvailableProcedureTypeNames()
{
m_CurrentAvailableProcedureTypeNames = new List<string>();
int count = m_AvailableProcedureTypeNames.arraySize;
for (int i = 0; i < count; i++)
{
m_CurrentAvailableProcedureTypeNames.Add(m_AvailableProcedureTypeNames.GetArrayElementAtIndex(i).stringValue);
}
}
配表加载流程
配表都有一个基类,程序获得配表基类的所有子类,得到要加载的配表列表。


资源路径
注意到预制体路径不是放在那一类物品的配表,而是配表里有一个资源Id,另有一个所有资源的Id-路径配表。
事件系统
事件类的继承关系,从子类:
GameEventArgs>BaseEventArgs>GameFrameworkEventArgs>EventArgs。
EventArgs是C# System的类。
再梳理关键类:GameFrameworkModule、GameFrameworkComponent、ObjectBase、Object<T>、ObjectPool<T>、ObjectPoolBase、GameFrameworkEntry、GameEntry
那么到此我们知道这个框架:
所有非Behavior管理器都叫XManager,继承GameFrameworkModule和同名接口;
所有Behavior管理器都叫XComponent,继承GameFrameworkComponent;
XComponent持有XManager;
XManager通过自定义的SetYManager持有对其他管理器的引用,一个管理器可以有多个方法和引用。

GameFrameworkEntry缓存和管理所有GameFrameworkModule,调用它们的Update;
GameEntry缓存和管理所有GameFrameworkComponent;
这就是所谓的专业框架,随便一个调用,调用链有18层函数,一个函数,五六个参数,不同参数组合要写出七八种重载。
我总结出一点读重载的技巧。一堆重载里面有一个是真正干活的,其他的只是调用那个。真正干活的就是参数最多的那个。

这个作者似乎很喜欢用重载,喜欢把一堆参数,有的没有的排列组合。似乎认为这是成熟代码的标志?
不能把参数打包成一个结构体,不想传的参数就不赋值?
启示
这个框架的异步加载启示我们,异步加载本质是往一个集合里添加加载请求,管理器在Update里处理加载。还可以控制Update里的工作量,进一步避免卡顿。
本文深入解析GameFramework框架核心架构,涵盖GameFrameworkModule与GameFrameworkComponent双基类体系、XXComponent-XXManager分层设计、UI资源异步加载流程(EditorResourceComponent/ResourceManager)、状态机与Procedure系统、配表加载机制、事件系统及对象池(ObjectPool<T>)实现。同时说明Unity2018兼容问题:asmdef引用配置、UnityWebRequest API变更及VS工具链适配方案。


被折叠的 条评论
为什么被折叠?



