PlayerController spawning and possesing new Character

Hello,

I’ve implemented a method to spawn a new ACharacter and posses it.

Using this code, the controller moves to the new pawn and the old pawn ends its animation of whatever it was doing.

void AMyCppController::SpawnMyCppCharacter()
{
	FActorSpawnParameters SpawnParameters;
	SpawnParameters.Owner = this;
	const FVector Location{FMath::RandRange(SpawnAreaMin.X, SpawnAreaMax.X), FMath::RandRange(SpawnAreaMin.Y, SpawnAreaMax.Y), GetPawn()->GetActorLocation().Z};

	if (AMyCppCharacter* NewMyCppCharacter = GetWorld()->SpawnActor<AMyCppCharacter>(MyCppCharacterClass, Location, GetPawn()->GetActorRotation(), SpawnParameters))
	{
		constexpr float SmoothBlendTime = 0.75f;
		SetViewTargetWithBlend(NewMyCppCharacter, SmoothBlendTime);
		SetPawn(NewMyCppCharacter);
		OnPossess(NewMyCppCharacter);
	}
}

but if I run this one it leaves the previous pawn hanging or running

void AMyCppController::SpawnMyCppCharacter()
{
	FActorSpawnParameters SpawnParameters;
	SpawnParameters.Owner = this;
	const FVector Location{FMath::RandRange(SpawnAreaMin.X, SpawnAreaMax.X), FMath::RandRange(SpawnAreaMin.Y, SpawnAreaMax.Y), GetPawn()->GetActorLocation().Z};

	if (AMyCppCharacter* NewMyCppCharacter = GetWorld()->SpawnActor<AMyCppCharacter>(MyCppCharacterClass, Location, GetPawn()->GetActorRotation(), SpawnParameters))
	{
		OnPossess(NewMyCppCharacter);
	}
}

I’ve noticed that when using the first block of code, when it enters the code in OnPosses it does not call UnPossess();

const bool bNewPawn = (GetPawn() != PawnToPossess);

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

Am I leaving unmanaged controllers? How should I fix it?

Thank you in advance

running as if you are still holding a run button on the keyboard? Could be you need to clear inputs when unpossessing, pretty sure I had some code for that ready years ago let me take a look. * can’t find it immediately I thought it was a method called flush / clear which would be logical on an input component.

That part of controller code is a shitshow at the moment:

[UE5.2.1, Bug report] Controller::Possess broken if Pawn auto possess AI. - #2 by Roy_Wierer.Seda145

*Edit question, why are you not using Possess but OnPossess? What version of UE are you on?

void AController::Possess(APawn* InPawn)
{
	if (!bCanPossessWithoutAuthority && !HasAuthority())
	{
		FMessageLog("PIE").Warning(FText::Format(
			LOCTEXT("ControllerPossessAuthorityOnly", "Possess function should only be used by the network authority for {0}"),
			FText::FromName(GetFName())
			));
		UE_LOG(LogController, Warning, TEXT("Trying to possess %s without network authority! Request will be ignored."), *GetNameSafe(InPawn));
		return;
	}

	REDIRECT_OBJECT_TO_VLOG(InPawn, this);

	APawn* CurrentPawn = GetPawn();

	// A notification is required when the current assigned pawn is not possessed (i.e. pawn assigned before calling Possess)
	const bool bNotificationRequired = (CurrentPawn != nullptr) && (CurrentPawn->GetController() == nullptr);

	// To preserve backward compatibility we keep notifying derived classed for null pawn in case some
	// overrides decided to react differently when asked to possess a null pawn.
	// Default engine implementation is to unpossess the current pawn.
	OnPossess(InPawn);

	// Notify when pawn to possess (different than the assigned one) has been accepted by the native class or notification is explicitly required
	APawn* NewPawn = GetPawn();
	if ((NewPawn != CurrentPawn) || bNotificationRequired)
	{
		ReceivePossess(NewPawn);
		OnNewPawn.Broadcast(NewPawn);
		OnPossessedPawnChanged.Broadcast(bNotificationRequired ? nullptr : CurrentPawn, NewPawn);
	}
	
	TRACE_PAWN_POSSESS(this, InPawn); 
}

Hello Roy,

Thank you for your reply.

running as if you are still holding a run button on the keyboard? Could be you need to clear inputs when unpossessing

Yes, as if he was stuck doing the last animation in which he was captured but on its own. It doesn’t respond to user input so I guess there’s no previous input to clear.

why are you not using Possess but OnPossess? What version of UE are you on?

I’m on 5.2.1. I’m using OnPossess because of the comment in header of the source code from Controller

virtual void Possess(APawn* InPawn) final; // DEPRECATED(4.22, "Possess is marked virtual final as you should now be overriding OnPossess instead")

As it did not use UnPosses on the previous pawn but it’s working as I intended, I wondered if I was doing something unnoticeable that would bite me in the future

1 Like

Sounds like the state machine is stuck on the animation blueprint. Perhaps it holds state like input based movement vectors which don’t update when there is no input or controller. Does it also happen on animation montages? Otherwise I’d take a look at where it could get stuck in a state machine or if it depends on a playercontroller at some point in the logic.

One situation would be if you implement response to “on hold button / on release button” and on release is never reached because you unpossess while holding it. I believe this was an issue on 4.27 and there was some situation which caused my characters to keep on running on an axis binding, though I can’t find any code of it in my repo.

  • Edit you say the problem occurs when you don’t call this in your spawn method:
SetViewTargetWithBlend(NewMyCppCharacter, SmoothBlendTime);
SetPawn(NewMyCppCharacter);

I’m checking it out but my VS is misbehaving, I can make 3 coffees before intellisense is done. brb

Looks like you may override OnPossess but should not be calling OnPossess. Call Possess instead. Reason is that you are missing out on some checks and delegate broadcasts on AController::Possess.

This makes sense because you called SetPawn before possessing, so the controller thinks the current pawn equals the new pawn.

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


///// bNewPawn will be false if you called SetPawn to PawnToPossess first.

Calling SetPawn yourself seems wrong.
*Edit calling SetPawn yourself is wrong. the comment on the declaration of this method states

"/** Setter for Pawn. Normally should only be used internally when possessing/unpossessing a Pawn. */"

This compares AController to APlayerController. AController::Possess automatically calls OnPossess when the time is right:

Does it also happen on animation montages?

I can’t tell, I have yet to enter that field. I’m using the mannequins from starter content so it should not, right?

you say the problem occurs when you don’t call this in your spawn method:

SetViewTargetWithBlend(NewMyCppCharacter, SmoothBlendTime);
SetPawn(NewMyCppCharacter);

This makes sense because you called SetPawn before possessing, so the controller thinks the current pawn equals the new pawn.
Calling SetPawn yourself seems wrong.

Yes, exactly my point.
I’ll try go further into the source code to see what is doing when unpossesing.
I speak from the little knowledge I have but, perhaps the previous pawn should still have a controller so it finishes its animations, doesn’t lose physics, etc?

Yea then it should only be the state machine.

Can you change your code to this:

void AMyCppController::SpawnMyCppCharacter()
{
	FActorSpawnParameters SpawnParameters;
	SpawnParameters.Owner = this;
	const FVector Location{FMath::RandRange(SpawnAreaMin.X, SpawnAreaMax.X), FMath::RandRange(SpawnAreaMin.Y, SpawnAreaMax.Y), GetPawn()->GetActorLocation().Z};

	if (AMyCppCharacter* NewMyCppCharacter = GetWorld()->SpawnActor<AMyCppCharacter>(MyCppCharacterClass, Location, GetPawn()->GetActorRotation(), SpawnParameters))
	{
		Possess(NewMyCppCharacter);
	}
}

Then debug again?

I’m not aware that it loses animations and physics by default without a controller, doesn’t make sense. Actors without the possibility of being controlled can still have physics. Please debug using the above code then we can continue from there.

First thing to debug is to set breakpoints on the Possess and inner methods and confirm that the old pawn is unpossessed properly and the new pawn is possessed properly so we can rule out any controller issues.

Can confirm that unpossessing does not reset physics or alter it in any way.

Ragdoll relevant methods called before unpossess:

GetCharacterMovement()->SetMovementMode(EMovementMode::MOVE_None);
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
GetMesh()->SetCollisionProfileName(UCoreUtils::ECPRagdoll);
GetMesh()->SetAllBodiesBelowSimulatePhysics(PhysicsRootBone, true, true);
GetMesh()->GetAnimInstance()->Montage_Stop(0.2f, nullptr);

Sorry for the delay, I had to recompile.

It behaves the same.
After spawning a new Character, the previous one stays in the last position where it was controlled by the controller in the falling animation.

I have to leave for today, I’ll continue tomorrow.
Thank you for your help.

1 Like

Hello Roy,

Did it work for you?
I get the same behaviour doing it in blueprints

All works fine for me. The only difference I can now think of is that I ported my project from UE4 to 5 and that our meshes are different. Possibly you use a different “base” for your character files… If you create a new blueprint inheriting from ACharacter, do you still have the problem on that?

Did you find new info during this?:

Yes, that screenshot was from a fresh blueprint inheriting from ACharacter.

Perhaps it’s something I don’t notice but I’ve debugging it and it seems to be unpossesing and possesing correctly.

I actually just reproduced your issue using one of my repos, there is a bug going on indeed. I need to figure this out. When I’m running all works fine, the character stops. But I reproduced it when falling. The character gets stuck in a falling animation and doesn’t reach the ground until I possess it.

The most logical assumption now is that the CharacterMovementComponent is told to quit on unpossess, I’m looking into it.

*Edit the CharacterMovementComponent its movement mode remains in “Falling” which seems fine at a first look. I see some oddity on ACharacter::Restart, apparently there is a need to reset jump related data when a character is possessed. Whatever it is for this looks odd:

void ACharacter::Restart()
{
	Super::Restart();

    JumpCurrentCount = 0;
	JumpCurrentCountPreJump = 0;

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

	if (CharacterMovement)
	{
		CharacterMovement->SetDefaultMovementMode();
	}
}

Don’t see yet what UnPossess causes, I’m rebuilding and debugging my old repo. If VS behaves I might find something soon. I’m checking if I can see anything strange on the floor detection, movement itself or gravity values while being unpossessed.

  • Edit found a symptom:

ACharacter::JumpIsAllowedInternal returns true when hanging in the air bugged, but not during a normal jump.

1 Like

Meanwhile, can you check the state machine what could cause a character to keep running in a direction? I believe this is a blendspace between idle and run animation taking in the actual velocity of a character, so I’m surprised to see it stand still and run. Might be a second issue after jump.

This is what I have now.

void AMyCppController::SpawnMyCppCharacter()
{
	AMyCppCharacter* ActualPawn = CastChecked<AMyCppCharacter>(GetPawn());

	FActorSpawnParameters SpawnParameters;
	SpawnParameters.Owner = this;
	SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButDontSpawnIfColliding;

	AMyCppCharacter* NewMyCppCharacter;
	do
	{
		const FVector Location{FMath::RandRange(SpawnAreaMin.X, SpawnAreaMax.X), FMath::RandRange(SpawnAreaMin.Y, SpawnAreaMax.Y), GetPawn()->GetActorLocation().Z};
		NewMyCppCharacter = GetWorld()->SpawnActor<AMyCppCharacter>(MyCppCharacterClass, Location, GetPawn()->GetActorRotation(), SpawnParameters);
	}
	while (NewMyCppCharacter == nullptr);

	// constexpr float SmoothBlendTime = 1.0f;
	// SetViewTargetWithBlend(NewMyCppCharacter, SmoothBlendTime);
	//SetPawn(NewMyCppCharacter);

	Possess(NewMyCppCharacter);
	ActualPawn->GetMovementComponent()->StopMovementImmediately();
}

Writing this after Possess stopped the Characters from running.

ActualPawn->GetMovementComponent()->StopMovementImmediately();

Found it, I’ll bug report it. There’s a hidden bool on the CharacterMovementComponent which allows movement when there is no controller. Why this even exist is beyond me. Tick it and your jump shouldn’t get stuck.

UCharacterMovementComponent::TickComponent

/////

	// Perform input-driven move for any locally-controlled character, and also
	// allow animation root motion or physics to move characters even if they have no controller
	const bool bShouldPerformControlledCharMove = CharacterOwner->IsLocallyControlled() 
	  || (!CharacterOwner->Controller && bRunPhysicsWithNoController)		
	  || (!CharacterOwner->Controller && CharacterOwner->IsPlayingRootMotion());


/////
1 Like

You shouldn’t need this, not a big fan of it. When something is no longer controlled it should still be allowed to move in my opinion. For example when something is slipping on ice it shouldn’t halt as if hitting a brick wall.

Wow! This worked for me too!
Thank you very much for troubleshooting with me.

Understood
Thank you again.

1 Like

If you wish you can vote on my report, hope they fix these things

[UE5.2.1, Bug report] ACharacter gets stuck in jump / falling / running state without controller.

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