整体架构
GF 层
- 包含框架中各个模块的具体实现,不依赖于引擎
- 各个模块若有需要引擎传递的参数,可通过 UGF 层的 Component 在初始化时传入
- 若需要调用依赖于引擎实现的接口,可定义对应的 IHelper 接口,并在 UGF 层实现接口,GF 层只调用,不关心具体实现
UGF 层
- 实现框架中需要依赖 Unity 的逻辑,把框架与引擎解耦
- 作为 Game 层与 GF 层之间的桥梁
- 实例化并初始化框架各个模块
- 借助 Unity 的 Editor 扩展,实现各个模块参数的可视化配置
上图UGF与GF的关系其实简化了,实际上每个模块的XXXManager都会实现了对应的IXXXManager接口,UGF只会直接引用接口,不会直接引用Manager
Game 层
游戏逻辑,只与 UGF 层直接接触
启动流程
Unity 要启动游戏,至少要有一个自定义的 Monobehaviour 脚本挂在场景中的 GameObject 上,来作为我们游戏的逻辑入口,那么任何框架要在 Unity 中启动也不例外,需要依赖 Monobehaviour 的生命周期方法启动。
- GF 中每个模块都有一个对应的 Component(继承自 Monobehaviour),并挂在各自对应的 GameObject 上,放在启动场景中,当运行游戏时,Unity 便会调用各个 Component 的 Awake 方法。
- 在 GameFrameworkComponent(UGF 中所有 Component 的基类)的 Awake 方法中,会调用 GameEntry.RegisterComponent 把 Component 注册到 GameEntry 中,以方便在其他地方通过 GameEntry 来访问任意 Component。
- 因为 Component 是继承自 Monobehaviour 并被直接挂在场景中的 GameObject 上,引擎会执行 Component 的实例化,而 GF 层中的各个模块(Module)是普通 C# 类,需要我们自己去实例化,GF 便是在 Component 的 Awake 中去实例化各个模块类。(Resource 模块比较特殊,在 Start 实例化)。
- 各个 Component 的 Start 方法被调用,考虑到模块可能需要依赖其他模块,所以在 Awake 过后,各个 Component 注册到 GameEntry 中后(这样才能获取到其他模块的引用),再在 Start 阶段初始化各个模块。
- 根据 Inspector 面板配置和 Unity API 的参数,初始化各个模块,并实例化 Component 所需要的 GameObject。
- 实例化模块所需要的 Helper,并以接口形式传递给对应模块。
- Procedure 是框架中管理整个游戏流程的模块,Procedure 的 Component 中的 Start 方法以协程方式启动,协程等待了一帧,这一帧猜测是等待步骤 5 中实例化出来的 GameObject 对象完成初始化,避免游戏启动逻辑与这些 GameObject 的初始化有依赖时产生时序问题。
- 游戏启动第二帧,Procedure 模块启动我们指定的入口流程,在此例中,入口为 ProcedureLaunch。
- 执行 ProcedureLaunch 流程的 OnEnter 方法,游戏的第一句具体逻辑,在这正式被调用。
GameFrameworkModule 的实例化
GameFrameworkModule(GF 层中各个模块的基类)采用惰性实例化,UGF 的 Component 通过调用 GameFrameworkModule.GetModule 来获取模块对象,在 GetModule 时,GameFrameworkEntry 会先检测内部有没有这个模块对象,没有时再调用内部的 CreateModule 来实例化该模块。
可以看到 GetModule 是传入模块的接口的,而不是模块类本身,GetModule 内部会通过反射获得传入接口名字的字符串,并裁掉第一个字符,如传入 IXXXManager,会取得 “XXXManager” 字符串,这个字符串对应了模块类名,然后通过反射来创建该模块实例。
Tick
因为各个 Monobehaviour 之间不能很好地控制顺序问题,GF 通过指定的一个 Monobehaviour 中的 Update 方法去驱动所有模块的 Update,各自模块的 Update 顺序由各自模块定义的优先级决定,这样只要在框架管理下的模块,都能做到执行顺序可控。
- 引擎调用所有 Monobehaviour 的 Update 方法
- BaseComponent 中的 Update 方法被调用
- BaseComponent 中的 Update 方法调用 GameFrameworkEntry 的 Update 方法
- GameFrameworkEntry 内部以链表的方式,把已经创建好的模块对象按优先级顺序(降序)组织好,GameFrameworkEntry 的 Update 中会以链表顺序遍历各个模块,并调用 Update 方法
Shutdown
- 业务层调用 GameEntry 的 Shutdown 方法(会传入枚举值 ShutdownType)
- GameEntry 的 Shutdown 方法调用 BaseComponent 的 Shutdown 方法
- BaseComponent 是个 Monobehaviour 类,在 Shutdown 中调用 Monobehaviour 的 Destroy 销毁自身
- 根据最初传入的 ShutdownType 来执行 LoadScene(重新启动游戏)或 Quit(退出游戏)。
- BaseComponent 的 OnDestroy 被调用
- BaseComponent 的 OnDestroy 里调用 GameFrameworkEntry 的 Shutdown 方法
- 与 Update 相反,GameFrameworkEntry 的 Shutdown 中会以链表逆序遍历各个模块,并调用 Shutdown 方法
最后
GameFramework 解析 系列目录:GameFramework 解析:开篇
个人原创,未经授权,谢绝转载!