vault backup: 2024-10-12 17:19:45
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
# Latent
|
||||
|
||||
- **功能描述:** 标明一个函数是一个延迟异步操作
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Blueprint
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [LatentInfo](LatentInfo.md), [NeedsLatentFixup](NeedsLatentFixup.md), [LatentCallbackTarget](LatentCallbackTarget.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
标明一个函数是一个延迟异步操作,需要配合LatentInfo来使用。
|
||||
|
||||
会导致在逻辑执行上Then(也叫Complete)引脚需要手动触发(引擎内部触发),且函数右上角增加时钟的图标。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
class FMySleepAction : public FPendingLatentAction
|
||||
{
|
||||
public:
|
||||
float TimeRemaining;
|
||||
FName ExecutionFunction;
|
||||
int32 OutputLink;
|
||||
FWeakObjectPtr CallbackTarget;
|
||||
|
||||
FMySleepAction(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);
|
||||
}
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyFunction_Latent :public UBlueprintFunctionLibrary
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, meta = (Latent, WorldContext = "WorldContextObject", LatentInfo = "LatentInfo", Duration = "5"))
|
||||
static void MySleep(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo)
|
||||
{
|
||||
if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
|
||||
{
|
||||
FLatentActionManager& LatentActionManager = World->GetLatentActionManager();
|
||||
if (LatentActionManager.FindExistingAction<FMySleepAction>(LatentInfo.CallbackTarget, LatentInfo.UUID) == NULL)
|
||||
{
|
||||
LatentActionManager.AddNewAction(LatentInfo.CallbackTarget, LatentInfo.UUID, new FMySleepAction(Duration, LatentInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (Latent, WorldContext = "WorldContextObject", Duration = "5"))
|
||||
static void MySleep2(const UObject* WorldContextObject, float Duration, FLatentActionInfo LatentInfo);
|
||||
};
|
||||
```
|
||||
|
||||
## 蓝图效果:
|
||||
|
||||

|
||||
|
||||
MySleep可以像Delay一样正常工作。但是MySleep2因为没有标明LatentInfo,因此LatentInfo函数参数没有被蓝图系统赋值,导致无法工作。
|
||||
|
||||
在源码里Latent使用的非常频繁,最常见的例子:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, meta=(WorldContext="WorldContextObject", Latent = "", LatentInfo = "LatentInfo", DisplayName = "Load Stream Level (by Name)"), Category="Game")
|
||||
static ENGINE_API void LoadStreamLevel(const UObject* WorldContextObject, FName LevelName, bool bMakeVisibleAfterLoad, bool bShouldBlockOnLoad, FLatentActionInfo LatentInfo);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (Latent, LatentInfo = "LatentInfo", WorldContext = "WorldContextObject", BlueprintInternalUseOnly = "true"), Category = "Utilities")
|
||||
static ENGINE_API void LoadAsset(const UObject* WorldContextObject, TSoftObjectPtr<UObject> Asset, FOnAssetLoaded OnLoaded, FLatentActionInfo LatentInfo);
|
||||
|
||||
UFUNCTION(BlueprintCallable, Category="Utilities|FlowControl", meta=(Latent, WorldContext="WorldContextObject", LatentInfo="LatentInfo", Duration="0.2", Keywords="sleep"))
|
||||
static ENGINE_API void Delay(const UObject* WorldContextObject, float Duration, struct FLatentActionInfo LatentInfo );
|
||||
```
|
||||
|
||||
关于使用Latent还是继承自UBlueprintAsyncActionBase来创建蓝图异步节点的差异,可以在网上别的文章查看。
|
@@ -0,0 +1,84 @@
|
||||
# LatentCallbackTarget
|
||||
|
||||
- **功能描述:** 用在FLatentActionInfo::CallbackTarget属性上,告诉蓝图VM在哪个对象上调用函数。
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [Latent](Latent.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
用在FLatentActionInfo::CallbackTarget属性上,告诉蓝图VM在哪个对象上调用函数。
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintInternalUseOnly)
|
||||
struct FLatentActionInfo
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/** Object to execute the function on. */
|
||||
UPROPERTY(meta=(LatentCallbackTarget = true))
|
||||
TObjectPtr<UObject> CallbackTarget;
|
||||
|
||||
//...
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 源码里作用的地方:
|
||||
|
||||
```cpp
|
||||
void EmitLatentInfoTerm(FBPTerminal* Term, FProperty* LatentInfoProperty, FBlueprintCompiledStatement* TargetLabel)
|
||||
{
|
||||
// Special case of the struct property emitter. Needs to emit a linkage property for fixup
|
||||
FStructProperty* StructProperty = CastFieldChecked<FStructProperty>(LatentInfoProperty);
|
||||
check(StructProperty->Struct == LatentInfoStruct);
|
||||
|
||||
int32 StructSize = LatentInfoStruct->GetStructureSize();
|
||||
uint8* StructData = (uint8*)FMemory_Alloca(StructSize);
|
||||
StructProperty->InitializeValue(StructData);
|
||||
|
||||
// Assume that any errors on the import of the name string have been caught in the function call generation
|
||||
StructProperty->ImportText_Direct(*Term->Name, StructData, NULL, 0, GLog);
|
||||
|
||||
Writer << EX_StructConst;
|
||||
Writer << LatentInfoStruct;
|
||||
Writer << StructSize;
|
||||
|
||||
checkSlow(Schema);
|
||||
for (FProperty* Prop = LatentInfoStruct->PropertyLink; Prop; Prop = Prop->PropertyLinkNext)
|
||||
{
|
||||
if (TargetLabel && Prop->GetBoolMetaData(FBlueprintMetadata::MD_NeedsLatentFixup))
|
||||
{
|
||||
// Emit the literal and queue a fixup to correct it once the address is known
|
||||
Writer << EX_SkipOffsetConst;
|
||||
CodeSkipSizeType PatchUpNeededAtOffset = Writer.EmitPlaceholderSkip();
|
||||
JumpTargetFixupMap.Add(PatchUpNeededAtOffset, FCodeSkipInfo(FCodeSkipInfo::Fixup, TargetLabel));
|
||||
}
|
||||
else if (Prop->GetBoolMetaData(FBlueprintMetadata::MD_LatentCallbackTarget))
|
||||
{
|
||||
FBPTerminal CallbackTargetTerm;
|
||||
CallbackTargetTerm.bIsLiteral = true;
|
||||
CallbackTargetTerm.Type.PinSubCategory = UEdGraphSchema_K2::PN_Self;
|
||||
EmitTermExpr(&CallbackTargetTerm, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new term for each property, and serialize it out
|
||||
FBPTerminal NewTerm;
|
||||
if(Schema->ConvertPropertyToPinType(Prop, NewTerm.Type))
|
||||
{
|
||||
NewTerm.bIsLiteral = true;
|
||||
Prop->ExportText_InContainer(0, NewTerm.Name, StructData, StructData, NULL, PPF_None);
|
||||
|
||||
EmitTermExpr(&NewTerm, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing for unsupported/unhandled property types. This will leave the value unchanged from its constructed default.
|
||||
Writer << EX_Nothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Writer << EX_EndStructConst;
|
||||
}
|
||||
```
|
@@ -0,0 +1,81 @@
|
||||
# LatentInfo
|
||||
|
||||
- **功能描述:** 和Latent配合,指明哪个函数参数是LatentInfo参数。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **元数据类型:** string="abc"
|
||||
- **关联项:** [Latent](Latent.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
Latent的函数需要FLatentActionInfo才能工作。FLatentActionInfo里记录着这个延迟操作的ID以及下一步要执行的函数名称等。在蓝图的虚拟机运行环境下,一个Latent函数执行的时候,蓝图VM会收集当前的函数上下文信息(典型的比如下Latent函数连接的下一个节点),然后继续赋值到Latent函数的FLatentActionInfo参数上,再配合FPendingLatentAction注册到FLatentActionManager里面去。等时间到达或者触发条件达成后,FLatentActionManager会触发CallbackTarget->ProcessEvent(ExecutionFunction, &(LinkInfo.LinkID)),从而继续执行下去。
|
||||
|
||||
如果没有用LatentInfo来指定函数参数,则因为断了LatentInfo的赋值操作,因此就无法正常工作,蓝图效果图见Latent页面。
|
||||
|
||||
LatentInfo值就像WorldContext一样,会被蓝图VM系统自动的填充值。填充值的操作是在EmitLatentInfoTerm里执行的。把LatentInfoStruct的值填充到LatentInfo的函数参数里去。LatentInfo的参数位置并不重要。LatentInfo指定的函数参数Pin会被隐藏。
|
||||
|
||||
```cpp
|
||||
void EmitFunctionCall(FKismetCompilerContext& CompilerContext, FKismetFunctionContext& FunctionContext, FBlueprintCompiledStatement& Statement, UEdGraphNode* SourceNode)
|
||||
{
|
||||
if (bIsUbergraph && FuncParamProperty->GetName() == FunctionToCall->GetMetaData(FBlueprintMetadata::MD_LatentInfo))
|
||||
{
|
||||
EmitLatentInfoTerm(Term, FuncParamProperty, Statement.TargetLabel);
|
||||
}
|
||||
}
|
||||
|
||||
void EmitLatentInfoTerm(FBPTerminal* Term, FProperty* LatentInfoProperty, FBlueprintCompiledStatement* TargetLabel)
|
||||
{
|
||||
// Special case of the struct property emitter. Needs to emit a linkage property for fixup
|
||||
FStructProperty* StructProperty = CastFieldChecked<FStructProperty>(LatentInfoProperty);
|
||||
check(StructProperty->Struct == LatentInfoStruct);
|
||||
|
||||
int32 StructSize = LatentInfoStruct->GetStructureSize();
|
||||
uint8* StructData = (uint8*)FMemory_Alloca(StructSize);
|
||||
StructProperty->InitializeValue(StructData);
|
||||
|
||||
// Assume that any errors on the import of the name string have been caught in the function call generation
|
||||
StructProperty->ImportText_Direct(*Term->Name, StructData, NULL, 0, GLog);
|
||||
|
||||
Writer << EX_StructConst;
|
||||
Writer << LatentInfoStruct;
|
||||
Writer << StructSize;
|
||||
|
||||
checkSlow(Schema);
|
||||
for (FProperty* Prop = LatentInfoStruct->PropertyLink; Prop; Prop = Prop->PropertyLinkNext)
|
||||
{
|
||||
if (TargetLabel && Prop->GetBoolMetaData(FBlueprintMetadata::MD_NeedsLatentFixup))
|
||||
{
|
||||
// Emit the literal and queue a fixup to correct it once the address is known
|
||||
Writer << EX_SkipOffsetConst;
|
||||
CodeSkipSizeType PatchUpNeededAtOffset = Writer.EmitPlaceholderSkip();
|
||||
JumpTargetFixupMap.Add(PatchUpNeededAtOffset, FCodeSkipInfo(FCodeSkipInfo::Fixup, TargetLabel));
|
||||
}
|
||||
else if (Prop->GetBoolMetaData(FBlueprintMetadata::MD_LatentCallbackTarget))
|
||||
{
|
||||
FBPTerminal CallbackTargetTerm;
|
||||
CallbackTargetTerm.bIsLiteral = true;
|
||||
CallbackTargetTerm.Type.PinSubCategory = UEdGraphSchema_K2::PN_Self;
|
||||
EmitTermExpr(&CallbackTargetTerm, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new term for each property, and serialize it out
|
||||
FBPTerminal NewTerm;
|
||||
if(Schema->ConvertPropertyToPinType(Prop, NewTerm.Type))
|
||||
{
|
||||
NewTerm.bIsLiteral = true;
|
||||
Prop->ExportText_InContainer(0, NewTerm.Name, StructData, StructData, NULL, PPF_None);
|
||||
|
||||
EmitTermExpr(&NewTerm, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing for unsupported/unhandled property types. This will leave the value unchanged from its constructed default.
|
||||
Writer << EX_Nothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Writer << EX_EndStructConst;
|
||||
}
|
||||
```
|
||||
|
||||
LatentInfo信息的收集是在FKCHandler_CallFunction::CreateFunctionCallStatement里
|
@@ -0,0 +1,86 @@
|
||||
# NeedsLatentFixup
|
||||
|
||||
- **功能描述:** 用在FLatentActionInfo::Linkage属性上,告诉蓝图VM生成跳转信息
|
||||
- **使用位置:** UPROPERTY
|
||||
- **元数据类型:** bool
|
||||
- **关联项:** [Latent](Latent.md)
|
||||
- **常用程度:** ★
|
||||
|
||||
## 在源码里找到的用处:
|
||||
|
||||
```cpp
|
||||
USTRUCT(BlueprintInternalUseOnly)
|
||||
struct FLatentActionInfo
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/** The resume point within the function to execute */
|
||||
UPROPERTY(meta=(NeedsLatentFixup = true))
|
||||
int32 Linkage;
|
||||
|
||||
//...
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 源码里发挥作用的地方:
|
||||
|
||||
看着就是把Linkage这个属性进行单独的处理。用来在JumpTargetFixupMap里进行专门的跳转
|
||||
|
||||
```cpp
|
||||
void EmitLatentInfoTerm(FBPTerminal* Term, FProperty* LatentInfoProperty, FBlueprintCompiledStatement* TargetLabel)
|
||||
{
|
||||
// Special case of the struct property emitter. Needs to emit a linkage property for fixup
|
||||
FStructProperty* StructProperty = CastFieldChecked<FStructProperty>(LatentInfoProperty);
|
||||
check(StructProperty->Struct == LatentInfoStruct);
|
||||
|
||||
int32 StructSize = LatentInfoStruct->GetStructureSize();
|
||||
uint8* StructData = (uint8*)FMemory_Alloca(StructSize);
|
||||
StructProperty->InitializeValue(StructData);
|
||||
|
||||
// Assume that any errors on the import of the name string have been caught in the function call generation
|
||||
StructProperty->ImportText_Direct(*Term->Name, StructData, NULL, 0, GLog);
|
||||
|
||||
Writer << EX_StructConst;
|
||||
Writer << LatentInfoStruct;
|
||||
Writer << StructSize;
|
||||
|
||||
checkSlow(Schema);
|
||||
for (FProperty* Prop = LatentInfoStruct->PropertyLink; Prop; Prop = Prop->PropertyLinkNext)
|
||||
{
|
||||
if (TargetLabel && Prop->GetBoolMetaData(FBlueprintMetadata::MD_NeedsLatentFixup))
|
||||
{
|
||||
// Emit the literal and queue a fixup to correct it once the address is known
|
||||
Writer << EX_SkipOffsetConst;
|
||||
CodeSkipSizeType PatchUpNeededAtOffset = Writer.EmitPlaceholderSkip();
|
||||
JumpTargetFixupMap.Add(PatchUpNeededAtOffset, FCodeSkipInfo(FCodeSkipInfo::Fixup, TargetLabel));
|
||||
}
|
||||
else if (Prop->GetBoolMetaData(FBlueprintMetadata::MD_LatentCallbackTarget))
|
||||
{
|
||||
FBPTerminal CallbackTargetTerm;
|
||||
CallbackTargetTerm.bIsLiteral = true;
|
||||
CallbackTargetTerm.Type.PinSubCategory = UEdGraphSchema_K2::PN_Self;
|
||||
EmitTermExpr(&CallbackTargetTerm, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new term for each property, and serialize it out
|
||||
FBPTerminal NewTerm;
|
||||
if(Schema->ConvertPropertyToPinType(Prop, NewTerm.Type))
|
||||
{
|
||||
NewTerm.bIsLiteral = true;
|
||||
Prop->ExportText_InContainer(0, NewTerm.Name, StructData, StructData, NULL, PPF_None);
|
||||
|
||||
EmitTermExpr(&NewTerm, Prop);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do nothing for unsupported/unhandled property types. This will leave the value unchanged from its constructed default.
|
||||
Writer << EX_Nothing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Writer << EX_EndStructConst;
|
||||
}
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
Reference in New Issue
Block a user