Accessing specific Pawn out of PlayerController

Hello Community,
suppose i have defined the following MyCharacter

    UCLASS(Blueprintable)
    class AMyCharacter : public ACharacter
    {
    	GENERATED_UCLASS_BODY()
    
    	UFUNCTION()
            void DoStuff();
    };

and the following MyPlayerController

    UCLASS()
    class AMyPlayerController : public APlayerController
    {
    	GENERATED_UCLASS_BODY()
    
    protected:
    	virtual void SetupInputComponent() override;
    
    	/** Input handler for Key pressed*/
    	void KeyPressed();
    };
    
    void AMyPlayerController::SetupInputComponent()
    {
    	// set up gameplay key bindings
    	Super::SetupInputComponent();
    
    	InputComponent->BindAction("KeyPressed", IE_Pressed, this, &AMyPlayerController::KeyPressed);
    }

In MyGameMode.cpp all appropriate actions are taken to make these two the DefaultPawnClass and PlayerControllerClass.

Question:
How do i access the DoStuff() Method on the spawned MyCharacter currently possesed by MyPlayerController?

If i try to do:

void AMyPlayerController::KeyPressed()
{
		ACharacter* const Character = GetCharacter();
		Character->DoStuff();
}

i get a compile time error because Character has no such DoStuff() Method. (where it is clear why)
For solving this issue i had a quick look at the UnrealTournament UTPlayerController.cpp where i found this snippet:

void AUTPlayerController::MoveForward(float Value)
{
	if (Value != 0.0f && UTCharacter != NULL)
	{
		MovementForwardAxis = Value;
		UTCharacter->MoveForward(Value);
	}
}

Essentially UTCharacter is defined in the PlayerController Class itself and is a pointer to a AUTCharacter which is a subclass of ACharacter. Sadly i could not find where this variable is actually set to point to the AUTCharacter Object possesed by the PlayerController. (This information would probably solve my issue)

So whats the right way to associate the MyPlayerController with MyCharacter?

You need to cast to your class where you got your function defined

 void AMyPlayerController::KeyPressed()
 {
         AMyCharacter* const Character = (AMyCharacter*)GetCharacter();
         Character->DoStuff();
 }

You could also add checks if GetCharacter() is indeed returning your class with if(GetCharacter()->IsA(AMyCharacter::StaticClass())) as wrong cast will crash the game, but if you 100% sure that playercontroller will use only this class then you don’t need to.

Hi ,
thank you for your answer!

First of all, it might be interesting for you that there exists a Cast Template inside of Cast.h which can be used for such cases:

template< class T > T* Cast( UObject* Src )
{
	CATCH_UNSUPPORTED_INTERFACECAST(T);

	return Src && Src->IsA<T>() ? (T*)Src : NULL;
}

Basically it has built in the checks you have mentioned (as you can see).

Nevertheless, i think it would be bad programming style to do this cast in every Method where it is needed. Especially if one PlayerController should be used to posses a wide variety of Characters/Pawns or the Characters/Pawns have many unique methods called from within the PlayerController.

After reading a little bit deeper into the engine code and the UnrealTournament Implementation of UTPlayerController i think i have found a somewhat better solution. I will post this as a Extra Answer to my original Question and would be pleased if you could give your thoughts to the solution (If something is fundamentally wrong, which could cause problems in the long run).

Assume everything is setup as in the original Question, but then make the following changes:

Insert a public SetPawn() override and a private AMyCharacter* into the Class definition of MyPlayerController:

public:

virtual void SetPawn(APawn* InPawn) override;

private:

	UPROPERTY()
	AMyCharacter* MyCharacter;

Implement the SetPawn() Method as follows:

void AMyPlayerController::SetPawn(APawn* InPawn)
{
	Super::SetPawn(InPawn);

    //Setting MyCharacter to the adress of InPawn if InPawn is a MyCharacter
	APawn* Pawn = GetPawn();
	MyCharacter = (Pawn ? Cast<AMyCharacter>(Pawn) : NULL);
}

Change the Implementation of SetupInputComponent() as follows:

void AMyPlayerController::SetupInputComponent()
     {
         // set up default Input Component
         Super::SetupInputComponent();

         // InputComponent setups which are specific to MyCharacter
         InputComponent->BindAction("KeyPressed", IE_Pressed, this, &AMyPlayerController::KeyPressed);
     }

Finally Change the KeyPressed Method into:

 void AMyPlayerController::KeyPressed()
 {
         if(MyCharacter != NULL) MyCharacter->DoStuff();
 }

Main advantages of using the given implementation are:

  • Easily extend the PlayerController to a wide variety of Characters/Pawns just by adding appropriate private Variables and extending SetPawn/SetInputComponent
  • If no GameMode is set (and the default GameMode is used) or if MyGameMode misses propper initialisation of the DefaultPawnClass to MyCharacter (such that the DefaultPawn is used) the PlayerController has a fallback mechanism to support the DefaultPawn Movement (flying around with WASD and mouse look)

Any Thoughts on this Solution?

Calling SetupInputComponent() out of SetPawn() is a REALLY Bad Idea. Because it causes

InputComponent->BindAction()

to create mutliple AB structs inside the InputComponents AB Array. Which all represent the same action.

This leads to multiple calls of the same handler function if the Key is pressed.

Furthermore it is a potential memory leak if one PlayerController posseses many Pawns one after another.

(I Edited the Answer and removed the SetupInputComponent() call)

You need to cast it anyway :stuck_out_tongue: one way or another