GetPawn() in a PlayerController class returns NULL

Hello! I have been trying to make a custom PlayerController class, I guess it is almost done, but I have some issues. When I call

within this function


void Auser_control::MoveUp(float Value)
{
	if (Value != 0.0f && (GetPawn() != NULL))
	{
		FRotator const ControlSpaceRot = GetControlRotation();
		// transform to world space and add it
		GetPawn()->AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Y), Value);
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("NULL")); //ALWAYS RETURNS NULL 
	}
}

always returns NULL. Please any ideas what do I do wrong? The whole code you can see down below.

In a GameMode class I set a default Pawn class and a PlayerController class. Like this:



UCLASS()
class HANDLECAMERA_API AHandleCameraGameMode : public AGameMode
{
	GENERATED_BODY()

public:
	AHandleCameraGameMode(const FObjectInitializer& ObjectInitializer);
	
	
};


 AHandleCameraGameMode::AHandleCameraGameMode(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	DefaultPawnClass = AMyPawn::StaticClass();
	PlayerControllerClass = Auser_control::StaticClass();
}

Within the PlayerController class I have next code:


UCLASS()
class HANDLECAMERA_API Auser_control : public APlayerController
{
	GENERATED_BODY()
	
private:
	UPROPERTY(VisibleDefaultsOnly, Category = "Player Controller")
	USpringArmComponent* SpringArm;
	UPROPERTY(VisibleDefaultsOnly, Category = "Player Controller")
	UCameraComponent* Camera;
	UPROPERTY(VisibleDefaultsOnly, Category = "Player Controller")
	UBoxComponent* CrouchCamera;
	UPROPERTY(VisibleDefaultsOnly, Category = "Player Controller")
	UBoxComponent* BackCamera;

public:
	Auser_control(const FObjectInitializer& ObjectInitializer);

	void MoveUp(float Value);

	virtual void SetupInputComponent() override;
	virtual void SetPawn(APawn* InPawn) override;
};



Auser_control::Auser_control(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	SpringArm = ObjectInitializer.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("SpringArm"));
	SpringArm->TargetArmLength = 100.f;
	SpringArm->bUsePawnControlRotation = false;
	SpringArm->bInheritPitch = true;
	SpringArm->bInheritYaw = true;
	SpringArm->bInheritRoll = true;
	SpringArm->bEnableCameraLag = false;
	SpringArm->bEnableCameraRotationLag = false;

	Camera = ObjectInitializer.CreateDefaultSubobject<UCameraComponent>(this, TEXT("Camera"));
	Camera->AttachTo(SpringArm, USpringArmComponent::SocketName);
	Camera->SetRelativeLocation(FVector(0.f, 0.f, 0.f), false);

	CrouchCamera = ObjectInitializer.CreateDefaultSubobject<UBoxComponent>(this, TEXT("CrouchCamera"));
	SpringArm->AttachTo(CrouchCamera);
}

void Auser_control::SetPawn(APawn* InPawn)
{
	Super::SetPawn(InPawn);
}

void Auser_control::SetupInputComponent()
{
	Super::SetupInputComponent();

	check(InputComponent);

	if (InputComponent != NULL)
	{
		InputComponent->BindAxis("MoveUp", this, &Auser_control::MoveUp);
	}
}

void Auser_control::MoveUp(float Value)
{
	if (Value != 0.0f && (GetPawn() != NULL))
	{
		FRotator const ControlSpaceRot = GetControlRotation();
		// transform to world space and add it
		GetPawn()->AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Y), Value);
	}
	else
	{
		UE_LOG(LogTemp, Warning, TEXT("NULL")); //ALWAYS RETURNS NULL 
	}
}

And my Pawn class looks like this:


UCLASS()
class HANDLECAMERA_API AMyPawn : public APawn
{
	GENERATED_BODY()

private:
	UPROPERTY(VisibleDefaultsOnly, BluePrintReadOnly, meta = (AllowPrivateAccess = "true"))
	class USceneComponent* DummyRoot;
	UPROPERTY(VisibleDefaultsOnly, BluePrintReadOnly, meta = (AllowPrivateAccess = "true"))
	class USkeletalMeshComponent* SkeletalMesh;

public:
	// Sets default values for this pawn's properties
	AMyPawn();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;

	
	
};


AMyPawn::AMyPawn()
{
	DummyRoot = CreateDefaultSubobject<USceneComponent>(TEXT("Dummy0"));
	RootComponent = DummyRoot;

	class USkeletalMesh* TmpSkeletalMesh;
	TmpSkeletalMesh = ConstructorHelpers::FObjectFinderOptional<USkeletalMesh>(TEXT("SkeletalMesh'/Game/MixamoAnimPack/Mixamo_Vanguard/Mesh/Vanguard_by_T__Choonyung.Vanguard_by_T__Choonyung'")).Get();

	SkeletalMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalMesh0"));
	SkeletalMesh->SetSkeletalMesh(TmpSkeletalMesh);
	SkeletalMesh->AttachTo(RootComponent);

 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AMyPawn::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AMyPawn::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}

// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);

}



You’ve set your custom gamemode as the one to use in the project settings right? (or the map settings)

First thing I would check it to make sure it’s actually spawning a pawn at all. Drop a breakpoint next to Super::BeginPlay() in the Pawn Class, and see if it actually hits.

OMG TheJamsh it is you. I have seen your videos from youtube they are pretty helpful and educative. As for my question, yes I have set all in the project settings. The problem was not in the GetPawn() but in my MyPawn custom class. Because APawn class does not assume UPawnMovementComponent by default I was not able to move my pawn until i created a custom UPawnMovementComponent and attached it to MyPawn class.

Thank you for your reply :).

Hey, hope it provides inspiration / help to people :slight_smile:

Yeah you do still have to specify the movement component sub-class to use. Also although I can’t comment fully on this since i don’t know what your end goal is, I would highly recommend doing the input for the pawn in the Pawn itself, and override SetupInputComponent() there instead.

The main reason is because you may want different pawns with different movement styles, so it’s better to keep all that in the Pawn.

GetPawn() returns the pawn that is currently Possessed by the PlayerController. Make sure you have called PlayerController->Possess(myPawn) before trying to call GetPawn

As for general question everything is ok. But I am experiencing new problems. After I had added a custom UPawnMovementComponent I was able to move a camera but not the Pawn. I was gambling with a PlayerController::Possess function trying to attach the Pawn to a camera and without any success. Please any suggestions how to move a camera with the Pawn?


 
if (PawnToPossess)
{
   SpringArm = NewObject<USpringArmComponent>(PawnToPossess, TEXT("SpringArm"));
   CameraBoom->AttachTo(PawnToPossess->GetRootComponent());
   Camera->AttachTo(SpringArm, USpringArmComponent::SocketName);
}


and like that



if (PawnToPossess)
	{
		CameraBoom = NewObject<USpringArmComponent>(PawnToPossess, TEXT("CameraBoom"));
		//CameraBoom = NewObject<USpringArmComponent>(this, USpringArmComponent::StaticClass());
		CameraBoom->RegisterComponent();
		CameraBoom->AttachTo(PawnToPossess->GetRootComponent());
		CameraBoom->bAbsoluteRotation = true; // Don't want arm to rotate when character does
		CameraBoom->TargetArmLength = 800.f;
		CameraBoom->RelativeRotation = FRotator(-60.f, 0.f, 0.f);
		CameraBoom->bDoCollisionTest = false; // Don't want to pull camera in when it collides with level

											  // Create a camera...
		Camera = NewObject<UCameraComponent>(this, UCameraComponent::StaticClass());
		Camera->RegisterComponent();
		Camera->AttachTo(CameraBoom, USpringArmComponent::SocketName);
		Camera->bUsePawnControlRotation = false;
	}


APlayerController



#pragma once

#include "GameFramework/PlayerController.h"
#include "MyPawn.h"
#include "user_control.generated.h"

/**
 * 
 */
UCLASS()
class HANDLECAMERA_API Auser_control : public APlayerController
{
	GENERATED_BODY()
	
public:
	UPROPERTY(VisibleDefaultsOnly, Category = "Player Controller")
	USpringArmComponent* CameraBoom;
	UPROPERTY(VisibleDefaultsOnly, Category = "Player Controller")
	UCameraComponent* Camera;

public:
	Auser_control(const FObjectInitializer& ObjectInitializer);

	void ChangeView();
	void MoveForward(float AxisValue);
	void MoveRight(float AxisValue);

	virtual void BeginPlay() override;

	virtual void SetupInputComponent() override;
	virtual void SetPawn(APawn* InPawn) override;
};

Auser_control::Auser_control(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	CameraBoom = ObjectInitializer.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("SpringArm"));
	CameraBoom->TargetArmLength = 100.f;
	CameraBoom->bUsePawnControlRotation = true;
	CameraBoom->bInheritPitch = false;
	CameraBoom->bInheritYaw = true;
	CameraBoom->bInheritRoll = true;
	CameraBoom->bEnableCameraLag = false;
	CameraBoom->bEnableCameraRotationLag = false;

	Camera = ObjectInitializer.CreateDefaultSubobject<UCameraComponent>(this, TEXT("Camera"));
	Camera->AttachTo(CameraBoom, USpringArmComponent::SocketName);
	Camera->SetRelativeLocation(FVector(100.f, 100.f, 100.f), false);
}

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

void Auser_control::SetPawn(APawn* InPawn)
{
	Super::SetPawn(InPawn);
}

void Auser_control::SetupInputComponent()
{
	Super::SetupInputComponent();

	check(InputComponent);

	if (InputComponent != NULL)
	{
		InputComponent->BindAxis("MoveUp", this, &Auser_control::MoveForward);
		InputComponent->BindAxis("MoveRight", this, &Auser_control::MoveRight);
	}
}

void Auser_control::MoveRight(float AxisValue)
{
	if (AxisValue != 0.0f && (GetPawn() != NULL))
	{
		FRotator const ControlSpaceRot = GetControlRotation();
		// transform to world space and add it
		GetPawn()->AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::Y), AxisValue);
	}
}

void Auser_control::MoveForward(float AxisValue)
{
	if (AxisValue != 0.0f && (GetPawn() != NULL))
	{
		FRotator const ControlSpaceRot = GetControlRotation();
		// transform to world space and add it
		GetPawn()->AddMovementInput(FRotationMatrix(ControlSpaceRot).GetScaledAxis(EAxis::X), AxisValue);
	}
}


MyPawn



#pragma once

#include "GameFramework/Pawn.h"
#include "MyPawnMovementComponent.h"
#include "MyPawn.generated.h"

UCLASS()
class HANDLECAMERA_API AMyPawn : public APawn
{
	GENERATED_BODY()

private:
	UPROPERTY(VisibleDefaultsOnly, BluePrintReadOnly, meta = (AllowPrivateAccess = "true"))
	class USceneComponent* DummyRoot;
	UPROPERTY(VisibleDefaultsOnly, BluePrintReadOnly, meta = (AllowPrivateAccess = "true"))
	class USkeletalMeshComponent* SkeletalMesh;

	class UMyPawnMovementComponent* OurMovementComponent;

public:
	// Sets default values for this pawn's properties
	AMyPawn();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* InputComponent) override;	

	virtual UPawnMovementComponent* GetMovementComponent() const override;
};


#include "HandleCamera.h"
#include "MyPawn.h"


// Sets default values
AMyPawn::AMyPawn()
{
	DummyRoot = CreateDefaultSubobject<USceneComponent>(TEXT("Dummy0"));
	RootComponent = DummyRoot;

	class USkeletalMesh* TmpSkeletalMesh;
	TmpSkeletalMesh = ConstructorHelpers::FObjectFinderOptional<USkeletalMesh>(TEXT("SkeletalMesh'/Game/MixamoAnimPack/Mixamo_Vanguard/Mesh/Vanguard_by_T__Choonyung.Vanguard_by_T__Choonyung'")).Get();

	SkeletalMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("SkeletalMesh0"));
	SkeletalMesh->SetSkeletalMesh(TmpSkeletalMesh);
	SkeletalMesh->AttachTo(RootComponent);

	OurMovementComponent = CreateDefaultSubobject<UMyPawnMovementComponent>(TEXT("CustomMovementComponent"));
	OurMovementComponent->UpdatedComponent = RootComponent;

 	// Set this pawn to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void AMyPawn::BeginPlay()
{
	Super::BeginPlay();
}

// Called every frame
void AMyPawn::Tick( float DeltaTime )
{
	Super::Tick( DeltaTime );

}

// Called to bind functionality to input
void AMyPawn::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
	Super::SetupPlayerInputComponent(InputComponent);
}

UPawnMovementComponent* AMyPawn::GetMovementComponent() const
{
	return OurMovementComponent;
}



Movement component



#pragma once

#include "GameFramework/PawnMovementComponent.h"
#include "MyPawnMovementComponent.generated.h"

/**
 * 
 */
UCLASS()
class HANDLECAMERA_API UMyPawnMovementComponent : public UPawnMovementComponent
{
	GENERATED_BODY()
	
public:
	virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
};


#include "HandleCamera.h"
#include "MyPawnMovementComponent.h"


void UMyPawnMovementComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// Make sure that everything is still valid, and that we are allowed to move.
	if (!PawnOwner || !UpdatedComponent || ShouldSkipUpdate(DeltaTime))
	{
		return;
	}

	// Get (and then clear) the movement vector that we set in ACollidingPawn::Tick
	FVector DesiredMovementThisFrame = ConsumeInputVector().GetClampedToMaxSize(1.0f) * DeltaTime * 150.0f;
	if (!DesiredMovementThisFrame.IsNearlyZero())
	{
		FHitResult Hit;
		SafeMoveUpdatedComponent(DesiredMovementThisFrame, UpdatedComponent->GetComponentRotation(), true, Hit);

		// If we bumped into something, try to slide along it
		if (Hit.IsValidBlockingHit())
		{
			SlideAlongSurface(DesiredMovementThisFrame, 1.f - Hit.Time, Hit.Normal, Hit);
		}
	}
}