How do you Setup Dynamic Third Person Camera

I am developing a First Person/ Third Person switchable camera and I want to utilize the behavior of the spring arm component just like the Third Person template. However I want the player to be able to control nearly any NPC in the game, but I do not want to have a Camera and Spring Arm attached to every single NPC or Actor in the game as this is an RPG with lots of NPC’s.

I have dabbled with putting the camera and spring arm on the Player Controller and attaching it on Begin Play with no luck. I’ve tried overriding the CalView to use the attached camera also with no luck all I get is a view from “crotch” level.

Can anyone please tell me what I need to do to Dynamically attach a camera and spring arm component to a Pawn at run time preferably from the Player controller or the Camera Manager. (even tried the camera manager, again no luck in overriding the view still got crotch view… I feel like I am missing something here…) There seems to be only the lightest of documentation (an overview at best) on how the camera system works.

will appreciate any help or guidance you can give.

Thanks

UPDATE

It appears the main culprit of my problem is the spring arm. I Moved the spring arm back onto the character but kept the camera attached to the controller and it started behaving as expected once again.

When I inspect the Controller blue print I see the camera is attached to the spring arm but it is attached at the pivot point, as apposed to the end. For some reason creating the spring arm on the controller and attaching it on the BeginPlay event seems to swap the end points…

Ideally I’d like the spring arm to be on the controller as well…

Well three days with no response I finally solved it. Hopefully this will aid anyone else looking to figure out this same problem. The following is psudocode you will need to properly adapt it to C++ for it to work.

First up my setup:

class CustomCharacter
{
// some class stuff here really not entirely important
};

class CustomPlayerController
{
public:
    TSubobjectPtr<USpringArm> CameraBoom;
    TSubobjectPtr<CameraComponent> Camera;
public:

    CustomPlayerController()
    {
        this->PlayerCameraManagerClass = CustomCameraManager::StaticClass()
    }

    void BeginPlay() override
    {
        if(CustomCharacter * character = Cast<CustomCharacter>(this->GetPawn())
        {
            this->CameraBoom->AttachTo(character->GetRootComponent())
        }

    }
};

class CustomCameraManager
{
void UpdateViewTarget(FTViewTarget & OutVT, float DeltaTime)
{
	if ((PendingViewTarget.Target != NULL) && BlendParams.bLockOutgoing && OutVT.Equal(ViewTarget))
	{
		return;
	}

	// store previous POV, incase we need it later
	FMinimalViewInfo OrigPOV = OutVT.POV;

	OutVT.POV.FOV = DefaultFOV;
	OutVT.POV.OrthoWidth = DefaultOrthoWidth;
	OutVT.POV.bConstrainAspectRatio = false;
	OutVT.POV.ProjectionMode = this->bIsOrthographic ? ECameraProjectionMode::Orthographic : ECameraProjectionMode::Perspective;
	OutVT.POV.PostProcessBlendWeight = 1.0f;

	bool bDoNotApplyModifiers = false;

	CustomPlayerController * customController = Cast<CustomPlayerController>(this->GetOwningPlayerController());
	if (customController != NULL)
	{
		CustomCharacter * customPawn = Cast<CustomCharacter>(customController->GetPawn());
		if (aPawn)
		{
			const FRotator pawnViewRotation = customPawn->GetViewRotation();
			if (!pawnViewRotation.Equals(customController->CameraBoom->GetComponentRotation()))
			{
				customController->CameraBoom->SetWorldRotation(pawnViewRotation);
			}
		}

		customController->Camera->AttachTo(customController->CameraBoom, USpringArmComponent::SocketName);

		OutVT.POV.Location = customController->Camera->GetComponentLocation();
		OutVT.POV.Rotation = customController->Camera->GetComponentRotation();

		OutVT.POV.FOV = customController->Camera->FieldOfView;
		OutVT.POV.AspectRatio = customController->Camera->AspectRatio;
		OutVT.POV.bConstrainAspectRatio = customController->Camera->bConstrainAspectRatio;
		OutVT.POV.ProjectionMode = customController->Camera->ProjectionMode;
		OutVT.POV.OrthoWidth = customController->Camera->OrthoWidth;

		OutVT.POV.PostProcessBlendWeight = customController->Camera->PostProcessBlendWeight;

		if (customController->Camera->PostProcessBlendWeight > 0.0f)
		{
			OutVT.POV.PostProcessSettings = customController->Camera->PostProcessSettings;
		}

		if (!bDoNotApplyModifiers || this->bAlwaysApplyModifiers)
		{
			this->ApplyCameraModifiers(DeltaTime, OutVT.POV);
		}

		this->SetActorLocationAndRotation(OutVT.POV.Location, OutVT.POV.Rotation, false);

		this->UpdateCameraLensEffects(OutVT);
	}
	else
	{
		Super::UpdateViewTarget(OutVT, DeltaTime);
	}
}
}

The rotate View function was pretty much swiped directly from the documentation (with a few alterations where needed) Camera Manager UpdateView

Now the problem I was having with the whole having a “Crotch” view ended up being due to the behavior of the spring arm itself. It performs a collision check and excludes it’s owner, which under normal circumstances is the pawn itself… To fix this I gave the spring arm component an offset but this is only a temporary fix. To truely fix this problem I will need to subclass the USpringArm component and completely override the behavior of the UpdateDesiredArmLocation function to ignore collision against a designated pawn rather than the owner