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