Camera management in C++

I’ve been trying to use the Strategy game template to understand how the camera system works, but there seems to be a couple of gaps (also pretty sure the code doesn’t compile as is, since it’s missing explicit constructor declarations in the headers for basically all the classes, but that’s another matter). Most of the documentation seems to revolve around using blueprints and the editor to set up camera systems, but I need to do mine in C++ since I have a big chunk of other logic that’s also in C++.

My understanding of the camera system is as follows. A CameraComponent is attached to a Pawn, whether it be a SpectatorPawn or a regular Pawn depends on what I want the camera to do. In my case I’m using a derivative of a SpectatorPawn since I need a free floating camera. The camera itself is supposed to be relative to the Pawn so when the Pawn moves, the camera moves along with it. All fine and good. Except that, in my implementation, when the Pawn moves, the camera just sits there. So obviously something’s wrong/I’m missing something. Only problem being, I don’t see anything else in the Strategy game template that I’m not also doing.

To associate a CameraComponent with the SpectatorPawn, the template has this:

StrategyCameraComponent = ObjectInitializer.CreateDefaultSubobject<UStrategyCameraComponent>(this, TEXT(“StrategyCameraComponent”));

Nothing else. I am assuming, based on the code I’ve read, that “this” pointer will get used to make the newly created object a child of “this” and so the CameraComponent should be associated with the SpectatorPawn. And in the CameraComponent class itself, every time the “camera” needs to move it’s the parent Pawn that gets location updated.

I’ve stepped through the code, breakpointing after the code is supposed to have moved. What’s interesting is that according to the CameraManager, the camera’s location is at 0,0,0, even though the Pawn itself is nowhere near there. And every time I hit the breakpoint in my code, while the pawn that is supposed to own the CameraComponent does indeed change position, the location that the CameraManager reports is always at 0,0,0. This makes me think that my Pawn class is perhaps not being properly associated with the PlayerController, save for the minor fact that the GetPawn method is giving me back my SpectatorPawn. Which then suggests that for some reason the “camera” being used by the CameraManager is not the CameraComponent owned by my custom Pawn. The only problem being, I see no additional code that might do such a task.

Trying to use the GetSpectatorPawn method liked the Strategy template failed, which suggests to me there’s a missing piece somewhere since I am again doing exactly what the Strategy template does to register the DefaultPawnClass, SpectatorClass, and PlayerControllerClass.

I am therefore, at this point, stuck. I’ve poked around on the wiki and seen some code written against older versions of Unreal where someone is explicitly calling AttachParent on the CameraComponent, but that’s about the only distinction between that code and the Strategy template, and I’m assuming here that the AttachParent is implicit in the CreateDefaultSubobject call. So, does anyone have any idea what the missing holes are or can point me to a working C++ example of a custom camera system?

this isn’t a strategy game at all, but maybe
can help you understand the camera a little bit?

Like you, I wanted a floating camera as seen in the StrategyGame example. I also looked at the Top Down Shooter example as well and I managed to get something working before moving on. It’s something I need to go back and have a proper look at along with my HUD and viewport in general. I think I could only get it to work with a CameraBoom but you can try the following for now: -

SpectatorPawn.cpp Constructor


	YourCameraComponent = ObjectInitializer.CreateDefaultSubobject<UYourCameraComponent>(this, TEXT("YourCameraComponent"));
	// Create a camera boom...
	CameraBoom = ObjectInitializer.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom"));
	CameraBoom->AttachTo(RootComponent);
	CameraBoom->bAbsoluteRotation = true; // Don't want arm to rotate when character does
	CameraBoom->TargetArmLength = 0.0f
	CameraBoom->bDoCollisionTest = false; // Don't want to pull camera in when it collides with level

	// Create a camera...
	YourCameraComponent->AttachTo(CameraBoom, USpringArmComponent::SocketName);


YourCameraComponent.cpp


void UYourCameraComponent::GetCameraView(float DeltaTime, FMinimalViewInfo& OutResult)
{
	APlayerController* Controller = GetPlayerController();
	if (Controller)
	{
		if (bRotateCameraEnabled)
		{
			Controller->GetInputMouseDelta(DeltaX, DeltaY);
			CameraAngle.Add(DeltaY*RotateSpeed, DeltaX*RotateSpeed, 0.0f);
		}
		OutResult.FOV = 30.f;
		const float CurrentOffset = MinCameraOffset + ZoomAlpha * (MaxCameraOffset - MinCameraOffset);
		FVector Pos2 = Controller->GetFocalLocation();
		OutResult.Location = Controller->GetFocalLocation() - CameraAngle.Vector() * CurrentOffset;
		OutResult.Rotation = CameraAngle;
	}
}


The extra item I added was the ability to rotate the camera whenever the MMB was pressed which triggers the bRotateCameraEnabled flag. The other thing I modified was the MoveForward function within the SpectatorPawn class by zeroing the Z-axis to stop the camera moving “forward” into the ground: -


void AYourSpectatorPawn::MoveForward(float Val)
{
	APlayerController* Controller = GetPlayerController();
	if (Controller != NULL)
	{
		if (Val != 0.f)
		{
			// Find camera angle
			const FRotationMatrix R(Controller->PlayerCameraManager->GetCameraRotation());
			// Get a vector relative to the camera angle
			FVector WorldSpaceAccel = R.GetScaledAxis(EAxis::X) * 100.0f;
			// We only want the X and Y values and not change the camera height
			WorldSpaceAccel[2] = 0.0f;
			// Transform to world space and add it
			AddMovementInput(WorldSpaceAccel, Val);
		}
	}
}


One of the things I do need to add at some point is boundaries to stop the camera going below the ground or “over the top”. This is probably not the best way, but it seems to work for me for now using the WASD to move around, mouse wheel to zoom, and MMB to rotate… I’ll be interested in seeing other people’s methods for doing this.

Apparently messing up the GetCameraView method is more problematic than I originally thought. Thanks for the pointers. Mildly irritated that the default GetCameraView method doesn’t just track the pawn that owns the CameraComponent.

Heh, coincidentally I’m also working on the camera as well, deriving my camera behavior off of the Strategy Game sample for my own RTS.

So far, what I’ve got:
-You can zoom the camera in and out with the mouse wheel scroll
-You can pan the camera around by holding the middle mouse button
-In progress: I want to lock the mouse cursor position when panning around, it shouldn’t move until MMB has been released. Its turning out that I need to make some changes to the player controller code and viewport code within the engine to make this happen. Sheesh! I’m hoping maybe I can submit my additions back to Epic for inclusion in the next engine release, but we’ll see if they’ll take it.
-You can draw selection boxes with the left mouse button
-Right clicking gives a selected object a movement command