In this post we’re going to be handling camera zoom and camera rotation around an object using spring arm component connected to camera.
There’s few other techniques for handling them, which may be worth checking depending on your needs:
- Rotate the object rather than camera and zoom
- Rotate and zoom the camera without spring arm (using lens)
In my example, I will use character creation screen – but it can be with any object, check out the video demo.
Creating camera with spring arm
First of all, you may not have a camera on a spring arm – if you don’t, you will want to create one.
Quite often your pawn will have a camera attached to a spring arm – but this is not always the case.
If you already have an Character
component that you’re using, then add the camera on spring arm to that, or create new one like I am doing.
You can create this Character blueprint by right clicking in Content Browser
and selecting to create new Blueprint class
then selecting Character
for parent class.
Once you’ve created the Character, add in a Spring Arm
component using the add
button highlighted in top left. Then add in the Camera
component.
Make sure the camera is nested inside the spring arm component.
Then, you can change some default location/rotation for the spring arm as you like.
Feel free to add your object into this class directly, this is optional.
Next, just make sure you’re using this Character when you’re started your project.
You can do this by opening the World Settings
for your map, and setting the default pawn class as demonstrated here
You can go ahead and change it in the game mode override if you prefer.
And if you just need it to always pop into same place, you can easily achieve this by adding player start
component into the map.
Character Controller implementation
The zoom and rotation will be handled inside the character controller. One of the reasons for this is because the skeletal mesh actors / character blueprints can sometimes consume the inputs which will cause unexpected and unwanted behaviors. To make things more consistent, we will delegate the rotation and zoom to the character controller blueprint which will make things more consistent.
Create new Player Controller class if you don’t already have one.
Again, update the game mode or override the world settings directly to make sure you’re utilizing the Player Controller blueprint.
Preparing graphs
Let’s create graphs to split the work and make things easier to see and understand.
- Bind Rotate Zoom Events
- Handle Rotate
- Handle Zoom
The binding of events graph is just ensuring the logic is connected and called correctly.
Handle rotate and handle zoom graphs will do as described.
Its easier this way as if you’re having logic issues with zoom, you know where to look. If zoom is not initialized at all, perhaps its in binding of events graph – so keeping the graphs separated helps you with debugging and locating the problem faster.
Binding keys for rotate and zoom
To bind the keys you will want to open Edit -> Project Settings -> Input.
See all highlighted sections. You may call them as you like. You can bind to different keys too, for example I will rotate/drag using right mouse click – you can do via left click or anything else.
Event graph for Character controller
The Event graph will be kept clean. On event begin play we’ll just initialize the rotate and zoom events.
Bind Rotate Zoom Events
First thing we’ll do is look at the Init function we just referenced.
Instead of using event tick, we’ll call our update methods via a timer.
We need to process the following every tick:
- Evaluate mouse drag, if a drag is in progress
- Evaluate zoom required
- Adjust camera position for rotation
Let’s start with the drag function.
In order to get the drag, we need to know the current mouse position and evaluate the difference between previous position, for both X and Y co-ordinates.
Before we look into the other functions, let’s look at the rest of blueprints in this graph.
Here we bind right mouse click – when the user right clicks, we will set Zoom in progress
to true and set the previous Loc X/Y
which helps us with evaluating drag in first tick.
Next, we do similar for the zoom. We bind on the mouse wheel up/down.
This simply adds to our zoom velocity directly, which we will process in the Zoom graph.
Evaluate Zoom
For evaluating zoom, we will leverage curve features. This is really cool feature which will allow us to achieve quite smooth zoom affects.
In order to utilize curve, you will want to add it to your variables, select Curve Float
as type and you will need to compile it before you will be able to set one as default value.
Let’s now define the two curves, for zoom in and zoom out.
Zoom in curve
The curve will have two points and an auto curve between them two
- 100, 0
- 200, 1
This is used as a multiplier, the X axis refers to the distance from the object, whereas the Y refers to the multiplier in this case.
This means that if we’re approach 100 units and trying to zoom in more, this curve will make the multiplier 0 and you will not zoom in any further.
It will also gradually make the zoom multiplier smaller as you go from distance of 200 to 100. Feel free to optimize these values to whatever you like.
Zoom out curve
The zoom out curve is very similar and almost opposite. It also has two values:
- 1. 230, 1
- 2. 300, 0
Again, X axis refers to distance of camera from object and Y refers to multiplier.
This means that after 230 units from the object, the multiplier will start approaching 0 and when we approach 300, the multiplier will affectively drop to 0.
This gives us a smooth effect for zoom function and allows us to control the zoom between units of 100 (in zoom in curve) and 300 (in zoom out curve).
Process Camera Arm Length
We just looked into the curve float so we know what that is, but how do we get the reference for the Camera Spring Arm
?
In my case, I just set it from the character blueprint class, as that’s what contains the actual spring arm.
First, create a variable of type Spring Arm Component reference
.
Next, open up your Character
class that you’ve created in step one. Then add to Event begin play
the following.
This is basically getting the player controller and setting the variable up for you to use.
Ok, so going back to Process Camera Arm Length
function.
We first get the length of spring arm. This is the input value (X param) for the curve).
If we’re zooming in and the length is approaching 100, the result of this will approach 0. This means whatever velocity is, we’re dropping it to 0 in the processing.
I also add 0.9 to the multiplier to add a 10% decay on velocity on each tick (set to 0.03 on a timer by default).
Apply Velocity to Camera Arm
Now that we’ve evaluated the actual velocity/speed of the zoom, we can add it to the length of spring arm.
Simply put, we take the current length of the spring arm and we add the zoom velocity to it. This automatically adjusts the camera location.
That’s zoom functionality complete!
Adjust camera position
This function is responsible for ‘rotating’ the camera around the object.
This function evaluates the velocity for rotation then applies it.
We evaluate two values, X drag / X velocity and Y drag / Y velocity.
The Y velocity will be used to move the camera up/down whereas the X velocity will literally rotate the camera around the object.
Note that the function Add World Rotation
did not work as expected, so I used Set world rotation
function for the Spring Arm instead. The weird behavior is that for some reason, despite me not adding Roll it was modifying it. Whereas Set actor rotation
was working as expected – perhaps its a bug which will be fixed in future versions.
Process Camera Position Velocity
This function looks a little busy because we’re evaluating both X and Y axis velocities.
We’re doing a few things here:
- add 10% decay to velocity on each tick
- Check if zoom drag is in progress (right mouse button is clicked)
- Apply invert if desired – feel free to parameterize this
- Apply sensitivity to drag, to determine additive velocity
The sensitivity can be different for the two sets, you can define it with default value like this:
Mines set to 0.03, do note that you may want to calibrate this to screen sizes. E.g. if your window is different resolution, you can have quite different mouse drag results, so you may want to calibrate/normalize sensitivity based on resolution.