整体架构

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 的生命周期方法启动。

  1. GF 中每个模块都有一个对应的 Component(继承自 Monobehaviour),并挂在各自对应的 GameObject 上,放在启动场景中,当运行游戏时,Unity 便会调用各个 Component 的 Awake 方法。
  2. 在 GameFrameworkComponent(UGF 中所有 Component 的基类)的 Awake 方法中,会调用 GameEntry.RegisterComponent 把 Component 注册到 GameEntry 中,以方便在其他地方通过 GameEntry 来访问任意 Component。
  3. 因为 Component 是继承自 Monobehaviour 并被直接挂在场景中的 GameObject 上,引擎会执行 Component 的实例化,而 GF 层中的各个模块(Module)是普通 C# 类,需要我们自己去实例化,GF 便是在 Component 的 Awake 中去实例化各个模块类。(Resource 模块比较特殊,在 Start 实例化)。
  4. 各个 Component 的 Start 方法被调用,考虑到模块可能需要依赖其他模块,所以在 Awake 过后,各个 Component 注册到 GameEntry 中后(这样才能获取到其他模块的引用),再在 Start 阶段初始化各个模块。
  5. 根据 Inspector 面板配置和 Unity API 的参数,初始化各个模块,并实例化 Component 所需要的 GameObject。
  6. 实例化模块所需要的 Helper,并以接口形式传递给对应模块。
  7. Procedure 是框架中管理整个游戏流程的模块,Procedure 的 Component 中的 Start 方法以协程方式启动,协程等待了一帧,这一帧猜测是等待步骤 5 中实例化出来的 GameObject 对象完成初始化,避免游戏启动逻辑与这些 GameObject 的初始化有依赖时产生时序问题。
  8. 游戏启动第二帧,Procedure 模块启动我们指定的入口流程,在此例中,入口为 ProcedureLaunch。
  9. 执行 ProcedureLaunch 流程的 OnEnter 方法,游戏的第一句具体逻辑,在这正式被调用。

GameFrameworkModule 的实例化

GameFrameworkModule(GF 层中各个模块的基类)采用惰性实例化,UGF 的 Component 通过调用 GameFrameworkModule.GetModule 来获取模块对象,在 GetModule 时,GameFrameworkEntry 会先检测内部有没有这个模块对象,没有时再调用内部的 CreateModule 来实例化该模块。

可以看到 GetModule 是传入模块的接口的,而不是模块类本身,GetModule 内部会通过反射获得传入接口名字的字符串,并裁掉第一个字符,如传入 IXXXManager,会取得 “XXXManager” 字符串,这个字符串对应了模块类名,然后通过反射来创建该模块实例。

Tick

因为各个 Monobehaviour 之间不能很好地控制顺序问题,GF 通过指定的一个 Monobehaviour 中的 Update 方法去驱动所有模块的 Update,各自模块的 Update 顺序由各自模块定义的优先级决定,这样只要在框架管理下的模块,都能做到执行顺序可控。

  1. 引擎调用所有 Monobehaviour 的 Update 方法
  2. BaseComponent 中的 Update 方法被调用
  3. BaseComponent 中的 Update 方法调用 GameFrameworkEntry 的 Update 方法
  4. GameFrameworkEntry 内部以链表的方式,把已经创建好的模块对象按优先级顺序(降序)组织好,GameFrameworkEntry 的 Update 中会以链表顺序遍历各个模块,并调用 Update 方法

Shutdown

  1. 业务层调用 GameEntry 的 Shutdown 方法(会传入枚举值 ShutdownType)
  2. GameEntry 的 Shutdown 方法调用 BaseComponent 的 Shutdown 方法
  3. BaseComponent 是个 Monobehaviour 类,在 Shutdown 中调用 Monobehaviour 的 Destroy 销毁自身
  4. 根据最初传入的 ShutdownType 来执行 LoadScene(重新启动游戏)或 Quit(退出游戏)。
  5. BaseComponent 的 OnDestroy 被调用
  6. BaseComponent 的 OnDestroy 里调用 GameFrameworkEntry 的 Shutdown 方法
  7. 与 Update 相反,GameFrameworkEntry 的 Shutdown 中会以链表逆序遍历各个模块,并调用 Shutdown 方法

最后

GameFramework 解析 系列目录:GameFramework 解析:开篇

个人原创,未经授权,谢绝转载!