Client players are unable to move their characters in build but are able to in PIE

In my game I overrode the “spawn default pawn for” using blueprints to spawn the clients’ selected characters then call a c++ function to posses their activate character and hide the inactive characters. It is working as expected in the PIE, but in the build version of the game I’m having issues.


void ABrawlersOfDualityPlayerController::TeamInit()

{

ServerTeamInit();

}

void ABrawlersOfDualityPlayerController::ServerTeamInit_Implementation()

{

if(TeamCharacters.Num() < 3) return;

TeamCharacters[0]->SetOwner(this);

TeamCharacters[1]->SetOwner(this);

TeamCharacters[2]->SetOwner(this);

TeamCharacters[0]->Controller = this;

TeamCharacters[1]->Controller = this;

TeamCharacters[2]->Controller = this;

TeamCharacters[0]->BrawlerPlayerController = this;

TeamCharacters[1]->BrawlerPlayerController = this;

TeamCharacters[2]->BrawlerPlayerController = this;

TeamCharacters[1]->ServerSwapOut();

TeamCharacters[2]->ServerSwapOut();

GetCharacter();

Possess(TeamCharacters[0]);

bTeamInitialized = true;

}
In the build version of my game the client player is unable to move the character, but the character can still attack and change animation states. The attacks are bound like this in the character class:
PlayerInputComponent->BindAction(“LightAttack”, IE_Released, this, &ABasePlayableCharacter::LightAttackButtonReleased);
While the movement is bound like this:
PlayerInputComponent->BindAxis(“MoveForward”, this, &ABasePlayableCharacter::MoveForward);
and can be called from the player controller for mouse and touch input in the playertick
PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
if(bInputPressed)
{
FollowTime += DeltaTime;

	// Look for the touch location
	FVector HitLocation = FVector::ZeroVector;
	FHitResult Hit;
	if(bIsTouch)
	{
		GetHitResultUnderFinger(ETouchIndex::Touch1, ECC_Visibility, true, Hit);
	}
	else
	{
		GetHitResultUnderCursor(ECC_Visibility, true, Hit);
	}
	HitLocation = Hit.Location;

	// Direct the Pawn towards that location

	if(bTeamInitialized && TeamCharacters.Num() > 0)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 0.016f,FColor::Red,FString(TEXT("Calling move forward")));
		}
		FVector WorldDirection = (HitLocation - TeamCharacters[0]->GetActorLocation()).GetSafeNormal();
		
		TeamCharacters[0]->MoveForward((float) WorldDirection.X);
		TeamCharacters[0]->MoveRight((float) WorldDirection.Y);
	}
}
else
{
	FollowTime = 0.f;
}

}
I need help figuring out how to solve this issue, and if there is a faster way than packaging the build and downloading it on another computer to test it.
Thanks!

Try to print out the TeamCharacters.Num() before you call return.
Something like this

if (GEngine) {
	GEngine->AddOnScreenDebugMessage(-1, 20, FColor::Cyan, FString::Printf(TEXT("%i TeamCharacters"), TeamCharacters.Num()));
}

(You may need to do a development build to not strip out GEngine message access)

I’m guessing you might have less than 3 and your function is ending too abruptly.

I did:
void ABrawlersOfDualityPlayerController::ServerTeamInit_Implementation()
{

if(TeamCharacters.Num() < 3)
{
	if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 20, FColor::Cyan, FString::Printf(TEXT("%i TeamCharacters"), TeamCharacters.Num()));
	return;
} 
TeamCharacters[0]->SetOwner(this);
TeamCharacters[1]->SetOwner(this);
TeamCharacters[2]->SetOwner(this);
TeamCharacters[0]->Controller = this;
TeamCharacters[1]->Controller = this;
TeamCharacters[2]->Controller = this;
TeamCharacters[0]->BrawlerPlayerController = this;
TeamCharacters[1]->BrawlerPlayerController = this;
TeamCharacters[2]->BrawlerPlayerController = this;
TeamCharacters[1]->ServerSwapOut();
TeamCharacters[2]->ServerSwapOut();
GetCharacter();
Possess(TeamCharacters[0]);
bTeamInitialized = true; 

}
But nothing printed on the client or host

That means there are less than 3 players and the function ends prematurely.
Move

if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 20, FColor::Cyan, FString::Printf(TEXT("%i TeamCharacters"), TeamCharacters.Num()));

Above if(TeamCharacters.Num() < 3){} to see how many TeamCharacters there are before you enter the if statement.

moved it to before the if(TeamCharacters.Num() < 3){}
the host sees GEngine displaying “3 TeamCharacters” twice, once for the host and once for the client

In ABrawlersOfDualityPlayerController you are assigning the whole team the server controller. I doubt that is what you want. Where is this being callled? Every player should have their own controller and not be the server.

You should override your game mode post login and put your spawning and possesion logic there.

TeamInit() is being called in the game mode’s “SpawnDefaultFor”
Moved the logic out of the server RPC, but the client player is still unable to move their character.
Also when the client player does an aimed attack, their rotation only updates on the client and not server.
Swapping between team characters, which is called from the player controller is working. Starting attacks, which is called in ABasePlayableCharacter, is also working, but the aim is wrong.
The only things that aren’t working as intended is the aiming and moving.

Here’s the logic for moving and doing an aimed attack
void ABrawlersOfDualityPlayerController::PlayerTick(float DeltaTime)
{
Super::PlayerTick(DeltaTime);
if(!IsLocalController()) return;
if(bInputPressed)
{
FollowTime += DeltaTime;

	// Look for the touch location
	FVector HitLocation = FVector::ZeroVector;
	FHitResult Hit;
	if(bIsTouch)
	{
		GetHitResultUnderFinger(ETouchIndex::Touch1, ECC_Visibility, true, Hit);
	}
	else
	{
		GetHitResultUnderCursor(ECC_Visibility, true, Hit);
	}
	HitLocation = Hit.Location;

	// Direct the Pawn towards that location

	if(bTeamInitialized && TeamCharacters.Num() > 0)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 0.016f,FColor::Red,FString(TEXT("Calling move forward")));
		}
		FVector WorldDirection = (HitLocation - TeamCharacters[0]->GetActorLocation()).GetSafeNormal();
		
		TeamCharacters[0]->MoveForward((float) WorldDirection.X);
		TeamCharacters[0]->MoveRight((float) WorldDirection.Y);
	}
}
else
{
	FollowTime = 0.f;
}

}
void ABasePlayableCharacter::MoveForward(float Value)
{
if(bDisableGameplay)
{
return;
}
else if(Controller != nullptr && Value != 0.f)
{
const FRotator YawRotation(0.f, Controller->GetControlRotation().Yaw, 0.f);
const FVector Direction(FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X));
AddMovementInput(Direction, Value);
}
}
void ABasePlayableCharacter::AimForward(float Value)
{
if(bDisableGameplay)
{
return;
}
if (Controller != nullptr && Value != 0.f)
{
const FRotator YawRotation(0.f, Controller->GetControlRotation().Yaw, 0.f);
const FVector Direction(FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X));
bAimForward = true;
AimForwardValue = Value;
}
else
{
bAimForward = false;
}

}
//used for gamepad controls and non aimed mouse and keyboard attacks
void ABasePlayableCharacter::LightAttackButtonReleased()
{
if(bDisableGameplay) return;
if(CombatState == ECombatState::ECS_Idle || CombatState == ECombatState::ECS_Walking)
{
bDisableGameplay = true;
if(bAimForward || bAimRight)
{
GetCharacterMovement()->bOrientRotationToMovement = false;
FVector LookAt(AimForwardValue,AimRightValue,0.f);
SetActorRotation(LookAt.Rotation());
SetCombatState(ECombatState::ECS_LightAim);
}
else
{
SetCombatState(ECombatState::ECS_LightTap);
}
}
else if(CombatState == ECombatState::ECS_Dashing)
{
SetCombatState(ECombatState::ECS_DashLightTap);
}
}
void ABasePlayableCharacter::LookAtMouse()
{
BrawlerPlayerController = BrawlerPlayerController == nullptr ? Cast(Controller) : BrawlerPlayerController;
if (BrawlerPlayerController)
{
UE_LOG(LogTemp, Warning, TEXT(“LookAtMouse”));
FHitResult HitResult;
BrawlerPlayerController->GetHitResultUnderCursor(
ECollisionChannel::ECC_Visibility,
false,
HitResult);

    LookAtLocation(HitResult.ImpactPoint);
}

}
//used for mouse and keyboard controls
void ABasePlayableCharacter::LightAimAttackButtonReleased()
{
if(bDisableGameplay) return;
if(CombatState == ECombatState::ECS_Idle || CombatState == ECombatState::ECS_Walking)
{
LookAtMouse();
bDisableGameplay = true;
SetCombatState(ECombatState::ECS_LightAim);
}
}
I’m having issues understanding when the server is using information from the client copy of the controller vs the server copy of the controller

Removed the Posses(TeamCharacters[0]); lines and it fixed it!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.