Download

How do you make a Pawn to move forward using PlayerController?

What I am trying to do is to control my Pawn, so that the Pawn actor will move forward when I press and hold down a key.

I am having a hard time getting my PlayerController to possess my Pawn using Possess(APawn*) function. Whenever I tried doing that, it crashes the UE4 editor if I passed in a null pointer, or the camera in the PIE would look at some place that’s away from my Pawn.

If I need to clarify something more, please do not hesitate and let me know. Thanks in advance.

[HR][/HR]

This is my PlayerController.h header code:



UCLASS()
class GETDEBT_API AMainPlayerController : public APlayerController
{
	GENERATED_BODY()
	
public:
	//Properties
	UPROPERTY(VisibleAnywhere, Category=CustomProperties) AMainCore* MainCorePawn;

	//Constructor
	AMainPlayerController(const FObjectInitializer& ObjectInitializer);
	
	//Tick
	virtual void Tick(float Delta) override;

	//BeginPlay
	virtual void BeginPlay() override;

	//Overriding SetPawn
	virtual void SetPawn(APawn* InPawn) override;

	//Overriding SetupInputComponent
	void SetupInputComponent() override;

	//When key is pressed, just show debug message.
	UFUNCTION() void KeyPressed(float Value);
};


And this is my PlayerController.cpp code:



AMainPlayerController::AMainPlayerController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	this->PrimaryActorTick.bCanEverTick = true;
}

void AMainPlayerController::BeginPlay(){
	Super::BeginPlay();
}

void AMainPlayerController::Tick(float DeltaTime){
	Super::Tick(DeltaTime);
}

void AMainPlayerController::SetPawn(APawn* InPawn){
	Super::SetPawn(InPawn);
	APawn* Pawn = this->GetPawn();
	this->MainCorePawn = (Pawn ? Cast<AMainCore>(Pawn) : nullptr);
}

void AMainPlayerController::SetupInputComponent(){
	Super::SetupInputComponent();
	this->InputComponent->BindAxis("MoveForward", this, &AMainPlayerController::KeyPressed);
}

void AMainPlayerController::KeyPressed(float Value){
	if (this->MainCorePawn != nullptr){
		LOG("PlayerController: " + FString::SanitizeFloat(Value));
	}
}


If you look inside RestartPlayer() of the game mode you can figure out how this is being done already. This step you’re doing can easily be avoided by overriding spawndefaultpawnfor in the game class. Although if you do a word search in the code you should be able to copy paste what parts are already working.


NewPlayer->Possess(NewPlayer->GetPawn());

I am pretty sure you want to set the **PlayerControllerClass **in your **GameMode **first.

If that is done, you can control your pawn using this pseudocode


void AMainPlayerController::KeyPressed(float Value)
{
        APawn* Pawn = this->GetPawn();
        // find out which way is forward
	FRotator Rotation = GetControlRotation();

	// add movement in that direction
	const FVector Direction = FRotationMatrix(Rotation).GetScaledAxis(EAxis::X);//Get the forward vector
	Pawn->AddMovementInput(Direction, Value);
}

It crashes when I tried that. Crashed, as in hard crash and gone. No need to bring up Task Manager and kill it. I added some codes to check to see if GetPawn() returns a nullptr, but it still crashes. Henceforth, I am back to square one.

I tried what you suggested. I even expected to move, as that code is the same as what was taught in the QuickStart tutorial. But actually, the camera won’t move, and I can’t click into the world for some reason. No debug messages were shown onto the screen. No activities, and I did checked to see StartMatch() was called in the StartPlay() function in my GameMode subclass.

Sorry sometimes I write stuff and it’s super early in the morning I’m half awake. Denny he should already have his playercontroller setup since he’s using his playercontroller class to change the Pawn type. I wasn’t talking about using the code directly with NewPlayer->GetPawn() the code you have for SetPawn is already in place. I was just copying a portion of what you need to read over that is specifically in RestartPlayer. I think this is becoming unnecessarily confusing.

If you simply set your player controller and pawn in the constructor of the gamemode class this doesn’t need to be an issue:



	//Set default pawn class to our Blueprinted Character
	static ConstructorHelpers::FObjectFinder<UClass> PlayerPawnBPClass(TEXT("Class'/Game/Character/0BaseCharacter/PCharacter_ST.PCharacter_ST_C'"));

	if (PlayerPawnBPClass.Object != NULL)
	{
		DefaultPawnClass = PlayerPawnBPClass.Object;
	}

	static ConstructorHelpers::FObjectFinder<UBlueprint> PlayerControllerBPClass(TEXT("Blueprint'/Game/Character/0BaseCharacter/PController_ST.PController_ST'"));
	if (PlayerControllerBPClass.Object != NULL)
	{
		PlayerControllerClass = (UClass*)PlayerControllerBPClass.Object->GeneratedClass;
	}


The player controller will get spawned automatically in Login(), checked if it’s been spawned and then Initialize the player at InitNewPlayer().



APlayerController* NewPlayerController = SpawnPlayerController(FVector::ZeroVector, FRotator::ZeroRotator);

When the PostLogin() gets called, it’ll go to StartNewPlayer(), hopefully call RestartPlayer(), find a start spot and then spawn the pawn using SpawnDefaultPawnFor(). If the pawn exists then it’ll be possessed, it’ll then check if it’s null and if not then it’ll access it. It doesn’t have to be done this way but as an example of what is working by default you could look it over.



void AGameMode::RestartPlayer(AController* NewPlayer)
{
	if ( NewPlayer == NULL || NewPlayer->IsPendingKillPending() )
	{
		return;
	}

	UE_LOG(LogGameMode, Verbose, TEXT("RestartPlayer %s"), (NewPlayer && NewPlayer->PlayerState) ? *NewPlayer->PlayerState->PlayerName : TEXT("Unknown"));

	if (NewPlayer->PlayerState && NewPlayer->PlayerState->bOnlySpectator)
	{
		UE_LOG(LogGameMode, Verbose, TEXT("RestartPlayer tried to restart a spectator-only player!"));
		return;
	}

	AActor* StartSpot = FindPlayerStart(NewPlayer);

	// if a start spot wasn't found,
	if (StartSpot == NULL)
	{
		// check for a previously assigned spot
		if (NewPlayer->StartSpot != NULL)
		{
			StartSpot = NewPlayer->StartSpot.Get();
			UE_LOG(LogGameMode, Warning, TEXT("Player start not found, using last start spot"));
		}
		else
		{
			// otherwise abort
			UE_LOG(LogGameMode, Warning, TEXT("Player start not found, failed to restart player"));
			return;
		}
	}
	// try to create a pawn to use of the default class for this player
	if (NewPlayer->GetPawn() == NULL && GetDefaultPawnClassForController(NewPlayer) != NULL)
	{
		NewPlayer->SetPawn(SpawnDefaultPawnFor(NewPlayer, StartSpot));
	}

	if (NewPlayer->GetPawn() == NULL)
	{
		NewPlayer->FailedToSpawnPawn();
	}
	else
	{
		// initialize and start it up
		InitStartSpot(StartSpot, NewPlayer);

		// @todo: this was related to speedhack code, which is disabled.
		/*
		if ( NewPlayer->GetAPlayerController() )
		{
			NewPlayer->GetAPlayerController()->TimeMargin = -0.1f;
		}
		*/
		NewPlayer->Possess(NewPlayer->GetPawn());

		// If the Pawn is destroyed as part of possession we have to abort
		if (NewPlayer->GetPawn() == nullptr)
		{
			NewPlayer->FailedToSpawnPawn();
		}
		else
		{
			// set initial control rotation to player start's rotation
			NewPlayer->ClientSetRotation(NewPlayer->GetPawn()->GetActorRotation(), true);

			FRotator NewControllerRot = StartSpot->GetActorRotation();
			NewControllerRot.Roll = 0.f;
			NewPlayer->SetControlRotation( NewControllerRot );

			SetPlayerDefaults(NewPlayer->GetPawn());
		}
	}

#if !UE_WITH_PHYSICS
	if (NewPlayer->GetPawn() != NULL)
	{
		UCharacterMovementComponent* CharacterMovement = Cast<UCharacterMovementComponent>(NewPlayer->GetPawn()->GetMovementComponent());
		if (CharacterMovement)
		{
			CharacterMovement->bCheatFlying = true;
			CharacterMovement->SetMovementMode(MOVE_Flying);
		}
	}
#endif	//!UE_WITH_PHYSICS
}

So as far as your setup it would probably be something like:



void AMainPlayerController::SetPawn(APawn* InPawn){

	Super::SetPawn(InPawn);

	if (this->GetPawn() == NULL)
	{
		//Failed
		this->FailedToSpawnPawn();
	}
	else{
		// initialize and start it up
		this->Possess(this->GetPawn());

		// If the Pawn is destroyed as part of possession we have to abort
		if (this->GetPawn() == nullptr)
		{
			this->FailedToSpawnPawn();
		}
		else
		{
			// set initial control rotation to player start's rotation
			this->ClientSetRotation(this->GetPawn()->GetActorRotation(), true);

			//FRotator NewControllerRot = StartSpot->GetActorRotation();
			//NewControllerRot.Roll = 0.f;
			this->SetControlRotation(FRotator(0));

			//SetPlayerDefaults(NewPlayer->GetPawn());
			MainCorePawn = this->GetPawn();
		}
	}
}

I haven’t checked it yet for bugs I’ll look into it if it still doesn’t work.

This is always a possibility: https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/PossessPawns/Blueprints/index.html

You could also directly place the pawn in the level through the editor, than iterate all the pawns in the level (using UWorld function GetPawnIterator()) and possess the desired pawn.

I am going to check this and see what I can come up with C++.