最近又在写一些游戏项目,想要看一些有什么方便开发的框架,来来回回发现 GameFramework 依然是绕不过去的一个框架。本章主要是分析一下 GameFramework 的生命周期。
Unity 生命周期
先看一下常见的 Unity 的生命周期
主要有几个问题:
- 各个模块之间的生命周期是独立的,执行顺序不确定
- 难以控制正确的初始化和销毁顺序
- 无法更好的处理依赖关系
GameFramework 生命周期
GameFrameworkModule
GameFramework 通过 GameFrameworkModule 来管理模块的生命周期。GameFrameworkModule 是一个抽象类,主要有三个方法:
代码是这样的:
/// <summary>
/// 游戏框架模块抽象类。
/// </summary>
internal abstract class GameFrameworkModule
{
/// <summary>
/// 获取游戏框架模块优先级。
/// </summary>
/// <remarks>优先级较高的模块会优先轮询,并且关闭操作会后进行。</remarks>
internal virtual int Priority
{
get
{
return 0;
}
}
/// <summary>
/// 游戏框架模块轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
internal abstract void Update(float elapseSeconds, float realElapseSeconds);
/// <summary>
/// 关闭并清理游戏框架模块。
/// </summary>
internal abstract void Shutdown();
}
- 通过 Priority 来控制模块的执行顺序
- 通过 Update 来控制模块的更新
- 通过 Shutdown 来控制模块的销毁
GameFrameworkEntry
看了 GameFrameworkModule 的实现,接下来看看 GameFrameworkEntry 的实现。GameFrameworkEntry 中是如何管理项目中所有的模块的。
1. CreateModule
- 使用 System.Activator.CreateInstance 来创建模块
- 通过 LinkedList 来管理模块的顺序,保证在数据结构中模块是有序的
/// <summary>
/// 创建游戏框架模块。
/// </summary>
/// <param name="moduleType">要创建的游戏框架模块类型。</param>
/// <returns>要创建的游戏框架模块。</returns>
private static GameFrameworkModule CreateModule(Type moduleType)
{
GameFrameworkModule module = (GameFrameworkModule)Activator.CreateInstance(moduleType);
if (module == null)
{
throw new GameFrameworkException(Utility.Text.Format("Can not create module '{0}'.", moduleType.FullName));
}
// 根据优先级插入,高优先级在前
LinkedListNode<GameFrameworkModule> current = s_GameFrameworkModules.First;
while (current != null)
{
if (module.Priority > current.Value.Priority)
{
break;
}
current = current.Next;
}
if (current != null)
{
s_GameFrameworkModules.AddBefore(current, module);
}
else
{
s_GameFrameworkModules.AddLast(module);
}
return module;
}
2. GetModule
- 强制使用接口来获取模块,依赖倒置原则:
- 高层模块不应该依赖低层模块,二者都应该依赖抽象
- 通过接口来获取模块,避免了直接依赖实现类
- 通过命名约定来获取模块
- IXxxManager -> XxxManager
- 通过反射来获取模块的类型
/// <summary>
/// 获取游戏框架模块。
/// </summary>
/// <typeparam name="T">要获取的游戏框架模块类型。</typeparam>
/// <returns>要获取的游戏框架模块。</returns>
/// <remarks>如果要获取的游戏框架模块不存在,则自动创建该游戏框架模块。</remarks>
public static T GetModule<T>() where T : class
{
Type interfaceType = typeof(T);
// 1. 必须是接口类型
if (!interfaceType.IsInterface)
{
throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName));
}
// 2. 必须是游戏框架模块, 必须是 GameFramework 命名空间下的类型
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));
}
// 3. 命名约定:IXxxManager -> XxxManager
string moduleName = Utility.Text.Format("{0}.{1}",
interfaceType.Namespace,
interfaceType.Name.Substring(1)); // 去掉前缀 I
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;
}
/// <summary>
/// 获取游戏框架模块。
/// </summary>
/// <param name="moduleType">要获取的游戏框架模块类型。</param>
/// <returns>要获取的游戏框架模块。</returns>
/// <remarks>如果要获取的游戏框架模块不存在,则自动创建该游戏框架模块。</remarks>
private static GameFrameworkModule GetModule(Type moduleType)
{
foreach (GameFrameworkModule module in s_GameFrameworkModules)
{
if (module.GetType() == moduleType)
{
return module;
}
}
return CreateModule(moduleType);
}
3. Update/Shutdown
这部分就没什么特别需要注意的地方了,唯一需要注意的是在 Shutdown 的时候是逆序的(优先级低的模块可能用到了高优先级的模块)。
/// <summary>
/// 所有游戏框架模块轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
public static void Update(float elapseSeconds, float realElapseSeconds)
{
foreach (GameFrameworkModule module in s_GameFrameworkModules)
{
module.Update(elapseSeconds, realElapseSeconds);
}
}
/// <summary>
/// 关闭并清理所有游戏框架模块。
/// </summary>
public static void Shutdown()
{
for (LinkedListNode<GameFrameworkModule> current = s_GameFrameworkModules.Last; current != null; current = current.Previous)
{
current.Value.Shutdown();
}
s_GameFrameworkModules.Clear();
ReferencePool.ClearAll();
Utility.Marshal.FreeCachedHGlobal();
GameFrameworkLog.SetLogHelper(null);
}
其他
用到的设计模式
- 工厂模式、单例模式
// GameFrameworkEntry 充当了工厂和单例管理器
public static class GameFrameworkEntry
{
// 单例容器
private static readonly GameFrameworkLinkedList<GameFrameworkModule> s_GameFrameworkModules;
// 工厂方法
public static T GetModule<T>() where T : class
{
// 1. 检查是否已存在
GameFrameworkModule existingModule = GetModule(moduleType);
if (existingModule != null) return existingModule as T;
// 2. 不存在则创建新实例
return CreateModule(moduleType) as T;
}
}
工厂模式:
单例模式:
LinkedList
使用了 LinkedList 来管理模块的顺序,LinkedList 是一个双向链表,支持在 O(1) 的时间复杂度内插入和删除节点。适合频繁插入和删除的场景。