Player Camera in Controller instead of Character

Hi all,

I currently have many actors on screen, only one of which can be controller by the player. As such, I am trying to move away from the default model, which is built upon the topdown template, by moving the camera and spring arm components onto my controller which is derived from Playercontroller. This should improve performance as I no longer have dozens of unused cameras!

Has anyone been able to achieve this? I have currently simply moved the two components, the camera and the spring arm, into my playercontroller rather than the character. Unfortunately this didnt work. When I boot the game, the camera seems to be embedded within my character. The camera faces the correct direction, but it is as though the spring arm has a permanent length of zero. The moving works, but i cannot zoom out.

What is the correct method of initializing these components so that i can keep them entirely within the controller?

Here is my current way within my controller:



void AMyPlayerController::BeginPlay()
{
	APlayerController::BeginPlay();

	AMyCharacter* character = Cast<AMyCharacter>(this->GetCharacter());

	CameraBoom = NewObject<USpringArmComponent>(character, TEXT("CameraBoom"));
	CameraBoom->bAbsoluteRotation = true; // Don't want arm to rotate when character does
	CameraBoom->TargetArmLength = 950;
	CameraBoom->RelativeRotation = FRotator(-60.f, 0.f, 0.f);
	CameraBoom->bDoCollisionTest = false; // Don't want to pull camera in when it collides with level
	CameraBoom->AttachTo(character->GetRootComponent());
	CameraBoom->Activate();

	TopDownCameraComponent = NewObject<UCameraComponent>(character, TEXT("TopDownCamera"));
	TopDownCameraComponent->bUseControllerViewRotation = false; // Camera does not rotate relative to arm
	TopDownCameraComponent->AttachTo(CameraBoom, USpringArmComponent::SocketName);
	TopDownCameraComponent->Activate();
	
	SetViewTarget(character);
}


I’d love to know if anyone has been able to move the camera to the controller instead of the character with success.

I’ve done this myself. My game is an FPS / RTS mix where the player can take control of a lot of different pawns, often in quick succession. To avoid all those extra scene components, I create the Camera, Spring Arm and First-Person Meshes as part of the Player Controller (the latter is a pain in the ****).

To do it successfully, you need to create a custom PlayerCameraManager and override the UpdateViewTarget function. This is a direct copy-paste from my project below, you can pretty much work it out :slight_smile:



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

	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;

	ABZGame_PlayerController* BZGame_Controller = Cast<ABZGame_PlayerController>(GetOwningPlayerController());
	if (BZGame_Controller != NULL)
	{
		UCameraComponent* ViewCam = BZGame_Controller->GetViewCamera();
		OutVT.POV.Location = ViewCam->GetComponentLocation();
		OutVT.POV.Rotation = ViewCam->GetComponentRotation();
		OutVT.POV.FOV = ViewCam->FieldOfView;
		OutVT.POV.AspectRatio = ViewCam->AspectRatio;
		OutVT.POV.bConstrainAspectRatio = ViewCam->bConstrainAspectRatio;
		OutVT.POV.ProjectionMode = ViewCam->ProjectionMode;
		OutVT.POV.OrthoWidth = ViewCam->OrthoWidth;
		OutVT.POV.PostProcessBlendWeight = ViewCam->PostProcessBlendWeight;

		if (BZGame_Controller->GetViewCamera()->PostProcessBlendWeight > 0.0f)
		{
			OutVT.POV.PostProcessSettings = ViewCam->PostProcessSettings;
		}

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

		SetActorLocationAndRotation(OutVT.POV.Location, OutVT.POV.Rotation, false);
		UpdateCameraLensEffects(OutVT);
	}
	else
	{
		Super::UpdateViewTarget(OutVT, DeltaTime);
	}
}


EDIT: Also, you really want to create the controller components in the constructor.

2 Likes

Well not sure if this is a viable solution for your case but I’m going to throw it on the table.

I fiddled around with RTS style game and I ended up doing “Dummy” pawn what was possessed by the PlayerController. This Dummy was invisible pawn that had the SpringArm and Camera attached to it. Then I had the “Unit” Character that was selectable by clicking them. After you’ve selected a unit you could issue move, attack and such commands for it. So this way, you will have only one camera (and the dummy pawn) spawned for each player in the game. Then you could have lots of units in the map and they would not have the basic spring arm+camera components. Also this way I could move and rotate the camera to look at different part of the map regardless of the unit location&rotation I was issuing commands for.

I never took the idea too far but the solution seemed to work out pretty well.

Yeah that’s not really suitable for me since the FPS element plays more of a role than the RTS one does. If however I was doing full-on RTS only, I would probably do something similar.

In fact I haven’t even reached the RTS elements yet, the most recent video is the one below but there’s probably better ones that show the switching between different vehicles etc. This one was taken after I got the smooth blending between view-changes done (since it’s third and first person), and used the first person mesh on the player controller instead of the pawn so it’s a bit er… strange.

Thanks a bunch for the help. Managed to get the camera manager working and it works a treat!

Thanks again

Sorry if its dumb question, but what
BZGame_Controller is the orewrite code ? Can’t undersand where you taking the new transform data from

One you bumped a really old thread, two you have very little c++ knowledge, three BZGame_Controller is the name of the pointer to
ABZGame_PlayerController.

By the way, your 3d mini map is sooo freaking cool, haha.
How did you make that kind of mini map?? :slight_smile:

I was running into the same problem, thanks for the help :smiley:.

Thanks for to code snippet, I had the same problem and this is exactly I was looking for!

I’ll just add for future visitors, if you just copy paste the solution don’t forget to create GetViewCamera(). It is a simple getter which you must create in your own PlayerController and looks like this

UCameraComponent* ACustomPlayerController::GetViewCamera()
{
	return ViewCamera;
}

Also don’t forget to assigned the custom PlayerCameraManager. You can do it either in the constructor of ACustomPlayerController

ACustomPlayerController::ACustomPlayerController()
{
	PlayerCameraManagerClass = ACustomPlayerCameraManager::StaticClass();
}

or in your BP_CustomPlayerController (blueprint) Details->PlayerController->PlayerCameraManagerClass