vault backup: 2024-10-12 17:19:45

This commit is contained in:
2024-10-12 17:19:46 +08:00
parent ff94ddca61
commit 244c0c52f6
960 changed files with 31348 additions and 10 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -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

View File

@@ -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();
```
## 效果展示:
右键可添加自定义实现
![Untitled](Untitled.png)
## 原理:
在C++里调用的时候里面会FindFunctionChecked根据名字寻找。如果蓝图中有找到的话则会调用。如果在蓝图中直接调用则其实是会直接FindFunctionChecked查找蓝图中有定义的话则会被直接找到。
```cpp
void AMyFunction_Default::MyFunc_ImplementableEvent()
{
ProcessEvent(FindFunctionChecked(NAME_AMyFunction_Default_MyFunc_ImplementableEvent),NULL);
}
```

View File

@@ -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");
}
```
## 效果展示:
![Untitled](Untitled.png)
## 原理:
在调用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);
}
```

View File

@@ -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;
```
## 效果展示:
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -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

View File

@@ -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(){}
};
```
## 蓝图展示:
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -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(){}
};
```
## 蓝图中的展示:
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@@ -0,0 +1,160 @@
# Exec
- **功能描述:** 在特定类里注册一个函数为作为控制台命令,允许接受参数。
- **元数据类型:** bool
- **引擎模块:** Behavior
- **限制类型:** 特定的几个类
- **作用机制:** 在FunctionFlags中加入[FUNC_Exec](../../../Flags/EFunctionFlags/FUNC_Exec.md)
- **常用程度:** ★★★
一般特定的几个类是UPlayerInputAPlayerControllerAPawnAHUDAGameModeBaseACheatManagerAGameStateBaseAPlayerCameraManager的子类。
当在视口中输入控制台命令后首先执行到的是UConsole::ConsoleCommand然后是APlayerController::ConsoleCommand然后是UPlayer::ConsoleCommand中间先尝试ViewportClient->Exec可能处理一些编辑器命令然后到达ULocalPlayer::Exec已经处理一些自定义命令了
UGameViewportClientUGameInstanceUPlayer是继承于FExec的因此本身含有一些ExecExec_RuntimeExec_DevExec_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的时候~打开控制台运行结果:
![Untitled](Untitled.png)
## 原理:
根据源码中的流程:
```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::ExecUGameInstance::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);//寻找函数
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -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);
}
```
## 蓝图代码:
![Untitled](Untitled.png)
对于不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;
}
```

View File

@@ -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();
```
## 测试蓝图:
节点上的电脑标记就是意味着只在客户端上运行。
![Untitled](Untitled.png)
结果输出
```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;
}
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -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/)
![Untitled](Untitled.png)
## 测试代码:
```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
![Untitled](Untitled%201.png)
## 测试输出结果:
```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上执行则只会在运行在服务器上不会传递到客户端。

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -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/)
![Untitled](Untitled.png)
## 测试代码:
```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
![Untitled](Untitled.png)
## 测试输出结果:
```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个端都得到了调用。

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@@ -0,0 +1,12 @@
# Reliable
- **功能描述:** 指定一个RPC函数为“可靠的”当遇见网络错误时会重发以保证到达。一般用在逻辑关键的函数上。
- **元数据类型:** bool
- **引擎模块:** Network
- **作用机制:** 在FunctionFlags加入[FUNC_NetReliable](../../../Flags/EFunctionFlags/FUNC_NetReliable.md)
- **常用程度:★★★★★**
指定一个RPC函数为“可靠的”当遇见网络错误时会重发以保证到达。一般用在逻辑关键的函数上。
具体的原理涉及到了重发信息包的逻辑。

View File

@@ -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/)
![Untitled](Untitled.png)
## 测试代码:
```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
![Untitled](Untitled.png)
## 测试输出结果:
```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上执行则只会在运行在服务器上不会传递到客户端。

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

View File

@@ -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

View File

@@ -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)
在源码里一个也没看到使用。

View File

@@ -0,0 +1,9 @@
# Unreliable
- **功能描述:** 指定一个RPC函数为“不可靠的”当遇见网络错误时就会被丢弃。一般用在传播效果表现的函数上就算漏掉也没有关系。
- **元数据类型:** bool
- **引擎模块:** Network
- **常用程度:★★★★★**
指定一个RPC函数为“不可靠的”当遇见网络错误时就会被丢弃。一般用在传播效果表现的函数上就算漏掉也没有关系。

View File

@@ -0,0 +1,118 @@
# WithValidation
- **功能描述:** 指定一个RPC函数在执行前需要验证只有验证通过才可以执行。
- **元数据类型:** bool
- **引擎模块:** Network
- **作用机制:** 在FunctionFlags中加入[FUNC_NetValidate](../../../Flags/EFunctionFlags/FUNC_NetValidate.md)
- **常用程度:★★★★★**
指定一个RPC函数在执行前需要验证只有验证通过才可以执行。
WithValidation实际上可以用于ClientServerNetMulticast的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;
}
```

View File

@@ -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是可以调用的。因此可以理解为这个函数依然暴露到蓝图但是却又被隐藏起来了。不能让用户自己直接调用但是可以在代码里通过查找函数名之类的间接可以调用到。
![Untitled](Untitled.png)
在源码里找到一个示例因此这个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 来阻止生成默认的蓝图节点。
![Untitled](Untitled%201.png)
## 原理:
关于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 就是隐藏掉默认生成的那个。

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -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;
}
```
## 蓝图效果:
![Untitled](Untitled.png)
可以看到即使是用除以0可以自定义报错信息。
最重要的是如果观察.gen.cpp可以对比发现内部不再生成execFoo的函数。

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View File

@@ -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。
![Untitled](Untitled.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -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);
```
蓝图的效果:
![Untitled](Untitled.png)
## 示例代码:
```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;
}
```
## 示例效果:
![Untitled](Untitled%201.png)
打印:
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自动生成的版本:
![Untitled](Untitled%202.png)
实际应该是:然后再手动添加参数。
![Untitled](Untitled%203.png)
ildcard的区别是ildcard的参数是任意类型的但个数是固定好的
![Untitled](Untitled%204.png)
官方添加的和**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]`