vault backup: 2024-10-12 17:19:45
@@ -0,0 +1,108 @@
|
||||
# BlueprintInternalUseOnly
|
||||
|
||||
- **功能描述:** 指示不应向最终用户公开此函数。蓝图内部调用,不暴露给用户。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint, UHT
|
||||
- **作用机制:** 在Meta中加入[BlueprintInternalUseOnly](../../../../Meta/Blueprint/BlueprintInternalUseOnly.md)、[BlueprintType](../../../../Meta/Blueprint/BlueprintType.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指示不应向最终用户公开此函数。蓝图内部调用,不暴露给用户。
|
||||
|
||||
等价于meta里加上BlueprintInternalUseOnly = true。默认情况下,BlueprintCallable/Pure的函数会生成UK2Node_CallFunction来调用。但BlueprintInternalUseOnly阻止了这一部分。
|
||||
|
||||
典型的用处有二:
|
||||
|
||||
一是在蓝图中隐藏该函数,但因为该函数依然有UFUNCTION,因此可以通过名字来反射调用该函数。虽然该用法比较稀少,但也算是一种用处。
|
||||
|
||||
二是引擎在别的地方会为该函数声明去按照特定的规则创建另一个蓝图函数节点,因此要隐藏掉按照默认规则创建的这个。这种用法就是引擎源码里大量在使用的用法。
|
||||
|
||||
## 示例代码1:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Internal :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//(BlueprintInternalUseOnly = true, BlueprintType = true, ModuleRelativePath = Function/MyFunction_Internal.h)
|
||||
//FunctionFlags: FUNC_Final | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
||||
UFUNCTION(BlueprintCallable, BlueprintInternalUseOnly)
|
||||
void MyFunc_InternalOnly() {}
|
||||
|
||||
//FunctionFlags: FUNC_Final | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void MyFunc_Default() {}
|
||||
};
|
||||
```
|
||||
|
||||
在蓝图中只有MyFunc_Default是可以调用的。因此可以理解为这个函数依然暴露到蓝图,但是却又被隐藏起来了。不能让用户自己直接调用,但是可以在代码里通过查找函数名之类的间接可以调用到。
|
||||
|
||||

|
||||
|
||||
在源码里找到一个示例,因此这个GetLevelScriptActor函数,可以不在蓝图中被调用,但是有可以通过名字查找到。方便生成一个UFunction以被注入到别的地方作为callback
|
||||
|
||||
```cpp
|
||||
ULevelStreaming:
|
||||
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"))
|
||||
ENGINE_API ALevelScriptActor* GetLevelScriptActor();
|
||||
|
||||
然后发现:
|
||||
GetLevelScriptActorNode->SetFromFunction(ULevelStreaming::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(ULevelStreaming, GetLevelScriptActor)));
|
||||
```
|
||||
|
||||
## 示例代码2:
|
||||
|
||||
实现代码就不贴了,可以自己去项目里查看。
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType,meta = (ExposedAsyncProxy = MyAsyncObject,HasDedicatedAsyncNode))
|
||||
class INSIDER_API UMyFunction_Async :public UCancellableAsyncAction
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FDelayOutputPin Loop;
|
||||
|
||||
UPROPERTY(BlueprintAssignable)
|
||||
FDelayOutputPin Complete;
|
||||
|
||||
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject"), Category = "Flow Control")
|
||||
static UMyFunction_Async* DelayLoop(const UObject* WorldContextObject, const float DelayInSeconds, const int Iterations);
|
||||
|
||||
virtual void Activate() override;
|
||||
|
||||
UFUNCTION()
|
||||
static void Test();
|
||||
private:
|
||||
const UObject* WorldContextObject = nullptr;
|
||||
float MyDelay = 0.f;
|
||||
int MyIterations = 0;
|
||||
bool Active = false;
|
||||
|
||||
UFUNCTION()
|
||||
void ExecuteLoop();
|
||||
|
||||
UFUNCTION()
|
||||
void ExecuteComplete();
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||
假如注释掉上述源码的BlueprintInternalUseOnly ,会发现在蓝图里可以有两个DelayLoop。上面的一个是按UBlueprintAsyncActionBase规则生成的,第二个是按普通的蓝图函数规则生成的。明显这种情况下我们并不想同时出现两个来给用户造成困惑。因此要加上BlueprintInternalUseOnly 来阻止生成默认的蓝图节点。
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
关于UBlueprintAsyncActionBase的使用,UK2Node_BaseAsyncTask的函数实现里体现了书写继承于UBlueprintAsyncActionBase的规则,简单来说就是通过static 函数来当作Factory function,然后分析这个Proxy类的Delegate property来当作Pin。
|
||||
|
||||
如果不加BlueprintInternalUseOnly = "true”,则会生成两个函数。下面那个是普通static函数的生成。上面那个是分析UBlueprintAsyncActionBase生成的函数。
|
||||
|
||||
其中识别UBlueprintAsyncActionBase里面static函数作为FactoryFunction的流程是,BlueprintActionDatabaseImpl::GetNodeSpecificActions会触发UK2Node_AsyncAction::GetMenuActions,从而ActionRegistrar.RegisterClassFactoryActions,内部再判断RegisterClassFactoryActions_Utils::IsFactoryMethod(Function, UBlueprintAsyncActionBase)会通过(判断是static函数,并且返回类型是UBlueprintAsyncActionBase的子类对象),继而继续通过回调UBlueprintFunctionNodeSpawner::Create(FactoryFunc);创建一个工厂方法的nodeSpawner。
|
||||
|
||||
因此总结,此时的BlueprintInternalUseOnly 就是隐藏掉默认生成的那个。
|
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,52 @@
|
||||
# CustomThunk
|
||||
|
||||
- **功能描述:** 指定UHT不为该函数生成蓝图调用的辅助函数,而需要用户自定义编写。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** UHT
|
||||
- **作用机制:** 在Meta中加入[CustomThunk](../../../../Meta/UHT/CustomThunk.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
指定UHT不为该函数生成蓝图调用的辅助函数,而需要用户自定义编写。
|
||||
|
||||
这里Thunk的意思就是类似execFoo的函数,需要用户自己定义。
|
||||
|
||||
CustomThunk一般是用于配合函数参数不定的情况,如各种通配符,或者需要自己更细致的自定义的逻辑处理。
|
||||
|
||||
## 测试代码;
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintPure, CustomThunk)
|
||||
static int32 MyFunc_CustomDivide(int32 A, int32 B = 1);
|
||||
|
||||
DECLARE_FUNCTION(execMyFunc_CustomDivide);
|
||||
|
||||
int32 UMyFunction_Custom::MyFunc_CustomDivide(int32 A, int32 B /*= 1*/)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEFINE_FUNCTION(UMyFunction_Custom::execMyFunc_CustomDivide)
|
||||
{
|
||||
P_GET_PROPERTY(FIntProperty, A);
|
||||
P_GET_PROPERTY(FIntProperty, B);
|
||||
|
||||
P_FINISH;
|
||||
|
||||
if (B == 0)
|
||||
{
|
||||
FFrame::KismetExecutionMessage(*FString::Printf(TEXT("Modulo by zero detected: %d %% 0\n%s"), A, *Stack.GetStackTrace()), ELogVerbosity::Warning);
|
||||
*(int32*)RESULT_PARAM = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
*(int32*)RESULT_PARAM = A/B;
|
||||
}
|
||||
```
|
||||
|
||||
## 蓝图效果:
|
||||
|
||||

|
||||
|
||||
可以看到,即使是用除以0,可以自定义报错信息。
|
||||
|
||||
最重要的是如果观察.gen.cpp,可以对比发现内部不再生成execFoo的函数。
|
After Width: | Height: | Size: 160 KiB |
@@ -0,0 +1,88 @@
|
||||
# FieldNotify
|
||||
|
||||
- **功能描述:** 为该函数创建一个FieldNotify的绑定点。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** UHT
|
||||
- **限制类型:** ViewModel里的函数
|
||||
- **常用程度:** ★★★
|
||||
|
||||
为该函数创建一个FieldNotify的绑定点。
|
||||
|
||||
需要注意的是,如果是Get函数则其返回值改变的时候,需要在别的触发改变的地方手动广播事件。正如下面的代码UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHPPercent);所做的。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(BlueprintType)
|
||||
class INSIDER_API UMyViewModel :public UMVVMViewModelBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
protected:
|
||||
UPROPERTY(BlueprintReadWrite, FieldNotify, Getter, Setter, BlueprintSetter = SetHP)
|
||||
float HP = 1.f;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, FieldNotify, Getter, Setter, BlueprintSetter = SetMaxHP)
|
||||
float MaxHP = 100.f;
|
||||
public:
|
||||
float GetHP()const { return HP; }
|
||||
UFUNCTION(BlueprintSetter)
|
||||
void SetHP(float val)
|
||||
{
|
||||
if (UE_MVVM_SET_PROPERTY_VALUE(HP, val))
|
||||
{
|
||||
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHPPercent);
|
||||
}
|
||||
}
|
||||
|
||||
float GetMaxHP()const { return MaxHP; }
|
||||
UFUNCTION(BlueprintSetter)
|
||||
void SetMaxHP(float val)
|
||||
{
|
||||
if (UE_MVVM_SET_PROPERTY_VALUE(MaxHP, val))
|
||||
{
|
||||
UE_MVVM_BROADCAST_FIELD_VALUE_CHANGED(GetHPPercent);
|
||||
}
|
||||
}
|
||||
|
||||
//You need to manually notify that GetHealthPercent changed when CurrentHealth or MaxHealth changed.
|
||||
UFUNCTION(BlueprintPure, FieldNotify)
|
||||
float GetHPPercent() const
|
||||
{
|
||||
return (MaxHP != 0.f) ? HP / MaxHP : 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## 测试效果:
|
||||
|
||||
可见GetHPPercent有生成一个FIELD。
|
||||
|
||||
```cpp
|
||||
//MyViewModel.generated.h
|
||||
#define FID_GitWorkspace_Hello_Source_Insider_Property_MVVM_MyViewModel_h_12_FIELDNOTIFY \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_CLASS_DESCRIPTOR_BEGIN(INSIDER_API ) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_FIELD(HP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_FIELD(MaxHP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_FIELD(GetHPPercent) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD_BEGIN(HP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD(MaxHP) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD(GetHPPercent) \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_ENUM_FIELD_END() \
|
||||
UE_FIELD_NOTIFICATION_DECLARE_CLASS_DESCRIPTOR_END();
|
||||
//MyViewModel.gen.cpp
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_FIELD(UMyViewModel, HP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_FIELD(UMyViewModel, MaxHP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_FIELD(UMyViewModel, GetHPPercent)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENTATION_BEGIN(UMyViewModel)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_ENUM_FIELD(UMyViewModel, HP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_ENUM_FIELD(UMyViewModel, MaxHP)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENT_ENUM_FIELD(UMyViewModel, GetHPPercent)
|
||||
UE_FIELD_NOTIFICATION_IMPLEMENTATION_END(UMyViewModel);
|
||||
```
|
||||
|
||||
## 蓝图效果:
|
||||
|
||||
进度条可以绑定到GetHPPercent。
|
||||
|
||||

|
After Width: | Height: | Size: 212 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 79 KiB |
@@ -0,0 +1,126 @@
|
||||
# Variadic
|
||||
|
||||
- **功能描述:** 标识一个函数可以接受任意类型的多个参数(包括input/output).
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint, UHT
|
||||
- **作用机制:** 在Meta中加入[Variadic](../../../../Meta/Blueprint/Variadic.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
标识一个函数可以接受任意类型的多个参数(包括input/output).
|
||||
|
||||
在源码中搜索应用:然后配合UK2Node_ExecutePythonScript
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, CustomThunk, Category = "Python|Execution", meta=(Variadic, BlueprintInternalUseOnly="true"))
|
||||
static bool ExecutePythonScript(UPARAM(meta=(MultiLine=True)) const FString& PythonScript, const TArray<FString>& PythonInputs, const TArray<FString>& PythonOutputs);
|
||||
DECLARE_FUNCTION(execExecutePythonScript);
|
||||
```
|
||||
|
||||
蓝图的效果:
|
||||
|
||||

|
||||
|
||||
## 示例代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API UMyFunction_Variadic : public UBlueprintFunctionLibrary
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
/*
|
||||
[PrintVariadicFields Function->Struct->Field->Object /Script/Insider.MyFunction_Variadic:PrintVariadicFields]
|
||||
(BlueprintInternalUseOnly = true, BlueprintType = true, CustomThunk = true, ModuleRelativePath = Function/Variadic/MyFunction_Variadic.h, Variadic = )
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, CustomThunk, BlueprintInternalUseOnly, meta = (Variadic))
|
||||
static FString PrintVariadicFields(const TArray<FString>& Inputs, const TArray<FString>& Outputs);
|
||||
DECLARE_FUNCTION(execPrintVariadicFields);
|
||||
};
|
||||
|
||||
FString UMyFunction_Variadic::PrintVariadicFields(const TArray<FString>& Inputs, const TArray<FString>& Outputs)
|
||||
{
|
||||
check(0);
|
||||
return TEXT("");
|
||||
}
|
||||
|
||||
DEFINE_FUNCTION(UMyFunction_Variadic::execPrintVariadicFields)
|
||||
{
|
||||
FString str;
|
||||
|
||||
P_GET_TARRAY_REF(FString, Inputs);
|
||||
P_GET_TARRAY_REF(FString, Outputs);
|
||||
|
||||
for (const FString& PythonInput : Inputs)
|
||||
{
|
||||
Stack.MostRecentPropertyAddress = nullptr;
|
||||
Stack.MostRecentProperty = nullptr;
|
||||
Stack.StepCompiledIn<FProperty>(nullptr);
|
||||
check(Stack.MostRecentProperty && Stack.MostRecentPropertyAddress);
|
||||
|
||||
FProperty* p = CastField<FProperty>(Stack.MostRecentProperty);
|
||||
|
||||
FString propertyValueString;
|
||||
const void* propertyValuePtr = p->ContainerPtrToValuePtr<const void*>(Stack.MostRecentPropertyContainer);
|
||||
|
||||
p->ExportTextItem_Direct(propertyValueString, propertyValuePtr, nullptr, nullptr, PPF_None);
|
||||
|
||||
str += FString::Printf(TEXT("%s:%s\n"), *p->GetFName().ToString(), *propertyValueString);
|
||||
|
||||
}
|
||||
P_FINISH;
|
||||
|
||||
*(FString*)RESULT_PARAM = str;
|
||||
}
|
||||
```
|
||||
|
||||
## 示例效果:
|
||||
|
||||

|
||||
|
||||
打印:
|
||||
|
||||
CallFunc_MakeVector_ReturnValue:(X=1.000000,Y=2.000000,Z=3.000000)
|
||||
CallFunc_MakeLiteralDouble_ReturnValue:456.000000
|
||||
|
||||
## 原理:
|
||||
|
||||
普通的CustomThunk函数还有一些限制,参数名字和个数是在UFuntion里写死的,不能支持动态的个数。
|
||||
|
||||
目前,想使用**Variadic**功能,需要自定义蓝图节点用C++来为**K2Node_CallFunction**添加引脚。
|
||||
|
||||
想必是想要开发来同时实现**K2Node**以及对应的**CustomThunk**+**Variadic**方法,来保证使用上的安全性。
|
||||
|
||||
BlueprintInternalUseOnly也要加上,否则会自动生成普通的蓝图函数,达不到variadic的效果。
|
||||
|
||||
以下是不加BlueprintInternalUseOnly自动生成的版本:
|
||||
|
||||

|
||||
|
||||
实际应该是:然后再手动添加参数。
|
||||
|
||||

|
||||
|
||||
和Wildcard的区别是,Wildcard的参数是任意类型的,但个数是固定好的.
|
||||
|
||||

|
||||
|
||||
官方添加的和**Python**交互的功能 [Added a Blueprint node for calling Python with args](https://link.zhihu.com/?target=https%3A//github.com/EpicGames/UnrealEngine/commit/61d0f65e1cded45ed94f0422eb931f446888e972)
|
||||
|
||||
官方的提交:
|
||||
|
||||
[https://github.com/EpicGames/UnrealEngine/commit/61d0f65e1cded45ed94f0422eb931f446888e972](https://github.com/EpicGames/UnrealEngine/commit/61d0f65e1cded45ed94f0422eb931f446888e972)
|
||||
|
||||
## 注释:
|
||||
|
||||
Implemented variadic function support for Blueprints
|
||||
|
||||
`Variadic functions are required to be a CustomThunk marked with the "Variadic" meta-data. They can then be used from a custom Blueprint node to accept arbitrary arguments at the end of their parameter list (any extra pins added to the node that aren't part of the main function definition will become the variadic payload).
|
||||
|
||||
Variadic arguments aren't type checked, so you need other function input to tell you how many to expect, and for a nativizied function, also what type of arguments you're dealing with.
|
||||
|
||||
#jira UE-84932
|
||||
#rb Dan.OConnor
|
||||
|
||||
[CL 10421401 by Jamie Dale in Dev-Editor branch]`
|