Blueprint Accessible Option for Movement Reset on PController Possess Pawn

Hello Epic Team,
First of all I’d like to thank you guys for the incredible work done with this engine!

Issue:

When Possessing a character with a PController, any movement the AIController was doing before the possess event is immediately stopped. Storing and re-applying velocity/movement input on possess doesn’t prevent the character from pausing for a frame or more.

Question:

Do you guys plan on implementing a Blueprint Accessible option to choose whether or not the character movement should stop immediately when there’s a restart? Usually called on PController Possess Event as well as Character Possess.

More Details:

My main issue being that I can’t make a custom version of Restart functions called by the PController on the Pawn (OnPossess event in PController) because the SetupInput functions from the Pawn are not accessible on the PController.
The Character Restart() can easily be overwrote since it inherits from Pawn.

I’ve been spending a few days on this issue and it seems the best way to handle is to make a custom build from source just for the one line of code in APawn::Restart() that calls Stop Movement Immediately on Character restart (character spawn, possess event by PController, possess event on character).

I have C++ classes in my project but making an entire custom engine build for this one line of code that is buried under multiple inheritances seems a bit overkill.

So I was wondering if it’s planned to add a bool or some other variable easily accessible to modify this behavior?

Thank you for reading!

Solution without altering engine source files
Overriding APawn Functions from MyCharacter and calling these new functions instead of APawn function from MyPlayerController OnPossess event.

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "MyCharacter.generated.h"

UCLASS()
class RUGBYPROJECT_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMyCharacter();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

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

	virtual void Restart() override;
	virtual void PawnClientRestart() override;
	void DispatchRestart(bool bCallClientRestart);

};

MyCharacter.CPP

#include "MyCharacter.h"
#include "GameFramework/InputSettings.h"
#include "Engine/InputDelegateBinding.h"
#include "MyPlayerController.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Character.h"

// Sets default values
AMyCharacter::AMyCharacter()
{
 	// Set this character 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 AMyCharacter::BeginPlay()
{
	Super::BeginPlay();
	
}

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

}

// Called to bind functionality to input
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

}

void AMyCharacter::Restart()
{
	// ACharacter Function
	JumpCurrentCount = 0;
	JumpCurrentCountPreJump = 0;

	bPressedJump = false;
	ResetJumpState();
	UnCrouch(true);

	UCharacterMovementComponent* MyCharacterMovement = GetCharacterMovement();
	if (MyCharacterMovement)
	{
		MyCharacterMovement->SetDefaultMovementMode();
	}

	// A PAWN FUNCTION
	ConsumeMovementInputVector();
	RecalculateBaseEyeHeight();
}

void AMyCharacter::PawnClientRestart()
{
	UCharacterMovementComponent* MyCharacterMovement = GetCharacterMovement();
	// ACharacter Function
	if (MyCharacterMovement != nullptr)
	{
		// CharacterMovement->StopMovementImmediately();
		MyCharacterMovement->ResetPredictionData_Client();
	}

	// APAWN FUNCTION
	Restart();

	AMyPlayerController* PC = Cast<AMyPlayerController>(Controller);
	if (PC && PC->IsLocalController())
	{
		// Handle camera possession
		if (PC->bAutoManageActiveCameraTarget)
		{
			PC->AutoManageActiveCameraTarget(this);
		}

		// Set up player input component, if there isn't one already.
		if (InputComponent == nullptr)
		{
			InputComponent = CreatePlayerInputComponent();
			if (InputComponent)
			{
				SetupPlayerInputComponent(InputComponent);
				InputComponent->RegisterComponent();
				if (UInputDelegateBinding::SupportsInputDelegate(GetClass()))
				{
					InputComponent->bBlockInput = bBlockInput;
					UInputDelegateBinding::BindInputDelegatesWithSubojects(this, InputComponent);
				}
			}
		}
	}
}

void AMyCharacter::DispatchRestart(bool bCallClientRestart)
{
	if (bCallClientRestart)
	{
		// This calls Restart()
		PawnClientRestart();
	}
	else
	{
		Restart();
	}

	NotifyRestarted();
}

MyPlayerController.h

#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "GameFramework/PlayerController.h"
#include "MyPlayerController.generated.h"

/**
 * 
 */
UCLASS()
class RUGBYPROJECT_API AMyPlayerController : public APlayerController
{
	GENERATED_BODY()
	
	virtual void OnPossess(APawn* PawnToPossess) override;
};

MyPlayerController.cpp

#include "MyPlayerController.h"
#include "MyCharacter.h"
#include "GameFramework/PlayerState.h"
#include "Interfaces/NetworkPredictionInterface.h"
#include "GameFramework/PawnMovementComponent.h"

void AMyPlayerController::OnPossess(APawn* PawnToPossess)
{
	AMyCharacter* MyCharacter = Cast<AMyCharacter>(PawnToPossess);
	if (PawnToPossess != NULL &&
		(PlayerState == NULL || !PlayerState->IsOnlyASpectator()))
	{
		const bool bNewPawn = (GetPawn() != PawnToPossess);

		if (GetPawn() && bNewPawn)
		{
			UnPossess();
		}

		if (PawnToPossess->Controller != NULL)
		{
			PawnToPossess->Controller->UnPossess();
		}

		PawnToPossess->PossessedBy(this);

		// update rotation to match possessed pawn's rotation
		SetControlRotation(PawnToPossess->GetActorRotation());

		SetPawn(PawnToPossess);
		check(GetPawn() != NULL);

		if (GetPawn() && GetPawn()->PrimaryActorTick.bStartWithTickEnabled)
		{
			GetPawn()->SetActorTickEnabled(true);
		}

		INetworkPredictionInterface* NetworkPredictionInterface = GetPawn() ? Cast<INetworkPredictionInterface>(GetPawn()->GetMovementComponent()) : NULL;
		if (NetworkPredictionInterface)
		{
			NetworkPredictionInterface->ResetPredictionData_Server();
		}

		AcknowledgedPawn = NULL;

		// Local PCs will have the Restart() triggered right away in ClientRestart (via PawnClientRestart()), but the server should call Restart() locally for remote PCs.
		// We're really just trying to avoid calling Restart() multiple times.
		if (!IsLocalPlayerController())
		{
			MyCharacter->DispatchRestart(false);
		}

		ClientRestart(GetPawn());

		ChangeState(NAME_Playing);
		if (bAutoManageActiveCameraTarget)
		{
			AutoManageActiveCameraTarget(GetPawn());
			ResetCameraMode();
		}
	}
}

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.