GameFramework生命周期

最近又在写一些游戏项目,想要看一些有什么方便开发的框架,来来回回发现 GameFramework 依然是绕不过去的一个框架。本章主要是分析一下 GameFramework 的生命周期。

Unity 生命周期

先看一下常见的 Unity 的生命周期

Awake
Start
Update
OnDestroy
其他MonoBehaviour
独立生命周期

主要有几个问题:

  • 各个模块之间的生命周期是独立的,执行顺序不确定
  • 难以控制正确的初始化和销毁顺序
  • 无法更好的处理依赖关系

GameFramework 生命周期

GameFrameworkModule

GameFramework 通过 GameFrameworkModule 来管理模块的生命周期。GameFrameworkModule 是一个抽象类,主要有三个方法:

GameFrameworkEntry.GetModule
自动创建模块
按优先级排序
统一Update循环
逆序Shutdown
高优先级模块
先执行Update
后执行Shutdown

代码是这样的:

/// <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();
}
  1. 通过 Priority 来控制模块的执行顺序
  2. 通过 Update 来控制模块的更新
  3. 通过 Shutdown 来控制模块的销毁

GameFrameworkEntry

看了 GameFrameworkModule 的实现,接下来看看 GameFrameworkEntry 的实现。GameFrameworkEntry 中是如何管理项目中所有的模块的。

1. CreateModule
  1. 使用 System.Activator.CreateInstance 来创建模块
  2. 通过 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
  1. 强制使用接口来获取模块,依赖倒置原则:
    • 高层模块不应该依赖低层模块,二者都应该依赖抽象
    • 通过接口来获取模块,避免了直接依赖实现类
  2. 通过命名约定来获取模块
    • 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;
    }
}

工厂模式:

TypeA
TypeB
TypeC
Client 客户端
Factory 工厂类
产品类型判断
ProductA 产品 A
ProductB 产品 B
ProductC 产品 C
IProduct产品接口
返回给客户端

单例模式:

已存在
不存在
Client 客户端
Singleton 单例类
判断是否已存在
返回实例
创建新实例
返回实例
LinkedList

使用了 LinkedList 来管理模块的顺序,LinkedList 是一个双向链表,支持在 O(1) 的时间复杂度内插入和删除节点。适合频繁插入和删除的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值