流程模块是 TEngine 框架中的商业化启动流程管理系统,提供了完整的游戏启动、资源更新、热更新等流程控制。流程模块采用状态机模式,确保游戏启动流程的稳定性和可维护性。
-
📋 完整的商业化流程
- 覆盖从启动到进入游戏的完整流程
- 支持资源更新、热更新等商业级功能
- 可自定义流程节点
-
🔄 状态机模式
- 清晰的流程状态管理
- 易于扩展和维护
- 支持流程跳转和回退
-
🚀 异步流程支持
- 基于 UniTask 的异步流程
- 不阻塞主线程
- 支持流程进度显示
TEngine 提供了以下标准流程节点:
游戏启动入口,初始化基础系统。
显示游戏启动画面(Splash Screen)。
初始化资源包系统(YooAsset)。
预加载必要的资源(如配置表、基础 UI 等)。
初始化资源模块。
检查并更新资源包版本。
更新资源清单文件。
创建资源下载器,用于下载更新资源。
下载需要更新的资源文件。
资源下载完成处理。
清理不需要的缓存文件。
加载热更新程序集(HybridCLR)。
进入游戏主流程。
标准流程执行顺序(根据实际代码中的 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(),
};// 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);每个流程节点应该只负责一个明确的任务:
// ✅ 推荐:职责单一
public class ProcedureUpdateVersion : ProcedureBase
{
// 只负责版本更新
}
// ❌ 不推荐:职责过多
public class ProcedureUpdateEverything : ProcedureBase
{
// 同时负责版本更新、资源更新、配置加载等
}对于耗时操作,使用 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());对于需要时间的流程,提供进度反馈:
// 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);
}