Hello world! Today I’ll be talking about a topic that’s very important and dear to my heart (and one that I wish someone had taken the time to talk to me about much earlier in my life): Cameras! The reason I’m doing this is because cameras are like waiters at a restaurant; You only really focus on them if they’re doing an amazingly good job or a slightly below average job. So with that in mind I’ll be breaking down setting up a third person camera (with code samples from some UE4 projects I’ve been working away at) in the hopes of giving you a solid foundation for you to build your own amazing systems off of. Without further adieu, let’s get into it!
To start let’s get into the very barebone basics of a third person camera. Arguably all you really need is an Offset. This just means you subtract the camera’s forward vector, multiplied by the offset amount, from the Player location to find where the camera should be. This will immediately give you a camera that will pivot around the Player and follow them as they move. Simple enough right?
this->SetActorLocation(Target->GetActorLocation() - this->GetForwardVector() * Offset);
That said, personally I think there’s another factor here that is still very important: A Pivot Offset. By using (Player Location + PivotOffset) you change where the camera’s pivot point is and can make things look a lot cleaner.
this->SetActorLocation(Target->GetActorLocation() + CamPivotOffset - this->GetForwardVector() * Offset);
“Alright that’s cool and all but surely there’s something simple we can do to make it a bit more interesting?” asks some hypothetical reader. Well my fictional friend there’s something very simple we can do to spice things up a bit! With a little good ol’fashioned mathematics before setting our camera location we can find the distance between our camera and our pivot point and use that to add drag, preventing the camera from immediately snapping to a consistent distance. This technique works best with an Ideal Offset (the value you’d want when no movement has happened), a Min/Max clamp (to prevent the camera from getting too far away) and a speed control value (to fine-tune how long it will take the camera to go to its ideal position). Here’s a bit of code with just the important bits:
FVector Delta = (Target->GetActorLocation() + CamPivotOffset) - this->GetActorLocation(); float DeltaOffset = Delta.Size() - Offset; float MovementCap = Delta.Size() - Clamp(Delta.Size(), IdealOffset + OffsetClamp.X, IdealOffset + OffsetClamp.Y); DeltaOffset -= MovementCap; float DistToIdeal = Offset - IdealOffset; DeltaOffset -= (Sign(DistToIdeal) * Speed * DeltaTime); Offset = Offset + DeltaOffset - MovementCap; this->SetActorLocation(Target->GetActorLocation() + CamPivotOffset - this->GetForwardVector() * Offset);
What the above code results in is a camera that will allow the player to move closer to or further away from it, adding a more dynamic feel. Here’s a comparison to show the difference:
Into the Frying Pan
Now that we have our absolute basics, the next pretty-simple-thing we can add is our Pans. 3D Cameras essentially have three axes of movement; Offset (X); Horizontal Pan (Y); Vertical Pan (Z). As you can probably surmise from their names the two pans involve horizontal (Left/Right in this case) movement and vertical (Up/Down) movement and essentially change where the camera’s pivot point is (something Offset does not do). A good way to think about it is that (Player Location + Pivot Offset) is your zero point and (Player Location + Pivot Offset + Pan Movement) will move away from that; You can then figure out your camera’s position by moving away from the new Pivot by the Offset amount. You can also take the drag concept we applied to the Offset and use it with pans to make it so that the scene will center itself when movement stops!
Now you’re probably asking yourself how to best get values for a horizontal and vertical pan, well there’s two different parts to keep in mind: The Vertical component, which is just the Vertical difference (which luckily never changes since Up will always be Up); and the Horizontal component, which is a bit more complicated and actually warrants discussion. My personal approach to the Horizontal component is to find where our current pivot location is and the delta between our current pivot and the pivot we want to be at next, then projecting that delta on to the right vector for the camera to leave only the relative horizontal part. It looks a little something like this:
FVector CurrentPivotLocation = CurrentLocation + (this->GetActorForwardVector() * Offsets.X);FVector CurrentPivotLocation = CurrentLocation + (this->GetActorForwardVector() * Offsets.X); FVector DeltaPivot = CurrentPivotLocation - (Target->GetActorLocation() + CamPivotOffset); float DeltaH = (DeltaPivot.ProjectOnTo(this->GetActorRightVector())).Size() * Sign(RadiansToDegrees(Acos(FVector::DotProduct(DeltaPivot.GetSafeNormal(), this->GetActorRightVector()))) - 90.0f); HPan = Clamp(HPan + DeltaH - (Sign(HPan) * HSpeed * DeltaTime), HPanClamp.X, HPanClamp.Y); float DeltaV = DeltaPivot.Z - VPan; VPan = Clamp(VPan + DeltaV - (Sign(VPan) * VSpeed * DeltaTime), VPanClamp.X, VPanClamp.Y);
For a really good example of how useful a panning effect can be take a look at Final Fantasy XIII. When out of combat the camera will often move horizontally to push the character model to one side of the screen, very often following the rule of thirds and making the entire game feel a bit more cinematic than if it just stayed centered the entire time. That said, there are plenty of instances where you might not want these controls available and it’s totally fine if you want to ignore them.
Controlling Your Camera
Now that we’ve established the different basic ways of moving the camera we need to talk about an issue that you’ve probably seen arise: Controlling it all! In the early stages of a camera, when things are still being added or removed I like to try and expose as many control points as possible, but I know this isn’t something everyone would like. That said, here are variables that absolutely need to be exposed for designers:
Ideal Offset, Clamp ranges for all axes (as well as one for Pitch) and Speed for all axes.
In addition to this I’m going to spend some time talking about a technique for even finer camera controls: Speed Adjustments. Admittedly this is a technique I’ve somewhat adapted from a stream done by Epic Games (you can watch it here and you absolutely should). To summarize it quickly: The idea is that you get the distance of one of the camera’s axes to its ideal location (i.e. from the current offset to the ideal offset) and use a range for distances with that value to map to a corresponding range of values that adjust the speed. It’s fantastic as it allows you to create dead zones, make smaller distances have reduced speeds and so on. The amount of fine-tuning it allows for is amazing and is definitely something that should be utilized!
Well now that we’ve covered the basic elements and controls the only thing left is whatever you want to add! Seriously, that’s it. Every game has unique elements and it’s honestly best for you to get creative with it to find what works best with yours.
That said, I guess I can throw out some ideas to help the process along. So here’s some stuff that I’ve played around with:
Alternative Set Order – So here’s an interesting thing about the order you set things in: It makes a huge difference! Setting the Rotation and then the Location results in a more typical camera which follows the character around whereas setting the Location and then the Rotation results in a camera that tries to maintain a more fixed position when the character is moving (think a cinematic mode where the camera stays still but tracks a target). Both options have their pros and cons so play around with it and find what you like as well as ways to combat their individual issues.
Offset-based Value Remapping – This uses the value of the offset relative to its ideal value to then adjust other values in the system such as the clamps for the Horizontal Pan as well as adding an additional vertical offset. The intention in this instance was to make it so that as the camera moved away from or towards the player the camera would adjust to maintain rule of thirds.
Inverted Horizontal Pan – So you calculated your Horizontal Pan so that when the player hits the side it seems to pull the camera along? Why not try inverting it so that the camera will move in front of the player! This makes it so that the camera will expose more of what the player is running towards as well as creating a more aesthetically pleasing shot (in terms of photography it’s the difference between looking like someone is running into frame vs running out of frame)
I hope you’ve found this read to be worthwhile and that you’ve come away having learned something or at the very least it has gotten some gears turning. I wish you all the best in making cameras that help make your games feel even better!