There is this big gap in ue4 framework due to which there is no way to know when a pawn is possessed on a client reliably at that exact moment!
I want to run a function on client as soon as the pawn is possessed by a player on start of the game.
The PossessedBy event is fired only on the server that too long before the client version is even spawned on the client , if you make any RPC to the owning client to notify at this time you’ll get a null controller since its not possessed on the client yet.
On the other hand there seems to be no event on the controller to notify it has possessed a pawn either.
If you put a timer you either make miss it if loading takes too long or keep the player waiting if the game loads too fast. This is really bad scenario
I believe you can create a bool for bIsPosessed and set replication to be repNotify to get your desired outcome. You can set bIsPossessed on the server side and once each client receives the update, they will call the repNotify function and you can then call the event or further functionality from there.
the function restart is called on client when it’s possessed atleast when it’s spawned for the first time by game mode. I can’t say if it’s called when it’s unpossessed by a controller and then possessed by another but when it’s spawned by AGameMode it is definitely called on the client and when it is called you’ll have a controller available on it
Yo. I am having a problem with OnRep_Pawn. It triggers immediately when the game starts, but when I change a client’s pawn later by calling possess on the server, OnRep_Pawn doesn’t get called anymore. I checked the Pawn-variable of the client’s controller on both server and the client itself: it changes to the new pawn correctly. And there is no call to OnRep_Pawn…
edit: just noticed this wasn’t the topic I wanted to reply to… but since it’s also pretty much on-topic, i’m gonna leave this here as well.
EDIT: my answer only works for controller changed, not as a substitute of OnPossessed. For that you should use Restart as mentioned.
There are several options to let the client know when a Pawn has been possessed.
You can either use the BlueprintImplementableEvent “ReceiveControllerChanged”, bind to the Delegate “ReceiveControllerChangedDelegate” or override “NotifyControllerChanged”.
/** Event called after a pawn's controller has changed, on the server and owning client. This will happen at the same time as the delegate on GameInstance */
UFUNCTION(BlueprintImplementableEvent)
void ReceiveControllerChanged(AController* OldController, AController* NewController);
/** Event called after a pawn's controller has changed, on the server and owning client. This will happen at the same time as the delegate on GameInstance */
UPROPERTY(BlueprintAssignable, Category = Pawn)
FPawnControllerChangedSignature ReceiveControllerChangedDelegate;
/** Call to notify about a change in controller, on both the server and owning client. This calls the above event and delegate */
virtual void NotifyControllerChanged();
For anyone looking at this in the future, there’s a much more straightforward method, but you need C++.
There’s a OnRep_Pawn method that is called whenever Possession changes, defined in AController. I would recommend just adding the override to be BP Implementable and extend from it there. Not sure why this isn’t default behavior tbh.
Using this, I call my character-specific UI construction after a “islocallycontrolled” check.
Property itself (header and cpp)
ACharacter.h
UPROPERTY(replicatedUsing=OnRep_Pawn) TObjectPtr Pawn;
ACharacter.cpp
/** Replication Notification Callbacks */
UFUNCTION()ENGINE_API virtual void OnRep_Pawn();AController Codevoid AController::OnRep_Pawn(){APawn StrongOldPawn = OldPawn.Get();
// Detect when pawn changes, so we can NULL out the controller on the old pawn
if ((StrongOldPawn != nullptr) && (Pawn != StrongOldPawn) && (StrongOldPawn->GetController() == this))
{
// Set the old controller to NULL, since we are no longer the owner, and can’t rely on it replicating to us anymore
StrongOldPawn->SetController(nullptr);
if (UE::Gameplay::CVars::bAlwaysNotifyClientOnControllerChange)
{
// This will notify other systems like the game instance
StrongOldPawn->OnRep_Controller();}}OldPawn = Pawn; SetPawn(Pawn);
if (StrongOldPawn != Pawn)
{
OnPossessedPawnChanged.Broadcast(StrongOldPawn, Pawn); }
}
Soo… Just create a BP Event from this Rep, and your gamin. Add in a GetPawn if you want for basically a RepNotify OnPossess (or unpossess)
protected:
/** This is the standard override on the replicated Pawn variable
(UPROPERTY) */
virtual void OnRep_Pawn() override;
/* BP Event triggered by OnRep */
UFUNCTION(BlueprintNativeEvent, Category = “Pawn”)
void K2_OnRep_Pawn();
virtual void K2_OnRep_Pawn_Implementation();
void ABGPPlayerController::OnRep_Pawn()
{
Super::OnRep_Pawn();
// Custom event for BP
K2_OnRep_Pawn();
}
void ABGPPlayerController::K2_OnRep_Pawn_Implementation()
{
// This is the base C++ implementation.
// You can leave this empty if you want all logic to be in Blueprint.
}