5.2 KiB
Raw Blame History

Exec

  • 功能描述: 在特定类里注册一个函数为作为控制台命令,允许接受参数。
  • 元数据类型: bool
  • 引擎模块: Behavior
  • 限制类型: 特定的几个类
  • 作用机制: 在FunctionFlags中加入FUNC_Exec
  • 常用程度: ★★★

一般特定的几个类是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。

测试代码:

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

原理:

根据源码中的流程:

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只能在以上几个类里面

bool UObject::CallFunctionByNameWithArguments(const TCHAR* Str, FOutputDevice& Ar, UObject* Executor, bool bForceCallWithNonExec/*=false*/)
{
	UFunction* Function = FindFunction(Message);//寻找函数
}