# 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的时候~打开控制台运行结果: ![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(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(this) == nullptr || static_cast(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);//寻找函数 } ```