赞
踩
FPendingLatentAction
。UpdateOperation
是关键。Response.DoneIf(true);
是该动作标志着工作做完了调用的,相当与需要 destory了。class ENGINE_API FPendingLatentAction { public: // 构造 FPendingLatentAction() { } // 析构 virtual ~FPendingLatentAction() { } // 动作完成后传入true,然后就会被销毁。 virtual void UpdateOperation(FLatentResponse& Response) { Response.DoneIf(true); } /* 让潜伏的动作知道,产生它的对象已经被垃圾回收了。 * 并且该动作将被销毁(不再有UpdateOperation的调用,并且CallbackTarget已经是NULL) * 只有当对象在动作完成前离开时才会被调用。 */ virtual void NotifyObjectDestroyed() {} virtual void NotifyActionAborted() {} #if WITH_EDITOR // 返回对潜伏操作当前状态的可读描述。 virtual FString GetDescription() const; #endif };
FPendingLatentAction
。UpdateOperation
,这个函数因为相当于 Tick
函数,所以可以写一些计时的业务逻辑。GetDescription
,为了使得在蓝图调试过程中打印在节点上。class FDelayAction : public FPendingLatentAction { public: float TimeRemaining; // 业务变量 FName ExecutionFunction; // LatentInfo服务 int32 OutputLink; // LatentInfo服务 FWeakObjectPtr CallbackTarget; // LatentInfo服务 // 有参构造 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(); // 完成与触发,如果想要深入理解可以去看FLatentResponse,这个函数相当于DoneIf + TriggerLink。 Response.FinishAndTriggerIf(TimeRemaining <= 0.0f, ExecutionFunction, OutputLink, CallbackTarget); } #if WITH_EDITOR // 蓝图调试过程中打印 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 };
UFUNCTION(BlueprintCallable, Category="Utilities|FlowControl", meta=(Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", Duration="0.2", Keywords="sleep"))
static void Delay(const UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );
UBlueprintFunctionLibrary
UKismetSystemLibrary
- \\UE_5.1\Engine\Source\Runtime\Engine\Classes\Kismet\KismetSystemLibrary.h
Delay
。void UKismetSystemLibrary::Delay(const 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));
}
}
}
// Copyright(c) 2023 TangYijun All rights reserved. #pragma once #include "LatentActions.h" #include "Misc/EnumRange.h" // 轴输入的几个阶段 UENUM(BlueprintType) enum class ESustainedAxisState : uint8 { EPress, ELowPulseInterval, ELowPulseTrigger, EHighPulseInterval, ELowToHighTransition, EHighPulseTrigger, ECount }; // 轴映射的正反 UENUM(BlueprintType) enum class ESustainedAxisExec : uint8 { EPositive, ENegative }; // 创建枚举的计数 ENUM_RANGE_BY_COUNT(ESustainedAxisState, ESustainedAxisState::ECount); // 基类 class FSustainedAxisTriggerActionBase : public FPendingLatentAction { public: float Axis; // 轴输入 float AxisThreshold; // 创建枚举的计数 FName ExecutionFunction; int32 OutputLink; FWeakObjectPtr CallbackTarget; ESustainedAxisExec& ExecRef; // 节点的输出执行线 protected: TBitArray<> AxisStateBits; // 轴输入的激活状态集 TMap<ESustainedAxisState, float> CountMap; // 计时器 bool bIsPress; // 是否摁下 public: //构造 FSustainedAxisTriggerActionBase(float Axis, float AxisThreshold, const FLatentActionInfo& LatentInfo, ESustainedAxisExec& AxisExec) : Axis(Axis) , AxisThreshold(AxisThreshold) , ExecutionFunction(LatentInfo.ExecutionFunction) , OutputLink(LatentInfo.Linkage) , CallbackTarget(LatentInfo.CallbackTarget) , ExecRef(AxisExec) { // 初始化状态集 AxisStateBits = TBitArray<>(false, uint8(ESustainedAxisState::ECount)); } // 业务更新 virtual void UpdateOperation(FLatentResponse& Response) override { // 判断轴输入 bIsPress = Axis > AxisThreshold || Axis < -AxisThreshold; UpdateDoneIf(Response); // 更新执行线走向 UpdateTimer(Response); // 更新计时器 } protected: FORCEINLINE bool IsTimeOver(const ESustainedAxisState& State) { return CountMap[State] <= 0.f && AxisStateBits[uint8(State)]; } FORCEINLINE void EnterState(const ESustainedAxisState& State) { AxisStateBits[uint8(State)] = true; } FORCEINLINE void ExitState(const ESustainedAxisState& State) { AxisStateBits[uint8(State)] = false; } FORCEINLINE void UpdateTimer(FLatentResponse& Response) { for (ESustainedAxisState AxisState : TEnumRange<ESustainedAxisState>()) { if (CountMap.Contains(AxisState)) { if (AxisStateBits[uint8(AxisState)]) { CountMap[AxisState] -= Response.ElapsedTime(); } } } } FORCEINLINE void UpdateDoneIf(FLatentResponse& Response) { if (!bIsPress) { Response.DoneIf(!bIsPress); } else { if (Axis > AxisThreshold) { ExecRef = ESustainedAxisExec::EPositive; } else if (Axis < -AxisThreshold) { ExecRef = ESustainedAxisExec::ENegative; } } } }; // 一阶段持续轴触发动作,就是按下后一定时间开始保持持续触发状态。 class FOneStepSustainedAxisTriggerAction : public FSustainedAxisTriggerActionBase { public: float PulseTriggerTime; // 脉冲触发时间 - 触发频率。 float PulseIntervalTime;// 脉冲间隔时间 - 从触发第一次到开始持续触发状态的间隔时间。 FOneStepSustainedAxisTriggerAction(float Axis, float AxisThreshold, float PulseTriggerTime, float PulseIntervalTime, const FLatentActionInfo& LatentInfo, ESustainedAxisExec& AxisExec) : FSustainedAxisTriggerActionBase(Axis, AxisThreshold, LatentInfo, AxisExec) , PulseTriggerTime(PulseTriggerTime) , PulseIntervalTime(PulseIntervalTime) { // 增加这两种状态的定时器 CountMap.Emplace(ESustainedAxisState::ELowPulseInterval, PulseIntervalTime); CountMap.Emplace(ESustainedAxisState::ELowPulseTrigger, PulseTriggerTime); } virtual void UpdateOperation(FLatentResponse& Response) override { FSustainedAxisTriggerActionBase::UpdateOperation(Response); // 简易状态机 if (bIsPress && !AxisStateBits[uint8(ESustainedAxisState::EPress)]) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); EnterState(ESustainedAxisState::EPress); EnterState(ESustainedAxisState::ELowPulseInterval); } if (IsTimeOver(ESustainedAxisState::ELowPulseInterval)) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); ExitState(ESustainedAxisState::ELowPulseInterval); EnterState(ESustainedAxisState::ELowPulseTrigger); } if (IsTimeOver(ESustainedAxisState::ELowPulseTrigger)) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); CountMap[ESustainedAxisState::ELowPulseTrigger] = PulseTriggerTime; } } }; class FTwoStepSustainedAxisTriggerAction : public FSustainedAxisTriggerActionBase { public: float LowPulseTriggerTime; // 低频脉冲触发时间 - 阶段一触发频率。 float LowPulseIntervalTime; // 低频脉冲间隔时间 - 从触发第一次到开始阶段一的间隔时间。 float HighPulseTriggerTime; // 高频脉冲触发时间 - 阶段二触发频率。 float HighPulseIntervalTime;// 高频脉冲间隔时间 - 从阶段一到阶段二的间隔时间。 FTwoStepSustainedAxisTriggerAction(float Axis, float AxisThreshold, float LowPulseTriggerTime, float LowPulseIntervalTime, float HighPulseTriggerTime, float HighPulseIntervalTime, const FLatentActionInfo& LatentInfo, ESustainedAxisExec& AxisExec) : FSustainedAxisTriggerActionBase(Axis, AxisThreshold, LatentInfo, AxisExec) , LowPulseTriggerTime(LowPulseTriggerTime) , LowPulseIntervalTime(LowPulseIntervalTime) , HighPulseTriggerTime(HighPulseTriggerTime) , HighPulseIntervalTime(HighPulseIntervalTime) { // 增加这四种状态的定时器 CountMap.Emplace(ESustainedAxisState::ELowPulseInterval, LowPulseIntervalTime); CountMap.Emplace(ESustainedAxisState::ELowPulseTrigger, LowPulseTriggerTime); CountMap.Emplace(ESustainedAxisState::EHighPulseInterval, HighPulseIntervalTime); CountMap.Emplace(ESustainedAxisState::EHighPulseTrigger, HighPulseTriggerTime); } virtual void UpdateOperation(FLatentResponse& Response) override { FSustainedAxisTriggerActionBase::UpdateOperation(Response); // 简易状态机 if (bIsPress && !AxisStateBits[uint8(ESustainedAxisState::EPress)]) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); EnterState(ESustainedAxisState::EPress); EnterState(ESustainedAxisState::ELowPulseInterval); } if (IsTimeOver(ESustainedAxisState::ELowPulseInterval)) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); ExitState(ESustainedAxisState::ELowPulseInterval); EnterState(ESustainedAxisState::ELowPulseTrigger); EnterState(ESustainedAxisState::EHighPulseInterval); } if (IsTimeOver(ESustainedAxisState::ELowPulseTrigger)) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); CountMap[ESustainedAxisState::ELowPulseTrigger] = LowPulseTriggerTime; if (AxisStateBits[uint8(ESustainedAxisState::ELowToHighTransition)]) { EnterState(ESustainedAxisState::EHighPulseTrigger); ExitState(ESustainedAxisState::ELowPulseTrigger); } } if (IsTimeOver(ESustainedAxisState::EHighPulseInterval)) { if (CountMap[ESustainedAxisState::ELowPulseTrigger] <= 0) { EnterState(ESustainedAxisState::EHighPulseTrigger); } else { EnterState(ESustainedAxisState::ELowToHighTransition); } ExitState(ESustainedAxisState::EHighPulseInterval); } if (IsTimeOver(ESustainedAxisState::EHighPulseTrigger)) { Response.TriggerLink(ExecutionFunction, OutputLink, CallbackTarget); CountMap[ESustainedAxisState::EHighPulseTrigger] = HighPulseTriggerTime; } } };
// Copyright(c) 2023 TangYijun All rights reserved. #pragma once #include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "PendingLatentAction/SustainedAxisTriggerAction.h" #include "LatentFunctionLibrary.generated.h" UCLASS() class HECATONCHEIRES_API ULatentFunctionLibrary : public UBlueprintFunctionLibrary { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, Category = "UI|Trigger", meta = (ExpandEnumAsExecs = "AxisExec", Latent, WorldContext = "WorldContextObject", LatentInfo = "LatentInfo")) static void OneStepSustainedAxisTrigger(const UObject* WorldContextObject, ESustainedAxisExec& AxisExec, float Axis, float AxisThreshold, float PulseTriggerTime, float PulseIntervalTime, FLatentActionInfo LatentInfo); UFUNCTION(BlueprintCallable, Category = "UI|Trigger", meta = (ExpandEnumAsExecs = "AxisExec", Latent, WorldContext = "WorldContextObject", LatentInfo = "LatentInfo")) static void TwoStepSustainedAxisTrigger(const UObject* WorldContextObject, ESustainedAxisExec& AxisExec, float Axis, float AxisThreshold, float LowPulseTriggerTime, float LowPulseIntervalTime, float HighPulseTriggerTime, float HighPulseIntervalTime, FLatentActionInfo LatentInfo); };
// Copyright(c) 2023 TangYijun All rights reserved. #include "FunctionLibrary/LatentFunctionLibrary.h" #include "Engine/LatentActionManager.h" void ULatentFunctionLibrary::OneStepSustainedAxisTrigger(const UObject* WorldContextObject, ESustainedAxisExec& AxisExec, float Axis, float AxisThreshold, float PulseTriggerTime, float PulseIntervalTime, FLatentActionInfo LatentInfo) { if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) { FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); FOneStepSustainedAxisTriggerAction* Action = LatentActionManager.FindExistingAction<FOneStepSustainedAxisTriggerAction>(LatentInfo.CallbackTarget, LatentInfo.UUID); if (!Action) { LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FOneStepSustainedAxisTriggerAction(Axis, AxisThreshold, PulseTriggerTime, PulseIntervalTime, LatentInfo, AxisExec)); } else { Action->Axis = Axis; } } } void ULatentFunctionLibrary::TwoStepSustainedAxisTrigger(const UObject* WorldContextObject, ESustainedAxisExec& AxisExec, float Axis, float AxisThreshold, float LowPulseTriggerTime, float LowPulseIntervalTime, float HighPulseTriggerTime, float HighPulseIntervalTime, FLatentActionInfo LatentInfo) { if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)) { FLatentActionManager& LatentActionManager = World->GetLatentActionManager(); FTwoStepSustainedAxisTriggerAction* Action = LatentActionManager.FindExistingAction<FTwoStepSustainedAxisTriggerAction>(LatentInfo.CallbackTarget, LatentInfo.UUID); if (!Action) { LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FTwoStepSustainedAxisTriggerAction(Axis, AxisThreshold, LowPulseTriggerTime, LowPulseIntervalTime, HighPulseTriggerTime, HighPulseIntervalTime, LatentInfo, AxisExec)); } else { Action->Axis = Axis; } } }
FTimerHandle TimerHandle;
const auto Exec = []()
{
// Implement...
};
World->GetTimerManager().SetTimer(TimerHandle, Exec, 1.0f, false);
FTimerHandle TimerHandle;
World->GetTimerManager().SetTimer(TimeHandler, this, &ThisClass::TimerFunction, 1.0f, false);
GetWorld()->GetTimerManager().ClearTimer(TimeHandler);
World->GetTimerManager().SetTimer(TimerHandle, Exec, 2.0f, false);
World->GetTimerManager().SetTimer(TimerHandle, Exec, 2.0f, true, 0.0f);
World->GetTimerManager().SetTimer(TimerHandle, Exec, 1.0f, true, 2.0f);
(TODO)
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。