3-7. 流程模块 - ProcedureModule

April 23, 2026 · View on GitHub

🔄 模块概述

流程模块是 TEngine 框架中的商业化启动流程管理系统,提供了完整的游戏启动、资源更新、热更新等流程控制。流程模块采用状态机模式,确保游戏启动流程的稳定性和可维护性。

✨ 核心优势

  1. 📋 完整的商业化流程

    • 覆盖从启动到进入游戏的完整流程
    • 支持资源更新、热更新等商业级功能
    • 可自定义流程节点
  2. 🔄 状态机模式

    • 清晰的流程状态管理
    • 易于扩展和维护
    • 支持流程跳转和回退
  3. 🚀 异步流程支持

    • 基于 UniTask 的异步流程
    • 不阻塞主线程
    • 支持流程进度显示

📖 流程节点说明

TEngine 提供了以下标准流程节点:

1. ProcedureLaunch - 流程启动

游戏启动入口,初始化基础系统。

2. ProcedureSplash - 流程闪屏

显示游戏启动画面(Splash Screen)。

3. ProcedureInitPackage - 流程初始化 Package

初始化资源包系统(YooAsset)。

4. ProcedurePreload - 流程预加载

预加载必要的资源(如配置表、基础 UI 等)。

5. ProcedureInitResources - 流程初始化 Resources

初始化资源模块。

6. ProcedureUpdateVersion - 流程更新版本 Version

检查并更新资源包版本。

7. ProcedureUpdateManifest - 流程更新 Manifest 清单

更新资源清单文件。

8. ProcedureCreateDownloader - 流程创建下载器

创建资源下载器,用于下载更新资源。

9. ProcedureDownloadFile - 流程下载文件

下载需要更新的资源文件。

10. ProcedureDownloadOver - 流程下载文件结束

资源下载完成处理。

11. ProcedureClearCache - 流程清理缓存

清理不需要的缓存文件。

12. ProcedureLoadAssembly - 流程加载进入热更新程序集

加载热更新程序集(HybridCLR)。

13. ProcedureStartGame - 流程开始游戏

进入游戏主流程。


🔄 流程执行顺序

标准流程执行顺序(根据实际代码中的 ChangeState<T>() 调用链):

ProcedureLaunch

ProcedureSplash

ProcedureInitPackage

ProcedureInitResources
  ↓                          ← 根据运行模式分支:
  ├─ EditorSimulateMode / OfflinePlayMode → ProcedurePreload(直通)
  └─ HostPlayMode → ProcedureCreateDownloader → ProcedureDownloadFile → ProcedureDownloadOver → ProcedureClearCache → ProcedurePreload

ProcedurePreload

ProcedureLoadAssembly

ProcedureStartGame

注意:联机模式下,ProcedureInitResources 之后会进入版本更新和下载流程(ProcedureCreateDownloader → ProcedureDownloadFile → ProcedureDownloadOver → ProcedureClearCache),然后才进入 ProcedurePreload。编辑器模拟模式和单机模式则直接跳到 ProcedurePreload。


💡 自定义流程

创建自定义流程节点

using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;

/// <summary>
/// 自定义流程节点。
/// </summary>
public class ProcedureCustom : ProcedureBase
{
    public override bool UseNativeDialog => false;

    protected override void OnEnter(ProcedureOwner procedureOwner)
    {
        base.OnEnter(procedureOwner);
        
        // 流程进入逻辑
        Log.Info("进入自定义流程");
        
        // 执行异步操作,使用 UniTask 并 Forget 启动
        DoSomethingAsync(procedureOwner).Forget();
    }
    
    protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds)
    {
        base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
        
        // 流程更新逻辑
    }
    
    protected override void OnLeave(ProcedureOwner procedureOwner, bool isShutdown)
    {
        base.OnLeave(procedureOwner, isShutdown);
        
        // 流程离开逻辑
        Log.Info("离开自定义流程");
    }
    
    private async UniTaskVoid DoSomethingAsync(ProcedureOwner procedureOwner)
    {
        // 异步操作(禁止使用 Coroutine,必须使用 UniTask)
        await UniTask.Delay(1000);
        
        // 切换到下一个流程
        ChangeState<ProcedureNext>(procedureOwner);
    }
}

注册自定义流程

在流程模块初始化时注册自定义流程:

// 在 ProcedureModule 中注册
ProcedureBase[] procedures = {
    new ProcedureLaunch(),
    new ProcedureSplash(),
    // ... 其他流程
    new ProcedureCustom(),  // 自定义流程
    new ProcedureStartGame(),
};

🔧 API 参考

流程切换

// ProcedureOwner 别名:using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;

/// <summary>
/// 切换到指定流程。
/// </summary>
/// <typeparam name="T">流程类型。</typeparam>
/// <param name="procedureOwner">流程状态机持有者。</param>
void ChangeState<T>(ProcedureOwner procedureOwner) where T : ProcedureBase;

流程生命周期

// ProcedureOwner 别名:using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;

/// <summary>
/// 流程进入时调用。
/// </summary>
/// <param name="procedureOwner">流程状态机持有者。</param>
protected override void OnEnter(ProcedureOwner procedureOwner);

/// <summary>
/// 流程更新时调用。
/// </summary>
/// <param name="procedureOwner">流程状态机持有者。</param>
/// <param name="elapseSeconds">逻辑流逝时间。</param>
/// <param name="realElapseSeconds">真实流逝时间。</param>
protected override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds);

/// <summary>
/// 流程离开时调用。
/// </summary>
/// <param name="procedureOwner">流程状态机持有者。</param>
/// <param name="isShutdown">是否是关闭状态机时触发。</param>
protected override void OnLeave(ProcedureOwner procedureOwner, bool isShutdown);

💡 最佳实践

1. 流程节点职责单一

每个流程节点应该只负责一个明确的任务:

// ✅ 推荐:职责单一
public class ProcedureUpdateVersion : ProcedureBase
{
    // 只负责版本更新
}

// ❌ 不推荐:职责过多
public class ProcedureUpdateEverything : ProcedureBase
{
    // 同时负责版本更新、资源更新、配置加载等
}

2. 使用异步操作

对于耗时操作,使用 UniTask 异步方式(禁止使用 async void 或 Coroutine):

// ✅ 推荐:在 OnEnter 中启动 UniTaskVoid 并 Forget
// using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
    base.OnEnter(procedureOwner);
    UpdateVersionAsync(procedureOwner).Forget();
}

private async UniTaskVoid UpdateVersionAsync(ProcedureOwner procedureOwner)
{
    await DoUpdateAsync();
    ChangeState<ProcedureNext>(procedureOwner);
}

// ❌ 禁止:async void 签名、Coroutine
// public override async void OnEnter(...) { ... }
// StartCoroutine(UpdateVersion());

3. 提供流程进度反馈

对于需要时间的流程,提供进度反馈:

// using ProcedureOwner = TEngine.IFsm<TEngine.IProcedureModule>;
protected override void OnEnter(ProcedureOwner procedureOwner)
{
    base.OnEnter(procedureOwner);
    DownloadWithProgressAsync(procedureOwner).Forget();
}

private async UniTaskVoid DownloadWithProgressAsync(ProcedureOwner procedureOwner)
{
    var downloader = GameModule.Resource.CreateResourceDownloader();
    
    // 显示进度
    while (!downloader.IsDone)
    {
        float progress = downloader.Progress;
        UpdateProgressUI(progress);
        await UniTask.Yield();
    }
    
    ChangeState<ProcedureNext>(procedureOwner);
}

🔗 相关链接