AIController possession and switching back to PlayerController Issues

Hi, I hope someone can assist with this as I’m struggling to figure out where I have gone wrong.

Mechanic I’m trying to acheive:

Upon seeing (I am using a sweep - geom trace) an object that can be used for hiding (I created a custom trace channel for “hiding place” objects), the character moves to the location and hides.

What works:

The sweep works perfectly, returning the hit location of the hiding place. The character is possessed by the AIController and moves to the stored hit location.

Key Info:
I have setup a custom class called RAIController.

The issues:

There appears to be two instances of the AIController that appears in the Outliner. The second instance is the one that possesses the character. The other doesn’t register as a valid AIController and that is why a second is created.
The second AIController that possesses the character moves it to the correct location but when the PlayerController is meant to take back possession, that does not happen.
When the player reaches the hiding spot, the camera and boom are meant to track in tight on the characters face. This was working before I implemented the AIController possession but doesn’t work now.

Here is the code for all AI functions:

RAIController.h

#pragma once

#include "CoreMinimal.h"
#include "AIController.h"
#include "RAIController.generated.h"

/**
 * 
 */
UCLASS()
class RUSALKA_API ARAIController : public AAIController
{
	GENERATED_BODY()
	
public:
	virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result) override;
};

RAIController.cpp

#include "Characters/RAIController.h"
#include "Characters/RCharacter.h"
#include "Navigation/PathFollowingComponent.h"
#include "Macros/RMacros.h"

void ARAIController::OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Result)
{
	Super::OnMoveCompleted(RequestID, Result);

	if (Result.Code == EPathFollowingResult::Success)
	{
		ARCharacter* Hiker = Cast<ARCharacter>(GetPawn());
		if (Hiker)
		{
			Hiker->bMoveCompleted = true;
			Hiker->PossessByPlayer();
			Hiker->Alpha = 0.0f;
			Hiker->bIsHiding = true;
			Hiker->SetRPlayerState(EPlayerState::EPS_Hiding);
			Hiker->SetRActionState(EActionState::EAS_Standing);
			PRINT(12, TEXT("Move has completed!"));
		}
	}
}

RCharacter.h

	void PossessByPlayer();
	void PossessByAI();
	void CheckForAIController();
	void OnMoveCompleted();
	virtual void PossessedBy(AController* NewController) override;

	UPROPERTY()
	ARAIController* AIControllerInstance;

RCharacter.cpp

ARCharacter::ARCharacter()
{

	AIControllerClass = ARAIController::StaticClass();
	AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;

	AIControllerInstance = nullptr;

}

void ARCharacter::BeginPlay()
{
	Super::BeginPlay();

	AIControllerInstance = Cast<ARAIController>(GetController());
	if (AIControllerInstance)
	{
		PRINT(-1, TEXT("AIController is initialized in BeginPlay"));
	}
	else
	{
		GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ARCharacter::CheckForAIController);
	}

	if (APlayerController* PlayerController = Cast<APlayerController>(GetController()))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(RPlayerMappingContext, 0);
		}
	}
}

void ARCharacter::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	AIControllerInstance = Cast<ARAIController>(NewController);

	if (AIControllerInstance)
	{
		PRINT(-1, TEXT("AIController has possessed the character"));
	}
}

void ARCharacter::TakingCover()
{
// this is a call back for when the Q-Key is pressed, which starts the taking cover and then hiding.
	UE_LOG(LogTemp, Warning, TEXT("Cover Location found: %s"), bHasHit ? TEXT("true") : TEXT("False"));
	
	bIsTakingCover = !bIsTakingCover;

	UE_LOG(LogTemp, Warning, TEXT("Taking Cover is: %s"), bIsTakingCover ? TEXT("true") : TEXT("False"));

	if (bHasHit && bIsTakingCover)
	{
		RActionState = EActionState::EAS_TakingCover;

		MoveToCover();
	}
	else
	{
		RPlayerState = EPlayerState::EPS_Idle;
		RActionState = EActionState::EAS_Standing;
	}
}

void ARCharacter::PossessByPlayer()
{
	if (GetRPlayerCamera())
	{
		StoredCameraLocation = GetRPlayerCamera()->GetRelativeLocation();
		StoredCameraRotation = GetRPlayerCamera()->GetRelativeRotation();
		StoredFieldOfView = GetRPlayerCamera()->FieldOfView;
		StoredSpringArmLength = GetRPlayerBoom()->TargetArmLength;
	}
	PRINT(-1, TEXT("PossessByPlayer has been called"))

	APlayerController* PlayerController = Cast<APlayerController>(GetController());
	if (PlayerController)
	{
		PlayerController->Possess(this);
		GetRPlayerBoom()->bUsePawnControlRotation = true;
		GetRPlayerCamera()->bUsePawnControlRotation = false;
	}

	if (GetRPlayerCamera())
	{
		GetRPlayerCamera()->SetRelativeLocation(StoredCameraLocation);
		GetRPlayerCamera()->SetRelativeRotation(StoredCameraRotation);
		GetRPlayerCamera()->FieldOfView = StoredFieldOfView;
		GetRPlayerBoom()->TargetArmLength = StoredSpringArmLength;
	}
	RPlayerState = EPlayerState::EPS_Idle;
	RActionState = EActionState::EAS_Standing;
}

void ARCharacter::PossessByAI()
{
	if (GetRPlayerCamera())
	{
		StoredCameraLocation = GetRPlayerCamera()->GetRelativeLocation();
		StoredCameraRotation = GetRPlayerCamera()->GetRelativeRotation();
		StoredFieldOfView = GetRPlayerCamera()->FieldOfView;
		StoredSpringArmLength = GetRPlayerBoom()->TargetArmLength;
	}

	if (!AIControllerInstance)
	{
		AIControllerInstance = Cast<ARAIController>(GetController());
	}

	if (!AIControllerInstance && GetWorld())
	{
		PRINT(-1, TEXT("No existing AI Controller found. Spawning a new one"));
		AIControllerInstance = GetWorld()->SpawnActor<ARAIController>(AIControllerClass, GetActorLocation(), GetActorRotation());

		if (AIControllerInstance)
		{
			AIControllerInstance->Possess(this);
		}
		else
		{
			PRINT(-1, TEXT("Failed to spawn AI Controller."));
			return;
		}
	}
	else if (AIControllerInstance)
	{
		AIControllerInstance->Possess(this);
	}

	GetRPlayerBoom()->bUsePawnControlRotation = false;
	GetRPlayerCamera()->bUsePawnControlRotation = true;

	if (GetRPlayerCamera())
	{
		GetRPlayerCamera()->SetRelativeLocation(StoredCameraLocation);
		GetRPlayerCamera()->SetRelativeRotation(StoredCameraRotation);
		GetRPlayerCamera()->FieldOfView = StoredFieldOfView;
		GetRPlayerBoom()->TargetArmLength = StoredSpringArmLength;
	}
}

void ARCharacter::CheckForAIController()
{
	if (AIControllerInstance)
	{
		PRINT(-1, TEXT("AIController is born (delayed check)"))
	}
}

Hi everyone, I believe I have fixed it. Clearly I needed to post the issue to clear my mind and find a solution.

I realised that I was creating a second PlayerController instead of storing the first one created in BeginPlay. I made the following changes:

void ARCharacter::BeginPlay()
{
	Super::BeginPlay();

	AIControllerInstance = Cast<ARAIController>(GetController());
	if (AIControllerInstance)
	{
		PRINT(0, TEXT("AIController is initialized in BeginPlay"));
	}
	else
	{
		GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ARCharacter::CheckForAIController);
	}
// I declared APlayerController* PlayerController in the header, instead of here.
	PlayerController = Cast<APlayerController>(GetController());
	if (PlayerController)
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(RPlayerMappingContext, 0);
		}
	}
}

/* Then in the PossessByPlayer() function I removed the Cast to APlayerController and 
used the stored PlayerController variable */

void ARCharacter::PossessByPlayer()
{
	if (PlayerController)
	{
		PlayerController->Possess(this);
		GetRPlayerBoom()->bUsePawnControlRotation = true;
		GetRPlayerCamera()->bUsePawnControlRotation = false;
	}
}

That did the trick. There are still some kinks to work out but the AIController and PlayerController switch perfectly and the camera zoom in on hide works now too.

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