vault backup: 2024-10-12 17:19:45
@@ -0,0 +1,19 @@
|
||||
# BlueprintCallable
|
||||
|
||||
- **功能描述:** 暴露到蓝图中可被调用
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在FunctionFlags增加[FUNC_BlueprintCallable](../../../../Flags/EFunctionFlags/FUNC_BlueprintCallable.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void MyFunc_BlueprintCallable() {}
|
||||
```
|
||||
|
||||
## 效果展示:
|
||||
|
||||

|
After Width: | Height: | Size: 29 KiB |
@@ -0,0 +1,14 @@
|
||||
# BlueprintGetter
|
||||
|
||||
- **功能描述:** 指定该函数作为属性的自定义Get函数。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在Meta中加入[BlueprintGetter](../../../Meta/Blueprint/BlueprintGetter.md),在FunctionFlags加入[FUNC_BlueprintCallable](../../../Flags/EFunctionFlags/FUNC_BlueprintCallable.md)、[FUNC_BlueprintPure](../../../Flags/EFunctionFlags/FUNC_BlueprintPure.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
指定该函数作为属性的自定义Get函数。
|
||||
|
||||
此说明符隐含BlueprintPure和BlueprintCallable。
|
||||
|
||||
更多可以参考UPROPERTY的BlueprintGetter
|
@@ -0,0 +1,39 @@
|
||||
# BlueprintImplementableEvent
|
||||
|
||||
- **功能描述:** 指定一个函数调用点,可以在蓝图中重载实现。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在FunctionFlags中增加[FUNC_Event](../../../../Flags/EFunctionFlags/FUNC_Event.md)、[FUNC_Native](../../../../Flags/EFunctionFlags/FUNC_Native.md)、[FUNC_BlueprintEvent](../../../../Flags/EFunctionFlags/FUNC_BlueprintEvent.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
指定一个函数调用点,可以在蓝图中重载实现。是一种方便的用来实现C++来调用蓝图函数的方式。
|
||||
|
||||
蓝图中如果没提供实现,调用的话相当于调用空函数。
|
||||
|
||||
BlueprintImplementableEvent也要配合BlueprintCallable使用,如果没加BlueprintCallable的话就只能在CPP里调用,在蓝图会发现找不到Call Function的节点。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
//FunctionFlags: FUNC_Event | FUNC_Public | FUNC_BlueprintCallable | FUNC_BlueprintEvent
|
||||
UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
|
||||
void MyFunc_ImplementableEvent();
|
||||
```
|
||||
|
||||
## 效果展示:
|
||||
|
||||
右键可添加自定义实现
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
在C++里调用的时候,里面会FindFunctionChecked根据名字寻找。如果蓝图中有找到的话,则会调用。如果在蓝图中直接调用,则其实是会直接FindFunctionChecked查找,蓝图中有定义的话则会被直接找到。
|
||||
|
||||
```cpp
|
||||
void AMyFunction_Default::MyFunc_ImplementableEvent()
|
||||
{
|
||||
ProcessEvent(FindFunctionChecked(NAME_AMyFunction_Default_MyFunc_ImplementableEvent),NULL);
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,49 @@
|
||||
# BlueprintNativeEvent
|
||||
|
||||
- **功能描述:** 可以在蓝图总覆盖实现,但是也在C++中提供一个默认实现。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在FunctionFlags中增加[FUNC_Event](../../../../Flags/EFunctionFlags/FUNC_Event.md)、[FUNC_BlueprintEvent](../../../../Flags/EFunctionFlags/FUNC_BlueprintEvent.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
可以在蓝图总覆盖实现,但是也在C++中提供一个默认实现。
|
||||
|
||||
需要在CPP中声明名称与主函数相同的附加函数,但是末尾添加了_Implementation。如果未找到任何蓝图覆盖,该自动生成的代码将调用“ [FunctionName]_Implementation”方法。一般用在OnXXX之类的函数上,在C++提供实现,这样如果蓝图中没有覆盖的时候,就可以默认调用C++中默认实现版本。
|
||||
BlueprintNativeEvent,没加BlueprintCallable的话就只能在CPP里调用,因此一般也要配合加上BlueprintCallable。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
//FunctionFlags: FUNC_Native | FUNC_Event | FUNC_Public | FUNC_BlueprintCallable | FUNC_BlueprintEvent
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent)
|
||||
void MyFunc_NativeEvent();
|
||||
|
||||
void AMyFunction_Default::MyFunc_NativeEvent_Implementation()
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, "MyFunc_NativeEvent_Implementation");
|
||||
}
|
||||
```
|
||||
|
||||
## 效果展示:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
在调用MyFunc_NativeEvent的时候,内部FindFunctionChecked会根据名字查找,如果在蓝图中有定义,则会找到蓝图中的实现版本。否则的话,则会找到execMyFunc_NativeEvent这个实现版本,从而调用MyFunc_NativeEvent_Implementation。
|
||||
|
||||
```cpp
|
||||
DEFINE_FUNCTION(AMyFunction_Default::execMyFunc_NativeEvent)
|
||||
{
|
||||
P_FINISH;
|
||||
P_NATIVE_BEGIN;
|
||||
P_THIS->MyFunc_NativeEvent_Implementation();
|
||||
P_NATIVE_END;
|
||||
}
|
||||
|
||||
void AMyFunction_Default::MyFunc_NativeEvent()
|
||||
{
|
||||
ProcessEvent(FindFunctionChecked(NAME_AMyFunction_Default_MyFunc_NativeEvent),NULL);
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 82 KiB |
@@ -0,0 +1,26 @@
|
||||
# BlueprintPure
|
||||
|
||||
- **功能描述:** 指定作为一个纯函数,一般用于Get函数用来返回值。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在FunctionFlags增加[FUNC_BlueprintCallable](../../../../Flags/EFunctionFlags/FUNC_BlueprintCallable.md)、[FUNC_BlueprintPure](../../../../Flags/EFunctionFlags/FUNC_BlueprintPure.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
指定作为一个纯函数,一般用于Get函数用来返回值。
|
||||
|
||||
- 纯函数是指没有执行引脚的函数,不是指const函数。
|
||||
- 纯函数可以有多个返回值,用引用参数加到函数里就行。
|
||||
- 不能用于void函数,否则会报错“error : BlueprintPure specifier is not allowed for functions with no return value and no output parameters.”
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintPure)
|
||||
int32 GetMyInt()const { return MyInt; }
|
||||
private:
|
||||
int32 MyInt;
|
||||
```
|
||||
|
||||
## 效果展示:
|
||||
|
||||

|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,13 @@
|
||||
# BlueprintSetter
|
||||
|
||||
- **功能描述:** 指定该函数作为属性的自定义Set函数。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Blueprint
|
||||
- **作用机制:** 在Meta中加入[BlueprintSetter](../../../Meta/Blueprint/BlueprintSetter.md),在FunctionFlags中加入[FUNC_BlueprintCallable ](../../../Flags/EFunctionFlags/FUNC_BlueprintCallable.md)
|
||||
- **常用程度:** ★★
|
||||
|
||||
指定该函数作为属性的自定义Set函数。
|
||||
|
||||
此说明符隐含BlueprintCallable。
|
||||
|
||||
更多可以参考UPROPERTY的BlueprintSetter
|
@@ -0,0 +1,32 @@
|
||||
# CallInEditor
|
||||
|
||||
- **功能描述:** 可以在属性细节面板上作为一个按钮来调用该函数。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Editor
|
||||
- **作用机制:** 在Meta中增加[CallInEditor](../../../../Meta/Blueprint/CallInEditor.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
可以在属性细节面板上作为一个按钮来调用该函数。
|
||||
|
||||
该函数写在AActor或UObject子类里都是可以的,只要有对应的属性细节面板。
|
||||
|
||||
注意这一般是处于Editor运行环境的。典型的例子是ASkyLight的Recapture按钮。因此函数里有时会调用编辑器环境下函数。但也要注意不要在runtime下混用了,比较容易出错。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Default :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(CallInEditor)
|
||||
void MyFunc_CallInEditor(){}
|
||||
};
|
||||
```
|
||||
|
||||
## 蓝图展示:
|
||||
|
||||

|
After Width: | Height: | Size: 83 KiB |
@@ -0,0 +1,106 @@
|
||||
# SealedEvent
|
||||
|
||||
- **功能描述:** 无法在子类中覆盖此函数。SealedEvent关键词只能用于事件。对于非事件函数,请将它们声明为static或final,以密封它们。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Behavior
|
||||
- **作用机制:** 在FunctionFlags中添加[FUNC_Final](../../../../Flags/EFunctionFlags/FUNC_Final.md)
|
||||
|
||||
在源码里搜索:发现都是用在网络的函数上
|
||||
|
||||

|
||||
|
||||
## UHT中的处理:
|
||||
|
||||
```cpp
|
||||
//先识别符号
|
||||
[UhtSpecifier(Extends = UhtTableNames.Function, ValueType = UhtSpecifierValueType.Legacy)]
|
||||
private static void SealedEventSpecifier(UhtSpecifierContext specifierContext)
|
||||
{
|
||||
UhtFunction function = (UhtFunction)specifierContext.Type;
|
||||
function.FunctionExportFlags |= UhtFunctionExportFlags.SealedEvent;
|
||||
}
|
||||
|
||||
//再设置标记
|
||||
// Handle the initial implicit/explicit final
|
||||
// A user can still specify an explicit final after the parameter list as well.
|
||||
if (automaticallyFinal || function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.SealedEvent))
|
||||
{
|
||||
function.FunctionFlags |= EFunctionFlags.Final;
|
||||
function.FunctionExportFlags |= UhtFunctionExportFlags.Final | UhtFunctionExportFlags.AutoFinal;
|
||||
}
|
||||
|
||||
再验证:限定只能用在Event上。
|
||||
|
||||
if (FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.SealedEvent) && !FunctionFlags.HasAnyFlags(EFunctionFlags.Event))
|
||||
{
|
||||
this.LogError("SealedEvent may only be used on events");
|
||||
}
|
||||
|
||||
if (FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.SealedEvent) && FunctionFlags.HasAnyFlags(EFunctionFlags.BlueprintEvent))
|
||||
{
|
||||
this.LogError("SealedEvent cannot be used on Blueprint events");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
//Error: "SealedEvent may only be used on events"
|
||||
UFUNCTION(SealedEvent)
|
||||
void MyFunc_SealedEvent() {}
|
||||
|
||||
//Error: "SealedEvent cannot be used on Blueprint events"
|
||||
UFUNCTION(BlueprintCallable,BlueprintImplementableEvent,SealedEvent)
|
||||
void MyFunc_ImplementableEvent();
|
||||
|
||||
//Error: "SealedEvent cannot be used on Blueprint events"
|
||||
UFUNCTION(BlueprintCallable,BlueprintNativeEvent,SealedEvent)
|
||||
void MyFunc_NativeEvent();
|
||||
```
|
||||
|
||||
因此无法用于普通的函数,又无法用于蓝图中的Event。所以既是Event又不是BlueprintEvent的是什么?看源码是只有网络的一些函数。
|
||||
|
||||
通过对比,发现Sealed函数的区别是多了FUNC_Final的标记。但FUNC_Final又不一定必须要以SealedEvent才能添加,exec或普通的BlueprintCallable函数都会添加。但是如果是vitural的函数就不会添加。在UHT中的原理是:
|
||||
|
||||
```cpp
|
||||
private static UhtParseResult ParseUFunction(UhtParsingScope parentScope, UhtToken token)
|
||||
{
|
||||
if (function.FunctionFlags.HasAnyFlags(EFunctionFlags.Net))
|
||||
{
|
||||
// Network replicated functions are always events, and are only final if sealed
|
||||
scopeName = "event";
|
||||
tokenContext.Reset(scopeName);
|
||||
automaticallyFinal = false;
|
||||
}
|
||||
|
||||
// If virtual, remove the implicit final, the user can still specifying an explicit final at the end of the declaration
|
||||
if (function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.Virtual))
|
||||
{
|
||||
automaticallyFinal = false;
|
||||
}
|
||||
// Handle the initial implicit/explicit final
|
||||
// A user can still specify an explicit final after the parameter list as well.
|
||||
if (automaticallyFinal || function.FunctionExportFlags.HasAnyFlags(UhtFunctionExportFlags.SealedEvent))
|
||||
{
|
||||
function.FunctionFlags |= EFunctionFlags.Final;
|
||||
function.FunctionExportFlags |= UhtFunctionExportFlags.Final | UhtFunctionExportFlags.AutoFinal;
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
在自己的C++代码中测试,发现在C++中怎么继承都不会触发编译错误。因此如果想拒绝被继承,还是用C++标准是final关键字。在函数末尾加final。
|
||||
|
||||
E:\P4V\Engine\Source\Editor\KismetCompiler\Private\KismetCompiler.cpp
|
||||
|
||||
```cpp
|
||||
const uint32 OverrideFlagsToCheck = (FUNC_FuncOverrideMatch & ~FUNC_AccessSpecifiers);
|
||||
if ((Context.Function->FunctionFlags & OverrideFlagsToCheck) != (OverridenFunction->FunctionFlags & OverrideFlagsToCheck))
|
||||
{
|
||||
MessageLog.Error(*LOCTEXT("IncompatibleOverrideFlags_Error", "Overriden function is not compatible with the parent function @@. Check flags: Exec, Final, Static.").ToString(), Context.EntryPoint);
|
||||
}
|
||||
```
|
||||
|
||||
在编译的时候检测的是否是重载父类的函数,但因为SealedEvent不作用于普通函数,也不作用于BlueprintEvent,因此感觉只能在C++中继承。
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,30 @@
|
||||
# Category
|
||||
|
||||
- **功能描述:** 在蓝图的右键菜单中为该函数指定类别分组,可以嵌套多级
|
||||
|
||||
- **元数据类型:** strings=“a|b|c”
|
||||
- **引擎模块:** Editor
|
||||
- **作用机制:** 在Meta中加入[Category](../../../Meta/DetailsPanel/Category.md)
|
||||
- **常用程度:** ★★★★★
|
||||
|
||||
在蓝图的右键菜单中为该函数指定类别分组。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Default :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Category = MyCategory)
|
||||
void MyFunc_WithCategory(){}
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void MyFunc_Default(){}
|
||||
};
|
||||
```
|
||||
|
||||
## 蓝图中的展示:
|
||||
|
||||

|
After Width: | Height: | Size: 60 KiB |
@@ -0,0 +1,160 @@
|
||||
# Exec
|
||||
|
||||
- **功能描述:** 在特定类里注册一个函数为作为控制台命令,允许接受参数。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Behavior
|
||||
- **限制类型:** 特定的几个类
|
||||
- **作用机制:** 在FunctionFlags中加入[FUNC_Exec](../../../Flags/EFunctionFlags/FUNC_Exec.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
一般特定的几个类是:UPlayerInput,APlayerController,APawn,AHUD,AGameModeBase,ACheatManager,AGameStateBase,APlayerCameraManager的子类。
|
||||
|
||||
当在视口中输入控制台命令后,首先执行到的是UConsole::ConsoleCommand,然后是APlayerController::ConsoleCommand,然后是UPlayer::ConsoleCommand,中间先尝试ViewportClient->Exec(可能处理一些编辑器命令),然后到达ULocalPlayer::Exec(已经处理一些自定义命令了)。
|
||||
|
||||
UGameViewportClient,UGameInstance,UPlayer是继承于FExec的,因此本身含有一些Exec,Exec_Runtime,Exec_Dev,Exec_Editor的4个虚函数重载。
|
||||
|
||||
其中UEngine::Exec,内部会转发给各个模块来尝试。其中重要的是StaticExec,最后会FSelfRegisteringExec::StaticExec( InWorld, Cmd,Ar )来调用自注册的Exec。
|
||||
|
||||
如果是在编辑器中~执行命令,FConsoleCommandExecutor::ExecInternal,最后也会到ULocalPlayer::Exec。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Exec :public APawn
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//FunctionFlags: FUNC_Final | FUNC_Exec | FUNC_Native | FUNC_Public
|
||||
UFUNCTION(exec)
|
||||
void MyExec();
|
||||
};
|
||||
void AMyFunction_Exec::MyExec()
|
||||
{
|
||||
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, "MyExec");
|
||||
}
|
||||
```
|
||||
|
||||
在PIE的时候~打开控制台运行结果:
|
||||
|
||||

|
||||
|
||||
## 原理:
|
||||
|
||||
根据源码中的流程:
|
||||
|
||||
```cpp
|
||||
bool UGameViewportClient::Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar)
|
||||
{
|
||||
//按顺序ULocalPlayer::Exec_Editor,Exec_Dev,Exec_Runtime,各自判断是否是一些命令
|
||||
if (FExec::Exec(InWorld, Cmd, Ar))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (ProcessConsoleExec(Cmd, Ar, NULL))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (GameInstance && (GameInstance->Exec(InWorld, Cmd, Ar) || GameInstance->ProcessConsoleExec(Cmd, Ar, nullptr)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (GEngine->Exec(InWorld, Cmd, Ar))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UPlayer::Exec( UWorld* InWorld, const TCHAR* Cmd,FOutputDevice& Ar)
|
||||
{
|
||||
// Route through Exec_Dev and Exec_Editor first
|
||||
//按顺序ULocalPlayer::Exec_Editor,Exec_Dev,Exec_Runtime,各自判断是否是一些命令
|
||||
if (FExec::Exec(InWorld, Cmd, Ar))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
AActor* ExecActor = PlayerController;
|
||||
if (!ExecActor)
|
||||
{
|
||||
UNetConnection* NetConn = Cast<UNetConnection>(this);
|
||||
ExecActor = (NetConn && NetConn->OwningActor) ? ToRawPtr(NetConn->OwningActor) : nullptr;
|
||||
}
|
||||
|
||||
if (ExecActor)
|
||||
{
|
||||
// Since UGameViewportClient calls Exec on UWorld, we only need to explicitly
|
||||
// call UWorld::Exec if we either have a null GEngine or a null ViewportClient
|
||||
UWorld* World = ExecActor->GetWorld();
|
||||
check(World);
|
||||
check(InWorld == nullptr || InWorld == World);
|
||||
const bool bWorldNeedsExec = GEngine == nullptr || Cast<ULocalPlayer>(this) == nullptr || static_cast<ULocalPlayer*>(this)->ViewportClient == nullptr;
|
||||
APawn* PCPawn = PlayerController ? PlayerController->GetPawnOrSpectator() : nullptr;
|
||||
if (bWorldNeedsExec && World->Exec(World, Cmd, Ar))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (PlayerController && PlayerController->PlayerInput && PlayerController->PlayerInput->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (ExecActor->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (PCPawn && PCPawn->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (PlayerController && PlayerController->MyHUD && PlayerController->MyHUD->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (World->GetAuthGameMode() && World->GetAuthGameMode()->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (PlayerController && PlayerController->CheatManager && PlayerController->CheatManager->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (World->GetGameState() && World->GetGameState()->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (PlayerController && PlayerController->PlayerCameraManager && PlayerController->PlayerCameraManager->ProcessConsoleExec(Cmd, Ar, PCPawn))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
||||
查找Exec的顺序应该是:
|
||||
|
||||
- UGameInstance::Exec,UGameInstance::ProcessConsoleExec
|
||||
- GEngine->Exec(InWorld, Cmd, Ar)
|
||||
- `UWorld`::Exec,在没有LocalPlayer处理的情况下
|
||||
- `UPlayerInput::ProcessConsoleExec`
|
||||
- `APlayerController::ProcessConsoleExec`
|
||||
- `APawn::ProcessConsoleExec`
|
||||
- `AHUD::ProcessConsoleExec`
|
||||
- `AGameModeBase::ProcessConsoleExec`
|
||||
- `ACheatManager::ProcessConsoleExec`
|
||||
- `AGameStateBase::ProcessConsoleExec`
|
||||
- `APlayerCameraManager::ProcessConsoleExec`
|
||||
|
||||
ProcessConsoleExec内部会调用CallFunctionByNameWithArguments代码:因此确实会限制这种方式声明的Exec只能在以上几个类里面
|
||||
|
||||
```cpp
|
||||
bool UObject::CallFunctionByNameWithArguments(const TCHAR* Str, FOutputDevice& Ar, UObject* Executor, bool bForceCallWithNonExec/*=false*/)
|
||||
{
|
||||
UFunction* Function = FindFunction(Message);//寻找函数
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 110 KiB |
@@ -0,0 +1,99 @@
|
||||
# BlueprintAuthorityOnly
|
||||
|
||||
- **功能描述:** 这个函数只能在拥有网络权限的端上运行。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags中添加[FUNC_BlueprintAuthorityOnly](../../../../Flags/EFunctionFlags/FUNC_BlueprintAuthorityOnly.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
这个函数只能在拥有网络权限的端上运行。HasAuthority::(GetLocalRole() == ROLE_Authority)。共有4种NetRole: ROLE_None(不复制),ROLE_SimulatedProxy(在客户端上模拟的代理),ROLE_AutonomousProxy(在客户端上的匿名代理,接收玩家输入),ROLE_Authority(服务器拥有权限的)。
|
||||
|
||||
因此BlueprintAuthorityOnly限定这个函数只能在服务器上运行,这个“服务器”可以是LS服务器,DS服务器,单机(可以看作没有客户端的服务器)。
|
||||
|
||||
注意在测试的时候需要把该Actor设置为Replicates。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Network :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
//FunctionFlags: FUNC_Final | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
||||
UFUNCTION(BlueprintCallable)
|
||||
void MyFunc_Default();
|
||||
|
||||
//FunctionFlags: FUNC_Final | FUNC_BlueprintAuthorityOnly | FUNC_Native | FUNC_Public | FUNC_BlueprintCallable
|
||||
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly)
|
||||
void MyFunc_BlueprintAuthorityOnly();
|
||||
|
||||
static void PrintFuncStatus(AActor* actor,FString funcName);
|
||||
};
|
||||
|
||||
void AMyFunction_Network::MyFunc_Default()
|
||||
{
|
||||
PrintFuncStatus(this,TEXT("MyFunc_Default"));
|
||||
}
|
||||
|
||||
void AMyFunction_Network::MyFunc_BlueprintAuthorityOnly()
|
||||
{
|
||||
PrintFuncStatus(this,TEXT("MyFunc_BlueprintAuthorityOnly"));
|
||||
}
|
||||
|
||||
void AMyFunction_Network::PrintFuncStatus(AActor* actor, FString funcName)
|
||||
{
|
||||
FString actorName = actor->GetName();
|
||||
|
||||
FString localRoleStr;
|
||||
UEnum::GetValueAsString(actor->GetLocalRole(), localRoleStr);
|
||||
|
||||
FString remoteRoleStr;
|
||||
UEnum::GetValueAsString(actor->GetRemoteRole(), remoteRoleStr);
|
||||
|
||||
FString netModeStr = Insider::NetModeToString(actor->GetNetMode());
|
||||
|
||||
FString str = FString::Printf(TEXT("%s\t%s\t%s\tLocal:%s\tRemote:%s"), *funcName,*actorName, *netModeStr, *localRoleStr, *remoteRoleStr);
|
||||
GEngine->AddOnScreenDebugMessage(-1, 20.f, FColor::Red, str);
|
||||
|
||||
UE_LOG(LogInsider, Display, TEXT("%s"), *str);
|
||||
}
|
||||
```
|
||||
|
||||
## 蓝图代码:
|
||||
|
||||

|
||||
|
||||
对于不Replicated的Actor:
|
||||
|
||||
```cpp
|
||||
MyFunc_Default BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_None
|
||||
MyFunc_Default BP_Network_C_1 NM_Client Local:ROLE_None Remote:ROLE_Authority
|
||||
MyFunc_Default BP_Network_C_1 NM_Client Local:ROLE_None Remote:ROLE_Authority
|
||||
```
|
||||
|
||||
而对于Replicated的Actor,同时有1个S和两个C,运行普通的函数:
|
||||
|
||||
```cpp
|
||||
MyFunc_Default BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
MyFunc_Default BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
MyFunc_Default BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
```
|
||||
|
||||
如果允许的BlueprintAuthorityOnly函数:
|
||||
|
||||
```cpp
|
||||
MyFunc_BlueprintAuthorityOnly BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
```
|
||||
|
||||
结果可见,Default的函数在3个端上都可以运行,而BlueprintAuthorityOnly只能在服务器上运行。而Client上无法运行。
|
||||
|
||||
## 原理:
|
||||
|
||||
```cpp
|
||||
int32 AActor::GetFunctionCallspace( UFunction* Function, FFrame* Stack )
|
||||
{
|
||||
FunctionCallspace::Type Callspace = (LocalRole < ROLE_Authority) && Function->HasAllFunctionFlags(FUNC_BlueprintAuthorityOnly) ? FunctionCallspace::Absorbed : FunctionCallspace::Local;
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 73 KiB |
@@ -0,0 +1,47 @@
|
||||
# BlueprintCosmetic
|
||||
|
||||
- **功能描述:** 此函数为修饰性的,无法在DS上运行。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags中加入[FUNC_BlueprintCosmetic](../../../../Flags/EFunctionFlags/FUNC_BlueprintCosmetic.md)
|
||||
- **常用程度:** ★★★
|
||||
|
||||
这个函数是修饰性的,所谓修饰性是指这个函数的内容是为了展现一些与逻辑无关的内容,比如动画音效特效等。因为DS并没有实际的画面输出,因此这些修饰性的函数是对DS无意义的。因此这些修饰性函数会被无视掉。
|
||||
|
||||
但是也注意在ListenServer或Client上,这二者都会允许运行。因为这两个端都需要画面展示。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UFUNCTION(BlueprintCallable, BlueprintCosmetic)
|
||||
void MyFunc_BlueprintCosmetic();
|
||||
```
|
||||
|
||||
## 测试蓝图:
|
||||
|
||||
节点上的电脑标记就是意味着只在客户端上运行。
|
||||
|
||||

|
||||
|
||||
结果输出
|
||||
|
||||
```cpp
|
||||
MyFunc_BlueprintCosmetic BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
MyFunc_BlueprintCosmetic BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
MyFunc_BlueprintCosmetic BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
```cpp
|
||||
int32 AActor::GetFunctionCallspace( UFunction* Function, FFrame* Stack )
|
||||
{
|
||||
// Dedicated servers don't care about "cosmetic" functions.
|
||||
if (NetMode == NM_DedicatedServer && Function->HasAllFunctionFlags(FUNC_BlueprintCosmetic))
|
||||
{
|
||||
DEBUG_CALLSPACE(TEXT("GetFunctionCallspace Blueprint Cosmetic Absorbed: %s"), *Function->GetName());
|
||||
return FunctionCallspace::Absorbed;
|
||||
}
|
||||
}
|
||||
```
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,48 @@
|
||||
# Client
|
||||
|
||||
- **功能描述:** 在Client-owned的Actor上(PlayerController或Pawn)执行一个RPC函数,只运行在客户端上。对应的实现函数会添加_Implementation后缀。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags加入[FUNC_Net](../../../../Flags/EFunctionFlags/FUNC_Net.md)、[FUNC_NetClient](../../../../Flags/EFunctionFlags/FUNC_NetClient.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
在Client-owned的Actor上(PlayerController或Pawn)执行一个RPC函数,只运行在客户端上。对应的实现函数会添加_Implementation后缀。
|
||||
|
||||
一般用于从Server发送一个RPC到Client。和蓝图里RunOnClient的效果一样。
|
||||
|
||||
所谓Client-owned,参考文档:[https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Networking/Actors/RPCs/](https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Networking/Actors/RPCs/)
|
||||
|
||||

|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_PlayerController :public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Client, Reliable)
|
||||
void MyFunc_RunOnClient();
|
||||
};
|
||||
|
||||
void AMyFunction_PlayerController::MyFunc_RunOnClient_Implementation()
|
||||
{
|
||||
UInsiderLibrary::PrintFuncStatus(this, TEXT("MyFunc_RunOnClient_Implementation"));
|
||||
}
|
||||
```
|
||||
|
||||
测试蓝图:PIE模式,一个ListenServer+2Client
|
||||
|
||||

|
||||
|
||||
## 测试输出结果:
|
||||
|
||||
```cpp
|
||||
MyFunc_Client_Implementation BP_NetworkPC_C_0 NM_Client Local:ROLE_AutonomousProxy Remote:ROLE_Authority
|
||||
OtherClientFunc BP_NetworkPC_C_0 NM_Client Local:ROLE_AutonomousProxy Remote:ROLE_Authority
|
||||
```
|
||||
|
||||
可见,测试代码中取第2个PC,发出一个Run on Client的RPC调用,最终在Client上成功触发。C++定义的函数和蓝图中添加的自定义RunOnClient事件效果是等价的。
|
||||
|
||||
而如果这个函数在Server owned Actor上执行,则只会在运行在服务器上,不会传递到客户端。
|
After Width: | Height: | Size: 167 KiB |
After Width: | Height: | Size: 84 KiB |
@@ -0,0 +1,52 @@
|
||||
# NetMulticast
|
||||
|
||||
- **功能描述:** 定义一个多播RPC函数在服务器和客户端上都执行。对应的实现函数会添加_Implementation后缀。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags中加入[FUNC_Net](../../../../Flags/EFunctionFlags/FUNC_Net.md)、[FUNC_NetMulticast](../../../../Flags/EFunctionFlags/FUNC_NetMulticast.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
定义一个多播RPC函数在服务器和客户端上都执行。对应的实现函数会添加_Implementation后缀。
|
||||
|
||||
RPC执行的规则,参考文档:[https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Networking/Actors/RPCs/](https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Networking/Actors/RPCs/)
|
||||
|
||||

|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Network :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable)
|
||||
void MyFunc_NetMulticast();
|
||||
};
|
||||
|
||||
void AMyFunction_Network::MyFunc_NetMulticast_Implementation()
|
||||
{
|
||||
UInsiderLibrary::PrintFuncStatus(this, TEXT("MyFunc_NetMulticast_Implementation"));
|
||||
}
|
||||
```
|
||||
|
||||
测试蓝图:PIE模式,一个ListenServer+2Client
|
||||
|
||||

|
||||
|
||||
## 测试输出结果:
|
||||
|
||||
```cpp
|
||||
LogInsider: Display: 46715a00 MyFunc_NetMulticast_Implementation BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
LogInsider: Display: 46e65000 MyFunc_NetMulticast_Implementation BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
LogInsider: Display: 29aaaa00 MyFunc_NetMulticast_Implementation BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
|
||||
LogInsider: Display: 4ff44600 OtherMulticastFunc BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
LogInsider: Display: 3bf89b00 OtherMulticastFunc BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
LogInsider: Display: 29d68700 OtherMulticastFunc BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
```
|
||||
|
||||
在一个Server Owned的Actor上,发出Multicast RPC事件调用,可以见到在3个端都得到了调用。
|
After Width: | Height: | Size: 145 KiB |
@@ -0,0 +1,12 @@
|
||||
# Reliable
|
||||
|
||||
- **功能描述:** 指定一个RPC函数为“可靠的”,当遇见网络错误时会重发以保证到达。一般用在逻辑关键的函数上。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags加入[FUNC_NetReliable](../../../Flags/EFunctionFlags/FUNC_NetReliable.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
指定一个RPC函数为“可靠的”,当遇见网络错误时会重发以保证到达。一般用在逻辑关键的函数上。
|
||||
|
||||
具体的原理涉及到了重发信息包的逻辑。
|
@@ -0,0 +1,52 @@
|
||||
# Server
|
||||
|
||||
- **功能描述:** 在Client-owned的Actor上(PlayerController或Pawn)执行一个RPC函数,只运行在服务器上。对应的实现函数会添加_Implementation后缀
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags中加入[FUNC_Net](../../../../Flags/EFunctionFlags/FUNC_Net.md)、[FUNC_NetServer](../../../../Flags/EFunctionFlags/FUNC_NetServer.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
在Client-owned的Actor上(PlayerController或Pawn)执行一个RPC函数,只运行在服务器上。对应的实现函数会添加_Implementation后缀。
|
||||
|
||||
和RunOnServer的效果一样。
|
||||
|
||||
所谓Client-owned,参考文档:[https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Networking/Actors/RPCs/](https://docs.unrealengine.com/4.27/zh-CN/InteractiveExperiences/Networking/Actors/RPCs/)
|
||||
|
||||

|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_PlayerController :public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable)
|
||||
void MyFunc_RunOnServer();
|
||||
};
|
||||
|
||||
void AMyFunction_PlayerController::MyFunc_RunOnServer_Implementation()
|
||||
{
|
||||
UInsiderLibrary::PrintFuncStatus(this, TEXT("MyFunc_RunOnServer_Implementation"));
|
||||
}
|
||||
```
|
||||
|
||||
测试蓝图:PIE模式,一个ListenServer+2Client
|
||||
|
||||

|
||||
|
||||
## 测试输出结果:
|
||||
|
||||
```cpp
|
||||
LogInsider: Display: 5118b400 MyFunc_RunOnServer_Implementation BP_NetworkPC_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_AutonomousProxy
|
||||
LogInsider: Display: 44ec3c00 MyFunc_RunOnServer_Implementation BP_NetworkPC_C_2 NM_ListenServer Local:ROLE_Authority Remote:ROLE_AutonomousProxy
|
||||
|
||||
LogInsider: Display: 49999000 OtherServerFunc BP_NetworkPC_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_AutonomousProxy
|
||||
LogInsider: Display: 4bcbd800 OtherServerFunc BP_NetworkPC_C_2 NM_ListenServer Local:ROLE_Authority Remote:ROLE_AutonomousProxy
|
||||
```
|
||||
|
||||
可见,测试代码中取第2个PC,发出一个Run on Server的RPC调用,最终在Server上成功触发。C++定义的函数和蓝图中添加的自定义RunOnServer事件效果是等价的。
|
||||
|
||||
而如果这个函数在Server owned Actor上执行,则只会在运行在服务器上,不会传递到客户端。
|
After Width: | Height: | Size: 143 KiB |
@@ -0,0 +1,39 @@
|
||||
# ServiceRequest
|
||||
|
||||
- **功能描述:** 此函数为RPC(远程过程调用)服务请求。rpc服务请求
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在Meta中加入[CustomThunk](../../../Meta/UHT/CustomThunk.md),在FunctionFlags加入[FUNC_Net](../../../Flags/EFunctionFlags/FUNC_Net.md)、[FUNC_Event](../../../Flags/EFunctionFlags/FUNC_Event.md)、[FUNC_NetReliable](../../../Flags/EFunctionFlags/FUNC_NetReliable.md)、[FUNC_NetRequest](../../../Flags/EFunctionFlags/FUNC_NetRequest.md)
|
||||
|
||||
在源码里都没看到使用,只搜到
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UTestReplicationStateDescriptor_TestFunctionWithNotReplicatedNonPODParameters : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
// Currently some features such as not replicating all parameters isn't allowed on regular RPCs
|
||||
UFUNCTION(ServiceRequest(Iris))
|
||||
void FunctionWithNotReplicatedNonPODParameters(int Param0, bool Param1, int Param2, UPARAM(NotReplicated) const TArray<FTestReplicationStateDescriptor_TestStructWithRefCArray>& NotReplicatedParam3);
|
||||
void FunctionWithNotReplicatedNonPODParameters_Implementation(int Param0, bool Param1, int Param2, UPARAM(NotReplicated) const TArray<FTestReplicationStateDescriptor_TestStructWithRefCArray>& NotReplicatedParam3);
|
||||
};
|
||||
```
|
||||
|
||||
## UDN回答:
|
||||
|
||||
Alex: Those specifiers were added quite a while ago as a way to mark functions as RPC requests/responses to and from a backend service, the name of which would be given as part of the specifier: UFUNCTION(ServiceRequest(<Endpoint Name>)). However, the feature was never fully implemented, and since then the specifiers have only been used internally (and even then, I don't believe "ServiceResponse" is used at all anymore). This is why there isn't any public documentation or examples available, as they're not formally supported in the engine. You can check out ServiceRequestSpecifier and ServiceResponseSpecifier in UhtFunctionSpecifiers.cs to see how UHT handles these specifiers.
|
||||
|
||||
Mi: 这两个标记是我们用来自由扩展和自己的服务器通信的(例如http request),譬如可以提供自己的NetDriver处理特定标记的ServiceRequest的RPC,自己序列化对应参数发给自己的服务。
|
||||
|
||||
“意思是如果使用引擎的默认实现的话,使用这两个标记是无效的吗?我尝试在服务器或者客户端发起对ServiceRequest标记的ufunction的调用,结果都是会打印错误日志”
|
||||
|
||||
是的,默认的UE client和DS通信的NetDriver的RPC不需要这两个关键字,用了之后会找不到相应处理的NetDriver的实现。
|
||||
|
||||
在Server Owned Actor上调用会出错:LogNet: Warning: UNetDriver::ProcessRemoteFunction: No owning connection for actor BP_Network_C_1. Function MyFunc_ServiceRequest will not be processed.
|
||||
|
||||
在PC上Server调用也会:
|
||||
|
||||
LogRep: Error: Rejected RPC function due to access rights. Object: BP_NetworkPC_C /Game/UEDPIE_0_StartMap.StartMap:PersistentLevel.BP_NetworkPC_C_1, Function: MyFunc_ServiceRequest
|
||||
LogNet: Error: UActorChannel::ProcessBunch: Replicator.ReceivedBunch failed. Closing connection. RepObj: BP_NetworkPC_C /Game/UEDPIE_0_StartMap.StartMap:PersistentLevel.BP_NetworkPC_C_1, Channel: 3
|
@@ -0,0 +1,9 @@
|
||||
# ServiceResponse
|
||||
|
||||
- **功能描述:** 此函数为RPC服务响应。rpc服务回复
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags加入[FUNC_Net](../../../Flags/EFunctionFlags/FUNC_Net.md)、[FUNC_Event](../../../Flags/EFunctionFlags/FUNC_Event.md)、[FUNC_NetReliable](../../../Flags/EFunctionFlags/FUNC_NetReliable.md)、[FUNC_NetResponse](../../../Flags/EFunctionFlags/FUNC_NetResponse.md)
|
||||
|
||||
在源码里一个也没看到使用。
|
@@ -0,0 +1,9 @@
|
||||
# Unreliable
|
||||
|
||||
- **功能描述:** 指定一个RPC函数为“不可靠的”,当遇见网络错误时就会被丢弃。一般用在传播效果表现的函数上,就算漏掉也没有关系。
|
||||
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
指定一个RPC函数为“不可靠的”,当遇见网络错误时就会被丢弃。一般用在传播效果表现的函数上,就算漏掉也没有关系。
|
@@ -0,0 +1,118 @@
|
||||
# WithValidation
|
||||
|
||||
- **功能描述:** 指定一个RPC函数在执行前需要验证,只有验证通过才可以执行。
|
||||
- **元数据类型:** bool
|
||||
- **引擎模块:** Network
|
||||
- **作用机制:** 在FunctionFlags中加入[FUNC_NetValidate](../../../Flags/EFunctionFlags/FUNC_NetValidate.md)
|
||||
- **常用程度:★★★★★**
|
||||
|
||||
指定一个RPC函数在执行前需要验证,只有验证通过才可以执行。
|
||||
|
||||
WithValidation实际上可以用于Client,Server,NetMulticast的RPC函数,但一般来说还是用在Server的最多,因为一般是Server的数据最权威可以进行数据合法性校验。
|
||||
|
||||
## 测试代码:
|
||||
|
||||
```cpp
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_PlayerController :public APlayerController
|
||||
{
|
||||
GENERATED_BODY()
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, Client, Reliable,WithValidation)
|
||||
void MyFunc2_RunOnClient();
|
||||
|
||||
UFUNCTION(BlueprintCallable, Server, Reliable,WithValidation)
|
||||
void MyFunc2_RunOnServer();
|
||||
};
|
||||
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class INSIDER_API AMyFunction_Network :public AActor
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
UFUNCTION(BlueprintCallable, NetMulticast, Reliable,WithValidation)
|
||||
void MyFunc2_NetMulticast();
|
||||
};
|
||||
|
||||
void AMyFunction_PlayerController::MyFunc2_RunOnServer_Implementation()
|
||||
{
|
||||
UInsiderLibrary::PrintFuncStatus(this, TEXT("MyFunc2_RunOnServer_Implementation"));
|
||||
}
|
||||
|
||||
bool AMyFunction_PlayerController::MyFunc2_RunOnServer_Validate()
|
||||
{
|
||||
UInsiderLibrary::PrintFuncStatus(this, TEXT("MyFunc2_RunOnServer_Validate"));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AMyFunction_Network::MyFunc2_NetMulticast_Validate()
|
||||
{
|
||||
UInsiderLibrary::PrintFuncStatus(this, TEXT("MyFunc2_NetMulticast_Validate"));
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## 测试结果:
|
||||
|
||||
```cpp
|
||||
RunOnClient:
|
||||
LogInsider: Display: 815f7800 MyFunc2_RunOnClient_Validate BP_NetworkPC_C_0 NM_Client Local:ROLE_AutonomousProxy Remote:ROLE_Authority
|
||||
LogInsider: Display: 815f7800 MyFunc2_RunOnClient_Implementation BP_NetworkPC_C_0 NM_Client Local:ROLE_AutonomousProxy Remote:ROLE_Authority
|
||||
|
||||
RunOnServer:
|
||||
LogInsider: Display: 7fd11800 MyFunc2_RunOnServer_Validate BP_NetworkPC_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_AutonomousProxy
|
||||
LogInsider: Display: 7fd11800 MyFunc2_RunOnServer_Implementation BP_NetworkPC_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_AutonomousProxy
|
||||
|
||||
Multicast: ServerOwned
|
||||
LogInsider: Display: 947e6400 MyFunc2_NetMulticast_Validate BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
LogInsider: Display: 947e6400 MyFunc2_NetMulticast_Implementation BP_Network_C_1 NM_ListenServer Local:ROLE_Authority Remote:ROLE_SimulatedProxy
|
||||
LogInsider: Display: 8795eb00 MyFunc2_NetMulticast_Validate BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
LogInsider: Display: 8795eb00 MyFunc2_NetMulticast_Implementation BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
LogInsider: Display: 8f6a3700 MyFunc2_NetMulticast_Validate BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
LogInsider: Display: 8f6a3700 MyFunc2_NetMulticast_Implementation BP_Network_C_1 NM_Client Local:ROLE_SimulatedProxy Remote:ROLE_Authority
|
||||
|
||||
```
|
||||
|
||||
## 原理:
|
||||
|
||||
如果加上WithValidation标记,在UHT生成代码的时候就会:
|
||||
|
||||
```cpp
|
||||
DEFINE_FUNCTION(AMyFunction_PlayerController::execMyFunc2_RunOnServer)
|
||||
{
|
||||
P_FINISH;
|
||||
P_NATIVE_BEGIN;
|
||||
if (!P_THIS->MyFunc2_RunOnServer_Validate())
|
||||
{
|
||||
RPC_ValidateFailed(TEXT("MyFunc2_RunOnServer_Validate"));
|
||||
return;
|
||||
}
|
||||
P_THIS->MyFunc2_RunOnServer_Implementation();
|
||||
P_NATIVE_END;
|
||||
}
|
||||
DEFINE_FUNCTION(AMyFunction_PlayerController::execMyFunc2_RunOnClient)
|
||||
{
|
||||
P_FINISH;
|
||||
P_NATIVE_BEGIN;
|
||||
if (!P_THIS->MyFunc2_RunOnClient_Validate())
|
||||
{
|
||||
RPC_ValidateFailed(TEXT("MyFunc2_RunOnClient_Validate"));
|
||||
return;
|
||||
}
|
||||
P_THIS->MyFunc2_RunOnClient_Implementation();
|
||||
P_NATIVE_END;
|
||||
}
|
||||
|
||||
DEFINE_FUNCTION(AMyFunction_Network::execMyFunc2_NetMulticast)
|
||||
{
|
||||
P_FINISH;
|
||||
P_NATIVE_BEGIN;
|
||||
if (!P_THIS->MyFunc2_NetMulticast_Validate())
|
||||
{
|
||||
RPC_ValidateFailed(TEXT("MyFunc2_NetMulticast_Validate"));
|
||||
return;
|
||||
}
|
||||
P_THIS->MyFunc2_NetMulticast_Implementation();
|
||||
P_NATIVE_END;
|
||||
}
|
||||
```
|
@@ -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]`
|