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:
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.
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).
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);
}
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)