vault backup: 2024-10-12 17:19:45
@@ -0,0 +1,46 @@
|
||||
# ArrayParm
|
||||
|
||||
- **功能描述:** 指定一个函数为使用Array<*>的函数,数组元素类型为通配符的泛型。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Blueprint
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
- **关联项:** [ArrayTypeDependentParams](../ArrayTypeDependentParams/ArrayTypeDependentParams.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定一个函数为使用Array<*>的函数,数组元素类型为通配符的泛型。
|
||||
|
||||
在内部逻辑上的处理区别是有ArrayParm的会采用UK2Node_CallArrayFunction来生成节点,而不是UK2Node_CallFunction。
|
||||
|
||||
ArrayParam可以指定多个,用逗号分隔开。
|
||||
|
||||
在源码里只在UKismetArrayLibrary里使用,但如果自己也想顶一个数组的操作,则也可以加上ArrayParam。
|
||||
|
||||
因为数组元素类型为通配符的泛型,因此在C++中实现的时候,要配合CustomThunk来自己写一些蓝图逻辑胶水代码才好正确处理不同的数组类型。这部分可以参照源码里UKismetArrayLibrary的样例模仿。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyFunction_Param :public UBlueprintFunctionLibrary
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//Array
|
||||
UFUNCTION(BlueprintPure, CustomThunk, meta = (ArrayParm = "TargetArray"))
|
||||
static int32 MyArray_Count(const TArray<int32>& TargetArray);
|
||||
static int32 GenericMyArray_Count(const void* TargetArray, const FArrayProperty* ArrayProp);
|
||||
DECLARE_FUNCTION(execMyArray_Count);
|
||||
|
||||
UFUNCTION(BlueprintPure, CustomThunk, meta = (ArrayParm = "ArrayA,ArrayB", ArrayTypeDependentParams = "ArrayB"))
|
||||
static int32 MyArray_CompareSize(const TArray<int32>& ArrayA, const TArray<int32>& ArrayB);
|
||||
static int32 GenericMyArray_CompareSize(void* ArrayA, const FArrayProperty* ArrayAProp, void* ArrayB, const FArrayProperty* ArrayBProp);
|
||||
DECLARE_FUNCTION(execMyArray_CompareSize);
|
||||
};
|
||||
```
|
||||
|
||||
## 蓝图效果:
|
||||
|
||||

|
||||
|
||||
可以看到,在没有连接具体数组类型的时候,Array是灰色的通配符类型。而连接上不同的数组类型,Array参数引脚就会自动变成相应的类型,这些逻辑是在UK2Node_CallArrayFunction中实现的,有兴趣的去自行翻阅。
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,69 @@
|
||||
# ArrayTypeDependentParams
|
||||
|
||||
- **功能描述:** 当ArryParam指定的函数拥有两个或以上Array参数的时候,指定哪些数组参数的类型也应该相应的被更新改变。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **元数据类型:** string="abc"
|
||||
- **关联项:** [ArrayParm](../ArrayParm/ArrayParm.md)
|
||||
|
||||
当ArryParam指定的函数拥有两个或以上Array参数的时候,指定哪些数组参数的类型也应该相应的被更新改变。
|
||||
|
||||
指明一个参数的类型,用于确定ArrayParam的值类型
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyFunction_Param :public UBlueprintFunctionLibrary
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//Array
|
||||
|
||||
UFUNCTION(BlueprintPure, CustomThunk, meta = (ArrayParm = "ArrayA,ArrayB", ArrayTypeDependentParams = "ArrayB"))
|
||||
static int32 MyArray_CompareSize(const TArray<int32>& ArrayA, const TArray<int32>& ArrayB);
|
||||
static int32 GenericMyArray_CompareSize(void* ArrayA, const FArrayProperty* ArrayAProp, void* ArrayB, const FArrayProperty* ArrayBProp);
|
||||
DECLARE_FUNCTION(execMyArray_CompareSize);
|
||||
};
|
||||
```
|
||||
|
||||
如果没有ArrayTypeDependentParams,在连接ArrayA后,ArrayB的类型依然没有确定,即使连接上了也是如此,这应该是引擎的实现所限制。编译会造成编译错误。
|
||||
|
||||

|
||||
|
||||
因此ArrayTypeDependentParams可以指定另外的数组参数,其类型会由别的(第一个)数组实际参数所决定,即typeof(ArrayB)=typeof(ArrayA)。在示例代码里所示加上ArrayB作为ArrayTypeDependentParams 之后,MyArrayB无论是先连接到ArrayA还是ArrayB都可以触发二者改变为一致的数组类型。这是因为ArrayA作为第一个参数,天生在引擎内已经实现了第一个参数的动态类型实时变化。因此我们只要再加上ArrayB就好了。
|
||||
|
||||
## 原理:
|
||||
|
||||
引擎内已经实现了第一个参数的动态类型实时变化:
|
||||
|
||||
```cpp
|
||||
void UK2Node_CallArrayFunction::AllocateDefaultPins()
|
||||
{
|
||||
Super::AllocateDefaultPins();
|
||||
|
||||
UEdGraphPin* TargetArrayPin = GetTargetArrayPin();
|
||||
if (ensureMsgf(TargetArrayPin, TEXT("%s"), *GetFullName()))
|
||||
{
|
||||
TargetArrayPin->PinType.ContainerType = EPinContainerType::Array;
|
||||
TargetArrayPin->PinType.bIsReference = true;
|
||||
TargetArrayPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
|
||||
TargetArrayPin->PinType.PinSubCategory = NAME_None;
|
||||
TargetArrayPin->PinType.PinSubCategoryObject = nullptr;
|
||||
}
|
||||
|
||||
TArray< FArrayPropertyPinCombo > ArrayPins;
|
||||
GetArrayPins(ArrayPins);
|
||||
for(auto Iter = ArrayPins.CreateConstIterator(); Iter; ++Iter)
|
||||
{
|
||||
if(Iter->ArrayPropPin)
|
||||
{
|
||||
Iter->ArrayPropPin->bHidden = true;
|
||||
Iter->ArrayPropPin->bNotConnectable = true;
|
||||
Iter->ArrayPropPin->bDefaultValueIsReadOnly = true;
|
||||
}
|
||||
}
|
||||
|
||||
PropagateArrayTypeInfo(TargetArrayPin);
|
||||
}
|
||||
```
|
||||
|
||||
关于ArrayDependentParam的作用机制,可以参照UK2Node_CallArrayFunction里的NotifyPinConnectionListChanged和PropagateArrayTypeInfo这两个函数的实现,可以看到其他的数组参数Pin类型被动态的修改为SourcePin的类型。
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,134 @@
|
||||
# AutoCreateRefTerm
|
||||
|
||||
- **功能描述:** 指定函数的多个输入引用参数在没有连接的时候自动为其创建默认值
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Blueprint
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
指定函数的多个输入引用参数在没有连接的时候自动为其创建默认值。
|
||||
|
||||
要注意“输入”+“引用”,这两个前提项。
|
||||
|
||||
当有些情况你想把函数的参数采用引用来传递,但是又不想每次都得必须连接一个变量,想在不连接的时候提供一个内联编辑的默认值,因此蓝图系统就提供了这么一个便利功能。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, meta = (AutoCreateRefTerm = "Location,Value"))
|
||||
static bool MyFunc_HasAutoCreateRefTerm(const FVector& Location, const int32& Value) { return false; }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static bool MyFunc_NoAutoCreateRefTerm(const FVector& Location, const int32& Value) { return false; }
|
||||
|
||||
UFUNCTION(BlueprintCallable)
|
||||
static bool MyFunc_NoRef(FVector Location, int32 Value) { return false; }
|
||||
```
|
||||
|
||||
## 蓝图效果:
|
||||
|
||||
可以见到MyFunc_NoAutoCreateRefTerm的函数会产生编译的报错,因为是引用参数但是却没有连接,导致引用缺少实参。
|
||||
|
||||

|
||||
|
||||
## 原理代码:
|
||||
|
||||
```cpp
|
||||
void UEdGraphSchema_K2::GetAutoEmitTermParameters(const UFunction* Function, TArray<FString>& AutoEmitParameterNames)
|
||||
{
|
||||
AutoEmitParameterNames.Reset();
|
||||
|
||||
const FString& MetaData = Function->GetMetaData(FBlueprintMetadata::MD_AutoCreateRefTerm);
|
||||
if (!MetaData.IsEmpty())
|
||||
{
|
||||
MetaData.ParseIntoArray(AutoEmitParameterNames, TEXT(","), true);
|
||||
|
||||
for (int32 NameIndex = 0; NameIndex < AutoEmitParameterNames.Num();)
|
||||
{
|
||||
FString& ParameterName = AutoEmitParameterNames[NameIndex];
|
||||
ParameterName.TrimStartAndEndInline();
|
||||
if (ParameterName.IsEmpty())
|
||||
{
|
||||
AutoEmitParameterNames.RemoveAtSwap(NameIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
++NameIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow any params that are blueprint defined to be autocreated:
|
||||
if (!FBlueprintEditorUtils::IsNativeSignature(Function))
|
||||
{
|
||||
for ( TFieldIterator<FProperty> ParamIter(Function, EFieldIterationFlags::Default);
|
||||
ParamIter && (ParamIter->PropertyFlags & CPF_Parm);
|
||||
++ParamIter)
|
||||
{
|
||||
FProperty* Param = *ParamIter;
|
||||
if(Param->HasAnyPropertyFlags(CPF_ReferenceParm))
|
||||
{
|
||||
AutoEmitParameterNames.Add(Param->GetName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
还有在
|
||||
void UK2Node_CallFunction::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
|
||||
{
|
||||
if ( Function )
|
||||
{
|
||||
TArray<FString> AutoCreateRefTermPinNames;
|
||||
CompilerContext.GetSchema()->GetAutoEmitTermParameters(Function, AutoCreateRefTermPinNames);
|
||||
const bool bHasAutoCreateRefTerms = AutoCreateRefTermPinNames.Num() != 0;
|
||||
|
||||
for (UEdGraphPin* Pin : Pins)
|
||||
{
|
||||
const bool bIsRefInputParam = Pin && Pin->PinType.bIsReference && (Pin->Direction == EGPD_Input) && !CompilerContext.GetSchema()->IsMetaPin(*Pin);
|
||||
if (!bIsRefInputParam)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool bHasConnections = Pin->LinkedTo.Num() > 0;
|
||||
const bool bCreateDefaultValRefTerm = bHasAutoCreateRefTerms &&
|
||||
!bHasConnections && AutoCreateRefTermPinNames.Contains(Pin->PinName.ToString());
|
||||
|
||||
if (bCreateDefaultValRefTerm)
|
||||
{
|
||||
const bool bHasDefaultValue = !Pin->DefaultValue.IsEmpty() || Pin->DefaultObject || !Pin->DefaultTextValue.IsEmpty();
|
||||
|
||||
// copy defaults as default values can be reset when the pin is connected
|
||||
const FString DefaultValue = Pin->DefaultValue;
|
||||
UObject* DefaultObject = Pin->DefaultObject;
|
||||
const FText DefaultTextValue = Pin->DefaultTextValue;
|
||||
bool bMatchesDefaults = Pin->DoesDefaultValueMatchAutogenerated();
|
||||
|
||||
UEdGraphPin* ValuePin = InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, bHasDefaultValue);
|
||||
if ( ValuePin )
|
||||
{
|
||||
if (bMatchesDefaults)
|
||||
{
|
||||
// Use the latest code to set default value
|
||||
Schema->SetPinAutogeneratedDefaultValueBasedOnType(ValuePin);
|
||||
}
|
||||
else
|
||||
{
|
||||
ValuePin->DefaultValue = DefaultValue;
|
||||
ValuePin->DefaultObject = DefaultObject;
|
||||
ValuePin->DefaultTextValue = DefaultTextValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// since EX_Self does not produce an addressable (referenceable) FProperty, we need to shim
|
||||
// in a "auto-ref" term in its place (this emulates how UHT generates a local value for
|
||||
// native functions; hence the IsNative() check)
|
||||
else if (bHasConnections && Pin->LinkedTo[0]->PinType.PinSubCategory == UEdGraphSchema_K2::PSC_Self && Pin->PinType.bIsConst && !Function->IsNative())
|
||||
{
|
||||
InnerHandleAutoCreateRef(this, Pin, CompilerContext, SourceGraph, /*bForceAssignment =*/true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 128 KiB |
@@ -0,0 +1,122 @@
|
||||
# CustomStructureParam
|
||||
|
||||
- **功能描述:** 被CustomStructureParam标记的函数参数会变成Wildcard的通配符参数,其引脚的类型会等于连接的变量类型。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Blueprint
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
被CustomStructureParam标记的多个函数参数会变成Wildcard的通配符参数,其引脚的类型会等于连接的变量类型。
|
||||
|
||||
CustomStructureParam总是和CustomThunk一起配合使用,这样才能在自己的函数体内来处理泛型的参数类型。
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, CustomThunk, meta = (DisplayName = "PrintStructFields", CustomStructureParam = "inputStruct"))
|
||||
static FString PrintStructFields(const int32& inputStruct) { return TEXT(""); }
|
||||
|
||||
DECLARE_FUNCTION(execPrintStructFields);
|
||||
static FString Generic_PrintStructFields(const UScriptStruct* ScriptStruct, const void* StructData);
|
||||
|
||||
DEFINE_FUNCTION(UMyFunction_Custom::execPrintStructFields)
|
||||
{
|
||||
FString result;
|
||||
Stack.MostRecentPropertyAddress = nullptr;
|
||||
Stack.StepCompiledIn<FStructProperty>(nullptr);
|
||||
|
||||
void* StructData = Stack.MostRecentPropertyAddress;
|
||||
FStructProperty* StructProperty = CastField<FStructProperty>(Stack.MostRecentProperty);
|
||||
UScriptStruct* ScriptStruct = StructProperty->Struct;
|
||||
P_FINISH;
|
||||
P_NATIVE_BEGIN;
|
||||
|
||||
result = Generic_PrintStructFields(ScriptStruct, StructData);
|
||||
|
||||
P_NATIVE_END;
|
||||
*(FString*)RESULT_PARAM = result;
|
||||
}
|
||||
|
||||
FString UMyFunction_Custom::Generic_PrintStructFields(const UScriptStruct* ScriptStruct, const void* StructData)
|
||||
{
|
||||
FString str;
|
||||
for (TFieldIterator<FProperty> i(ScriptStruct); i; ++i)
|
||||
{
|
||||
FString propertyValueString;
|
||||
const void* propertyValuePtr = i->ContainerPtrToValuePtr<const void*>(StructData);
|
||||
i->ExportTextItem_Direct(propertyValueString, propertyValuePtr, nullptr, (UObject*)ScriptStruct, PPF_None);
|
||||
|
||||
str += FString::Printf(TEXT("%s:%s\n"), *i->GetFName().ToString(), *propertyValueString);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 蓝图中的效果:
|
||||
|
||||

|
||||
|
||||
可以看到定义了一个接受通用结构参数的节点,然后打印出内部所有的属性。其中CustomStructureParam 指定函数的参数是自定义的类型。
|
||||
|
||||
源码中的典型例子是
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, CustomThunk, Category = "DataTable", meta=(CustomStructureParam = "OutRow", BlueprintInternalUseOnly="true"))
|
||||
static ENGINE_API bool GetDataTableRowFromName(UDataTable* Table, FName RowName, FTableRowBase& OutRow);
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
首先拥有CustomStructureParam的参数会被识别为Wildcard属性。然后通过FCustomStructureParamHelper来控制Pin->PinType = LinkedTo->PinType;,从而改变Pin的实际类型。
|
||||
|
||||
```cpp
|
||||
bool UEdGraphSchema_K2::IsWildcardProperty(const FProperty* Property)
|
||||
{
|
||||
UFunction* Function = Property->GetOwner<UFunction>();
|
||||
|
||||
return Function && ( UK2Node_CallArrayFunction::IsWildcardProperty(Function, Property)
|
||||
|| UK2Node_CallFunction::IsStructureWildcardProperty(Function, Property->GetFName())
|
||||
|| UK2Node_CallFunction::IsWildcardProperty(Function, Property)
|
||||
|| FEdGraphUtilities::IsArrayDependentParam(Function, Property->GetFName()) );
|
||||
}
|
||||
|
||||
static void FCustomStructureParamHelper::HandleSinglePin(UEdGraphPin* Pin)
|
||||
{
|
||||
if (Pin)
|
||||
{
|
||||
if (Pin->LinkedTo.Num() > 0)
|
||||
{
|
||||
UEdGraphPin* LinkedTo = Pin->LinkedTo[0];
|
||||
check(LinkedTo);
|
||||
|
||||
if (UK2Node* Node = Cast<UK2Node>(Pin->GetOwningNode()))
|
||||
{
|
||||
ensure(
|
||||
!LinkedTo->PinType.IsContainer() ||
|
||||
Node->DoesWildcardPinAcceptContainer(Pin)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
ensure( !LinkedTo->PinType.IsContainer() );
|
||||
}
|
||||
|
||||
Pin->PinType = LinkedTo->PinType;
|
||||
}
|
||||
else
|
||||
{
|
||||
// constness and refness are controlled by our declaration
|
||||
// but everything else needs to be reset to default wildcard:
|
||||
const bool bWasRef = Pin->PinType.bIsReference;
|
||||
const bool bWasConst = Pin->PinType.bIsConst;
|
||||
|
||||
Pin->PinType = FEdGraphPinType();
|
||||
Pin->PinType.bIsReference = bWasRef;
|
||||
Pin->PinType.bIsConst = bWasConst;
|
||||
Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
|
||||
Pin->PinType.PinSubCategory = NAME_None;
|
||||
Pin->PinType.PinSubCategoryObject = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 69 KiB |
@@ -0,0 +1,144 @@
|
||||
# DeterminesOutputType
|
||||
|
||||
- **功能描述:** 指定一个参数的类型作为函数动态调整输出参数类型的参考类型
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Blueprint
|
||||
- **元数据类型:** string="abc"
|
||||
- **限制类型:** Class或Object指针类型,或容器类型
|
||||
- **关联项:** [DynamicOutputParam](../DynamicOutputParam.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定一个参数的类型作为函数输出参数的类型。
|
||||
|
||||
假定这么一个函数原型:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "A",DynamicOutputParam="P1,P2"))
|
||||
TypeR MyFunc(TypeA A,Type1 P1,Type2 P2,Type3 P3);
|
||||
```
|
||||
|
||||
DeterminesOutputType的值指定了一个函数参数名称,即A。其TypeA的类型必须是Class或Object,一般是TSubClassOf<XXX> 或者XXX* ,当然也可以是TArray<XXX*>,还可以是指向参数结构里的某个属性。如Args_ActorClassType。TSoftObjectPtr<XXX>也是可以的,指向一个子类Asset对象,然后输出的基类Asset*就可以相应改变。
|
||||
|
||||
所谓输出参数包括返回值和函数的输出参数,因此上述函数原型里的TypeR,P1,P2都是输出参数。为了让输出参数的类型也相应变化,TypeR、Type1和Type2的类型也得是Class或Object类型,且A参数在蓝图节点上实际选择的类型必须是输出参数类型的子类,这样才能自动转换过去。
|
||||
|
||||
如果没有P1和P2,只把返回值当作TypeR,则可以不指定DynamicOutputParam也可以自动默认把返回值当作动态的输出参数。否则则需要手动书写DynamicOutputParam来指定哪些函数参数来支持动态类型。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyAnimalActor :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyCatActor :public AMyAnimalActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyDogActor :public AMyAnimalActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FMyOutputTypeArgs
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
int32 MyInt = 1;
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere)
|
||||
TSubclassOf<AMyAnimalActor> ActorClassType;
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyFunctionLibrary_OutputTypeTest :public UBlueprintFunctionLibrary
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//class
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ActorClassType"))
|
||||
static TArray<AActor*> MyGetAnimals(TSubclassOf<AMyAnimalActor> ActorClassType);
|
||||
|
||||
//have to add DynamicOutputParam
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ActorClassType", DynamicOutputParam = "OutActors"))
|
||||
static void MyGetAnimalsOut(TSubclassOf<AMyAnimalActor> ActorClassType, TArray<AActor*>& OutActors);
|
||||
|
||||
//have to add DynamicOutputParam
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ActorClassType", DynamicOutputParam = "FirstOutActor,OutActors"))
|
||||
static void MyGetAnimalsOut2(TSubclassOf<AMyAnimalActor> ActorClassType, AActor*& FirstOutActor, TArray<AActor*>& OutActors);
|
||||
|
||||
//object
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ExampleActor"))
|
||||
static TArray<AActor*> MyGetAnimalsWithActor(AMyAnimalActor* ExampleActor);
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "ExampleActorArray"))
|
||||
static TArray<AActor*> MyGetAnimalsWithActorArray(TArray<AMyAnimalActor*> ExampleActorArray);
|
||||
|
||||
//struct property
|
||||
UFUNCTION(BlueprintCallable, meta = (DeterminesOutputType = "Args_ActorClassType"))
|
||||
static TArray<AActor*> MyGetAnimalsWithStructProperty(const FMyOutputTypeArgs& Args);
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 蓝图中效果:
|
||||
|
||||
用返回值当作输出参数的例子,注意到返回值类型实际变成了TArray<AMyCatActor*>。
|
||||
|
||||

|
||||
|
||||
也可以加上DynamicOutputParam来指定输出参数作为动态类型参数:
|
||||
|
||||

|
||||
|
||||
DynamicOutputParam可以指定多个参数
|
||||
|
||||

|
||||
|
||||
DeterminesOutputType 的参数类型也可以是Object或者Object的容器:
|
||||
|
||||

|
||||
|
||||
DeterminesOutputType 的参数甚至可以是结构里的某个属性,但是只有SplitStruct的时候才生效,因为这个时候结构的属性变量才变成函数的Pin,才可以进行DeterminesOutputType的名称比对。这个时候要书写成“A_B”,而不是“A.B”。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
DeterminesOutputType的作用机制是根据这个名称去函数蓝图节点上查找Pin,这个Pin得是Class或Object类型的(容器也行),因为必须是这二者才支持指针类型的转换。这个Pin在蓝图节点上是会由各种TypePicker来实际指定值,比如ClassPicker或ObjectPicker。之后根据TypePicker选择的值,就可以相应的调整DynamicOutputParam指定的参数的类型(或返回参数),真正发挥类型改变的是
|
||||
|
||||
Pin->PinType.PinSubCategoryObject = PickedClass;这一句。
|
||||
|
||||
```cpp
|
||||
void FDynamicOutputHelper::ConformOutputType() const
|
||||
{
|
||||
if (IsTypePickerPin(MutatingPin))
|
||||
{
|
||||
UClass* PickedClass = GetPinClass(MutatingPin);
|
||||
UK2Node_CallFunction* FuncNode = GetFunctionNode();
|
||||
|
||||
// See if there is any dynamic output pins
|
||||
TArray<UEdGraphPin*> DynamicPins;
|
||||
GetDynamicOutPins(FuncNode, DynamicPins);
|
||||
|
||||
// Set the pins class
|
||||
for (UEdGraphPin* Pin : DynamicPins)
|
||||
{
|
||||
if (ensure(Pin != nullptr))
|
||||
{
|
||||
Pin->PinType.PinSubCategoryObject = PickedClass;//设定每个动态参数的子类型
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 102 KiB |
@@ -0,0 +1,9 @@
|
||||
# DynamicOutputParam
|
||||
|
||||
- **功能描述:** 配合DeterminesOutputType,指定多个支持动态类型的输出参数。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
- **限制类型:** Class或Object指针类型,或容器类型
|
||||
- **关联项:** [DeterminesOutputType](DeterminesOutputType/DeterminesOutputType.md)
|
||||
|
||||
常常和DeterminesOutputType一起配合。动态参数的数量可以为多个。
|
@@ -0,0 +1,41 @@
|
||||
# HideSpawnParms
|
||||
|
||||
- **功能描述:** 在UGamelayTask子类生成的蓝图异步节点上隐藏UGamelayTask子类继承链中某些属性。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **元数据类型:** strings="a,b,c"
|
||||
- **关联项:** [ExposedAsyncProxy](../../ExposedAsyncProxy/ExposedAsyncProxy.md)
|
||||
|
||||
在UGamelayTask子类生成的蓝图异步节点上隐藏UGamelayTask子类继承链中某些属性。
|
||||
|
||||
HideSpawnParms 只在UK2Node_LatentGameplayTaskCall中判断,因此只作用于UGameplayTask的子类。在源码中找到的唯一用法是 HideSpawnParms = "Instigator”,但是其UGamelayTask子类继承链中并无该属性,因此其实是不发挥作用的。
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, Meta = (HidePin = "OwningAbility", DefaultToSelf = "OwningAbility", BlueprintInternalUseOnly = "true", HideSpawnParms = "Instigator"), Category = "Ability|Tasks")
|
||||
static UAbilityTask_StartAbilityState* StartAbilityState(UGameplayAbility* OwningAbility, FName StateName, bool bEndCurrentState = true);
|
||||
```
|
||||
|
||||
保留和去掉HideSpawnParms 的蓝图的节点都为:
|
||||
|
||||

|
||||
|
||||
## 源码里发生的位置:
|
||||
|
||||
```cpp
|
||||
void UK2Node_LatentGameplayTaskCall::CreatePinsForClass(UClass* InClass)
|
||||
{
|
||||
// Tasks can hide spawn parameters by doing meta = (HideSpawnParms="PropertyA,PropertyB")
|
||||
// (For example, hide Instigator in situations where instigator is not relevant to your task)
|
||||
|
||||
TArray<FString> IgnorePropertyList;
|
||||
{
|
||||
UFunction* ProxyFunction = ProxyFactoryClass->FindFunctionByName(ProxyFactoryFunctionName);
|
||||
|
||||
const FString& IgnorePropertyListStr = ProxyFunction->GetMetaData(FName(TEXT("HideSpawnParms")));
|
||||
|
||||
if (!IgnorePropertyListStr.IsEmpty())
|
||||
{
|
||||
IgnorePropertyListStr.ParseIntoArray(IgnorePropertyList, TEXT(","), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,8 @@
|
||||
# MapKeyParam
|
||||
|
||||
- **功能描述:** 指定一个函数参数为Map的Key,其根据MapParam指定的实际Map参数的Key类型而相应改变。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **元数据类型:** string="abc"
|
||||
- **限制类型:** TMap
|
||||
- **关联项:** [MapParam](MapParam.md)
|
||||
- **常用程度:** ★★★
|
@@ -0,0 +1,92 @@
|
||||
# MapParam
|
||||
|
||||
- **功能描述:** 指定一个函数为使用TMap<TKey,TValue>的函数,元素类型为通配符的泛型。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **引擎模块:** Blueprint
|
||||
- **元数据类型:** string="abc"
|
||||
- **限制类型:** TMap
|
||||
- **关联项:** [MapKeyParam](MapKeyParam.md), [MapValueParam](MapValueParam.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定一个函数为使用TMap<TKey,TValue>的函数,元素类型为通配符的泛型。
|
||||
|
||||
只能支持一个MapParam,源码中的实现是只根据一个名字来FindPin。
|
||||
|
||||
在源码中,例子都是在UBlueprintMapLibrary中使用。
|
||||
|
||||
## 测试代码1:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintPure, CustomThunk, meta = (MapParam = "TargetMap"))
|
||||
static int32 MyMap_Count(const TMap<int32, int32>& TargetMap);
|
||||
static int32 GenericMyMap_Count(const void* TargetMap, const FMapProperty* MapProperty);
|
||||
DECLARE_FUNCTION(execMyMap_Count);
|
||||
```
|
||||
|
||||
## 蓝图中效果1:
|
||||
|
||||

|
||||
|
||||
因为只支持一个MapParam,因此如果你书写这种代码 。
|
||||
|
||||
## 测试代码2:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintPure, CustomThunk, meta = (MapParam = "MapA,MapB"))
|
||||
static int32 MyMap_CompareSize(const TMap<int32, int32>& MapA, const TMap<int32, int32>& MapB);
|
||||
static int32 GenericMyMap_CompareSize(void* MapA, const FMapProperty* MapAProp, void* MapB, const FMapProperty* MapBProp);
|
||||
DECLARE_FUNCTION(execMyMap_CompareSize);
|
||||
```
|
||||
|
||||
会导致MapParam搜索不到Pin,从而失去通配符的功能。
|
||||
|
||||

|
||||
|
||||
而如果要实现类似Add的功能,达到Key和Value的Pin类型也可以动态的根据Map的类型而自动的改变。则需要加上MapKeyParam 和MapValueParam 分别的指定另外的函数参数以便能找到正确的Pin,从而实现动态的根据Map类型而更改KeyValue Pin类型。MapKeyParam 和MapValueParam 指定的参数也可以为数组等容器,可以参照UBlueprintMapLibrary中的Keys和Values参数。
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, CustomThunk, meta = (MapParam = "TargetMap",MapKeyParam = "Key", MapValueParam = "Value"))
|
||||
static bool MyMap_FindOrAdd(const TMap<int32, int32>& TargetMap, const int32& Key, const int32& Value);
|
||||
static bool GenericMyMap_FindOrAdd(const void* TargetMap, const FMapProperty* MapProperty, const void* KeyPtr, const void* ValuePtr);
|
||||
DECLARE_FUNCTION(execMyMap_FindOrAdd);
|
||||
```
|
||||
|
||||
## 蓝图中的效果2:
|
||||
|
||||

|
||||
|
||||
## 原理代码:
|
||||
|
||||
```cpp
|
||||
void UK2Node_CallFunction::ConformContainerPins()
|
||||
{
|
||||
//在这其中检测容器Pin
|
||||
const FString& MapPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapParam);
|
||||
const FString& MapKeyPinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapKeyParam);
|
||||
const FString& MapValuePinMetaData = TargetFunction->GetMetaData(FBlueprintMetadata::MD_MapValueParam);
|
||||
|
||||
if(!MapPinMetaData.IsEmpty() || !MapKeyPinMetaData.IsEmpty() || !MapValuePinMetaData.IsEmpty() )
|
||||
{
|
||||
// if the map pin has a connection infer from that, otherwise use the information on the key param and value param:
|
||||
bool bReadyToPropagateKeyType = false;
|
||||
FEdGraphTerminalType KeyTypeToPropagate;
|
||||
bool bReadyToPropagateValueType = false;
|
||||
FEdGraphTerminalType ValueTypeToPropagate;
|
||||
|
||||
UEdGraphPin* MapPin = MapPinMetaData.IsEmpty() ? nullptr : FindPin(MapPinMetaData);
|
||||
UEdGraphPin* MapKeyPin = MapKeyPinMetaData.IsEmpty() ? nullptr : FindPin(MapKeyPinMetaData);
|
||||
UEdGraphPin* MapValuePin = MapValuePinMetaData.IsEmpty() ? nullptr : FindPin(MapValuePinMetaData);
|
||||
|
||||
TryReadTypeToPropagate(MapPin, bReadyToPropagateKeyType, KeyTypeToPropagate);//读取MapPin的Key连接类型
|
||||
TryReadValueTypeToPropagate(MapPin, bReadyToPropagateValueType, ValueTypeToPropagate);//读取MapPin上连接的Map Value类型
|
||||
TryReadTypeToPropagate(MapKeyPin, bReadyToPropagateKeyType, KeyTypeToPropagate);//读取KeyPin上的连接类型
|
||||
TryReadTypeToPropagate(MapValuePin, bReadyToPropagateValueType, ValueTypeToPropagate);//读取ValuePin上的连接类型
|
||||
|
||||
TryPropagateType(MapPin, KeyTypeToPropagate, bReadyToPropagateKeyType);//改变MapPin的Key当前类型
|
||||
TryPropagateType(MapKeyPin, KeyTypeToPropagate, bReadyToPropagateKeyType);//改变KeyPin的当前类型
|
||||
|
||||
TryPropagateValueType(MapPin, ValueTypeToPropagate, bReadyToPropagateValueType);//改变MapPin的Value当前类型
|
||||
TryPropagateType(MapValuePin, ValueTypeToPropagate, bReadyToPropagateValueType);//改变ValuePin的当前类型
|
||||
}
|
||||
}
|
||||
```
|
@@ -0,0 +1,8 @@
|
||||
# MapValueParam
|
||||
|
||||
- **功能描述:** 指定一个函数参数为Map的Value,其根据MapParam指定的实际Map参数的Value类型而相应改变。
|
||||
- **使用位置:** UFUNCTION
|
||||
- **元数据类型:** string="abc"
|
||||
- **限制类型:** TMap
|
||||
- **关联项:** [MapParam](../MapParam/MapParam.md)
|
||||
- **常用程度:** ★★★
|
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 64 KiB |
After Width: | Height: | Size: 45 KiB |