SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) 被调用2次

问题

GAS技能输入绑定失效。

直接原因

在ListenServer的一端,ACharacter::SetupPlayerInputComponent在游戏开始后,被调用了2次, 说明第1次输入绑定的InputComponent被移除(注意:没有从内存销毁,能被复用,这也是2次调用InputComponent内存地址一致的原因)。而且第1次的调用本不该存在,由于我用本地变量确保GAS输入只绑定一次,所以第1次绑定失效后,由于第2次调用的绑定操作被终止,导致GAS的输入实际未绑定。

分析

由GameMode生成配置的DefaultPawn的时候:

void AGameModeBase::RestartPlayerAtPlayerStart(AController* NewPlayer, AActor* StartSpot)
{
	// ... ...
	if (NewPlayer->GetPawn() != nullptr)
	{
		// If we have an existing pawn, just use it's rotation
		SpawnRotation = NewPlayer->GetPawn()->GetActorRotation();
	}
	else if (GetDefaultPawnClassForController(NewPlayer) != nullptr)
	{
		// Try to create a pawn to use of the default class for this player
		//注意这里的SpawnDefaultPawnFor
		NewPlayer->SetPawn(SpawnDefaultPawnFor(NewPlayer, StartSpot));
	}

	if (NewPlayer->GetPawn() == nullptr)
	{
		NewPlayer->FailedToSpawnPawn();
	}
	else
	{
		// Tell the start spot it was used
		InitStartSpot(StartSpot, NewPlayer);
		
		FinishRestartPlayer(NewPlayer, SpawnRotation);
	}

在里边调用SpawnDefaultPawnFor时,来到:

void APawn::PreInitializeComponents()
{
	Super::PreInitializeComponents();

	// ... ...
	//注意这里,AutoPossessPlayer的判断
	if (AutoPossessPlayer != EAutoReceiveInput::Disabled && GetNetMode() != NM_Client )
	{
		const int32 PlayerIndex = int32(AutoPossessPlayer.GetValue()) - 1;

		APlayerController* PC = UGameplayStatics::GetPlayerController(this, PlayerIndex);
		if (PC)
		{
			//这里会调用到Pawn的SetupPlayerInputComponent
			PC->Possess(this);
		}
		else
		{
			GetWorld()->PersistentLevel->RegisterActorForAutoReceiveInput(this, PlayerIndex);
		}
	}
	UpdateNavigationRelevance();
}

这里根据AutoPossessPlayer,如果不是Disabled,则会让PC调用Possess,而Possess后续会调用ACharacter::SetupPlayerInputComponent这是本不该有的第1次调用
而在AGameModeBase::RestartPlayerAtPlayerStart里,后续的FinishRestartPlayer(NewPlayer, SpawnRotation);则会再次调用NewPlayer->Possess(NewPlayer->GetPawn()),此时需要注意的是,在后续被调用的APlayerController::OnPossess里,有一下操作:

if (PawnToPossess->Controller != NULL)
{
	PawnToPossess->Controller->UnPossess();
}
//... ...
//这里会在后续调用ACharacter::SetupPlayerInputComponent
ClientRestart(GetPawn());

里边调用UnPossess的时候,会调到:

void APawn::DestroyPlayerInputComponent()
{
	if (InputComponent)
	{
		InputComponent->DestroyComponent();
		InputComponent = nullptr;
	}
}

此时我们第一次绑定输入用的InputComponent被移除。
之后ClientRestart(GetPawn())再次让Pawn绑定输入,此时我的代码阻止了里边对GAS的输入绑定。

解决

由于我的第一次输入绑定被调用是因为AutoPossessPlayer != EAutoReceiveInput::Disabled导致的,所以将它设置为默认的Disabled即可。
image

总结

由GameMode生成Player Character的时候,和直接把Character放到场景中不一样,需要将AutoPossessPlayer保持为默认的Disabled,后续GameMode会给他分配配置的PlayerController,否则可能导致程序异常运行。

posted @ 2022-11-30 20:02  GameSprite  阅读(208)  评论(1编辑  收藏  举报