Unity MyFramework 用法说明(十七):使用 WaitingManager 统一等待异步任务与业务条件

上一篇介绍了 AsyncTaskGroup,它适合等待多个异步操作全部完成。

实际业务中,流程开始的条件不一定都是异步任务。例如进入战斗前,除了等待地图加载,还可能需要等待玩家创建完成、服务器场景数据到达。

MyFramework 提供了 WaitingManager,用于把这些等待条件统一封装起来。

项目地址:

github.com/ZHOURUIH/My...

一、WaitingManager 解决什么问题

假设进入战斗需要满足三个条件:

bash 复制代码
地图资源加载完成
玩家角色创建完成
服务器场景数据接收完成

直接写在 update() 中:

bash 复制代码
public override void update(float elapsedTime)
{
	base.update(elapsedTime);

	if (!mMapLoaded ||
		!mPlayerCreated ||
		!mServerDataReady)
	{
		return;
	}

	enterBattle();
}

还需要额外保证 enterBattle() 只执行一次,并在流程退出后停止检查。

使用 WaitingManager 后,可以直接描述:

bash 复制代码
等待什么
    ↓
全部满足
    ↓
执行什么

二、等待一个业务条件

最简单的用法是等待一个 BoolFunction 返回 true

bash 复制代码
using static FrameBaseHotFix;

protected bool mPlayerCreated;

protected void waitPlayerCreated()
{
	mWaitingManager.createWaiting(
		() => mPlayerCreated,
		onPlayerCreated);
}

protected void onPlayerCreated()
{
	log("玩家创建完成");
}

WaitingManager 每帧检查条件。

当条件第一次成立时,会执行完成回调,并自动销毁这个 Waiting

不需要自己在 update() 中持续判断。

三、同时等待多个条件

通过 createWaiting(Action) 创建一个空的等待对象,再逐项添加条件:

bash 复制代码
protected bool mPlayerCreated;
protected bool mServerDataReady;
protected bool mSceneInitialized;

protected void waitEnterBattle()
{
	Waiting waiting =
		mWaitingManager.createWaiting(onEnterBattle);

	waiting.addCondition(() => mPlayerCreated);
	waiting.addCondition(() => mServerDataReady);
	waiting.addCondition(() => mSceneInitialized);
}

protected void onEnterBattle()
{
	log("所有条件完成,进入战斗");
}

只有三个条件全部返回 true,才会调用 onEnterBattle()

完成回调最多执行一次,即使条件之后一直保持为 true,也不会重复触发。

四、同时等待异步操作和业务条件

Waiting 不仅能添加条件,也可以添加 CustomAsyncOperation

bash 复制代码
protected bool mPlayerCreated;
protected bool mServerDataReady;

protected void prepareBattle()
{
	CustomAsyncOperation mapOperation =
		startLoadMap();

	Waiting waiting =
		mWaitingManager.createWaiting(onPrepareFinished);

	waiting.addAsyncOperation(mapOperation);
	waiting.addCondition(() => mPlayerCreated);
	waiting.addCondition(() => mServerDataReady);
}

protected void onPrepareFinished()
{
	log("地图、玩家和服务器数据都已准备完成");
}

protected CustomAsyncOperation startLoadMap()
{
	// 启动地图加载并返回对应的异步操作
	return null;
}

判断规则是:

bash 复制代码
所有 CustomAsyncOperation 不再等待
并且
所有 BoolFunction 返回 true

两部分同时满足后,才会执行完成回调。

传入的异步操作和条件不能为 null,否则检查时会出现空引用。

五、快速等待一到三个异步操作

WaitingManager 还提供了常用重载:

bash 复制代码
mWaitingManager.createWaiting(
	operation,
	onFinished);

等待两个异步操作:

bash 复制代码
mWaitingManager.createWaiting(
	mapOperation,
	playerOperation,
	onFinished);

等待三个异步操作:

bash 复制代码
mWaitingManager.createWaiting(
	mapOperation,
	playerOperation,
	monsterOperation,
	onFinished);

如果只有异步操作,不需要额外业务条件,直接使用这些重载更简洁。

任务数量更多,或者还要混合普通条件时,再创建 Waiting 后逐项添加。

六、获取简单进度

Waiting 可以计算当前进度:

bash 复制代码
float progress = waiting.getProgress();

它会把每一个异步操作和每一个业务条件都视为一项任务。

例如共有四项,其中两项完成:

bash 复制代码
进度 = 2 / 4 = 0.5

可以用于显示简单的准备进度:

bash 复制代码
protected Waiting mWaiting;

public float getPrepareProgress()
{
	return mWaiting?.getProgress() ?? 1.0f;
}

需要注意,它只是按照完成数量计算,不会读取单个资源加载任务内部的真实百分比。

bash 复制代码
未完成    计为 0
已完成    计为 1

因此它适合显示整体步骤进度,不适合代替精确的下载进度。

七、等待过程中的取消条件

当所属流程已经退出时,原来的等待回调不应该继续执行。

可以设置取消条件:

bash 复制代码
protected bool mCanceled;

protected void beginWaiting()
{
	mCanceled = false;

	Waiting waiting =
		mWaitingManager.createWaiting(onFinished);

	waiting.addCondition(() => mPlayerCreated);
	waiting.addCondition(() => mServerDataReady);
	waiting.setCancelCondition(() => mCanceled);
}

protected void leaveCurrentFlow()
{
	mCanceled = true;
}

当取消条件返回 true 时:

bash 复制代码
不执行完成回调
自动销毁 Waiting

默认创建的 Waiting 会自动销毁,所以这里只需要修改取消条件。

取消条件适合处理:

bash 复制代码
界面已经关闭
SceneProcedure 已经退出
玩家主动取消匹配
当前请求已经被新请求替代
对象准备销毁

八、手动管理 Waiting 生命周期

默认情况下,等待完成或取消后会自动销毁:

bash 复制代码
Waiting waiting =
	mWaitingManager.createWaiting(onFinished);

等价于:

bash 复制代码
Waiting waiting =
	mWaitingManager.createWaiting(onFinished, true);

如果需要保存引用并由业务手动管理,可以关闭自动销毁:

bash 复制代码
protected Waiting mWaiting;

protected void beginWaiting()
{
	mWaiting =
		mWaitingManager.createWaiting(
			onFinished,
			false);

	mWaiting.addCondition(() => mPlayerCreated);
	mWaiting.addCondition(() => mServerDataReady);
}

完成后手动销毁:

bash 复制代码
protected void onFinished()
{
	mWaitingManager.destroyWaiting(ref mWaiting);

	enterBattle();
}

流程提前退出时取消:

bash 复制代码
protected void cancelWaiting()
{
	mWaitingManager?.cancel(ref mWaiting);
}

cancel() 会立即移除并回收等待对象,同时将引用设置为 null,不会调用完成回调。

关闭自动销毁后,必须主动调用:

bash 复制代码
destroyWaiting(ref waiting);

或者:

bash 复制代码
cancel(ref waiting);

否则等待对象会一直保留在 WaitingManager 中。

九、空 Waiting 的行为

下面创建了一个没有任何条件的等待对象:

bash 复制代码
mWaitingManager.createWaiting(onFinished);

没有条件时,Waiting.isDone() 会直接返回 true

因此它会在 WaitingManager 下一次更新时执行回调,而不是在 createWaiting() 中立即调用。

这可以用来把一段逻辑延迟到下一次框架更新,但最好只在确实需要这种行为时使用。

十、和 AsyncTaskGroup 的区别

两者都能等待多个任务,但侧重点不同。

AsyncTaskGroup

主要等待多个异步操作:

bash 复制代码
资源A加载
资源B加载
资源C加载
    ↓
全部完成后回调

适合资源预加载和批量异步初始化。

WaitingManager

可以混合等待:

bash 复制代码
异步操作
业务布尔条件
服务器数据状态
对象创建状态
    ↓
全部满足后回调

选择方式可以概括为:

bash 复制代码
只组合多个异步任务
使用 AsyncTaskGroup

需要同时等待异步任务和业务状态
使用 WaitingManager

十一、总结

WaitingManager 的基本用法是:

bash 复制代码
Waiting waiting =
	mWaitingManager.createWaiting(onFinished);

waiting.addAsyncOperation(operation);
waiting.addCondition(() => conditionA);
waiting.addCondition(() => conditionB);

需要取消时:

bash 复制代码
waiting.setCancelCondition(() => canceled);

需要手动管理生命周期时:

bash 复制代码
mWaitingManager.cancel(ref waiting);

它把原本散落在 update()、异步回调和多个布尔字段中的判断,集中成一个明确的等待对象:

bash 复制代码
添加异步操作
添加业务条件
设置取消条件
全部满足后统一回调

这样进入场景、开始战斗、匹配完成和批量初始化等流程,可以直接表达"需要等待什么",而不需要在各处重复维护完成判断。

相关推荐
SmalBox10 小时前
【节点】[UVCombine节点]原理解析与实际应用
unity3d·游戏开发·图形学
_zhourui_h_1 天前
Unity MyFramework 用法说明(十五):使用 ResourceRef 管理资源引用与异步加载
unity3d
SmalBox1 天前
【节点】[ThreadMapDetail节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox2 天前
【节点】[Emission节点]原理解析与实际应用
unity3d·游戏开发·图形学
_zhourui_h_2 天前
Unity MyFramework 用法说明(十四):使用 ComponentOwner 和 GameComponent 拆分对象能力
unity3d
_zhourui_h_3 天前
Unity MyFramework 用法说明(十三):使用 StateMachine 管理互斥与共存的角色状态
unity3d
SmalBox3 天前
【节点】[UnpackWaterData节点]原理解析与实际应用
unity3d·游戏开发·图形学