UE4 ActionGame知识点总结1-FLatentActionInfo以及FLatentActionManager使用

写这篇文字之前一直有两个问题:

1. Delay操作是如何实现的?

2. 是否可以使用Delay的实现机制来处理一些需要异步处理的东西,比如一些读写数据?

我们先看下Delay的使用和实现:


按下1键,5秒后会打印出文字。需要说明的是:调用完Delay节点后,Completed的pin在5秒后才会调用,所以应该把这一帧需要执行的操作放在delay节点前。

注意Delay和Window编程中的Sleep(5000)操作完全不同,Sleep是挂起当前的线程,把执行权交给其他线程,大约5秒后再唤醒这个线程。Delay并没有挂起本线程,而是立即返回,5秒后再来执行Completed Pin后的节点。

了解了Delay的使用后,我们看下Delay的实现:

/** 
	 * Perform a latent action with a delay (specified in seconds).  Calling again while it is counting down will be ignored.
	 * 
	 * @param WorldContext	World context.
	 * @param Duration 		length of delay (in seconds).
	 * @param LatentInfo 	The latent action.
	 */
	UFUNCTION(BlueprintCallable, Category="Utilities|FlowControl", meta=(Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", Duration="0.2", Keywords="sleep"))
	static void	Delay(UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );
void UKismetSystemLibrary::Delay(UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo )
{
	if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
	{
		FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
		if (LatentActionManager.FindExistingAction<FDelayAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
		{
			LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FDelayAction(Duration, LatentInfo));
		}
	}
}

Delay的内部逻辑很简单,向LatentActionManager实例中Add了一个FDelayAction的实例。我们看下FDelayAction的实现:

// FDelayAction
// A simple delay action; counts down and triggers it's output link when the time remaining falls to zero
class FDelayAction : public FPendingLatentAction
{
public:
	float TimeRemaining;
	FName ExecutionFunction;
	int32 OutputLink;
	FWeakObjectPtr CallbackTarget;

	FDelayAction(float Duration, const FLatentActionInfo& LatentInfo)
		: TimeRemaining(Duration)
		, ExecutionFunction(LatentInfo.ExecutionFunction)
		, OutputLink(LatentInfo.Linkage)
		, CallbackTarget(LatentInfo.CallbackTarget)
	{
	}

	virtual void UpdateOperation(FLatentResponse& Response) override
	{
		TimeRemaining -= Response.ElapsedTime();
		Response.FinishAndTriggerIf(TimeRemaining <= 0.0f, ExecutionFunction, OutputLink, CallbackTarget);
	}

#if WITH_EDITOR
	// Returns a human readable description of the latent operation's current state
	virtual FString GetDescription() const override
	{
		static const FNumberFormattingOptions DelayTimeFormatOptions = FNumberFormattingOptions()
			.SetMinimumFractionalDigits(3)
			.SetMaximumFractionalDigits(3);
		return FText::Format(NSLOCTEXT("DelayAction", "DelayActionTimeFmt", "Delay ({0} seconds left)"), FText::AsNumber(TimeRemaining, &DelayTimeFormatOptions)).ToString();
	}
#endif
};

Delay的实现都在这里了,我们是Delay了5秒并且是在关卡蓝图里调用的,构建FDelayAction传入的参数中,TimeRemaining就是5.0f,ExecutionFunction和OutputLink是Completed Pin后面节点的相关信息。CallbackTarget是指这个Delay操作是在哪个UObject里面的,因为我们是在关卡蓝图里调用的,所以这里的CallbackTarget是ALevelScriptActor实例。FDelayAction有个UpdateOperation操作,我们可以猜测到是游戏线程每帧都会更新的代码,通过每次减去DeltaTime并且判断时间TimeRemaining是否已经小于0,如果是则执行后面的节点。目前就剩下最后一个疑惑了,UpdateOperation是如何调用的呢?通过调试我们可以知道是FLatentActionManager::ProcessLatentActions进行调用的,我们全局搜索发现调用ProcessLatentActions的有下面几个:

  D:\Software\UE4\Engine\UE_4.19\Engine\Source\Runtime\Engine\Private\Components\ActorComponent.cpp(909):			ComponentWorld->GetLatentActionManager().ProcessLatentActions(this, ComponentWorld->GetDeltaSeconds());
  D:\Software\UE4\Engine\UE_4.19\Engine\Source\Runtime\Engine\Private\Actor.cpp(889):	MyWorld->GetLatentActionManager().ProcessLatentActions(this, MyWorld->GetDeltaSeconds());
  D:\Software\UE4\Engine\UE_4.19\Engine\Source\Runtime\Engine\Private\LevelTick.cpp(1468):				CurrentLatentActionManager.ProcessLatentActions(NULL, DeltaSeconds);
  D:\Software\UE4\Engine\UE_4.19\Engine\Source\Runtime\UMG\Private\UserWidget.cpp(1347):		World->GetLatentActionManager().ProcessLatentActions(this, InDeltaTime);
上面几个调用都是在各自的Tick函数中执行的。

现在整个框架已经知道了,当某个Object中有Delay操作执行时,会向FLatentActionManager中添加一个对应的Action并且立即返回,后续Level或者这个Object的Tick函数就会执行到这个Action的UpdateOperation中,FLatentActionManager的实现机制保证了UpdateOperation在本帧只会执行一次。Action的UpdateOperation就是Delay的相关逻辑判断,如果条件达到了就通知Delay后面的节点执行即可。

那么我们现在看下上面提到的两个问题:

1. Delay操作是如何实现的?这个上面一段话已经解释了。

2. 是否可以使用Delay的实现机制来处理一些需要异步处理的东西,比如一些读写数据?这个得看下具体要实现什么,如果Action的UpdateOperation中执行一些需要时间的逻辑(磁盘读数据,网络等数据,计算时间很长的),那么是不可以的,因为UpdateOperation阻塞或者等待其实会导致游戏线程的Tick阻塞和等待,会导致游戏画面卡死等问题,UpdateOperation应该执行一些不会等待的东西,比如可以查看某些状态是否已经完成了等等。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值