Let’s Make: UE4: Camera Spline System
A Camera that follows a user created spline based on the player location
LET’S MAKE a camera spline system, in which the camera follows behind our character on a spline. Many games use a camera track, like TellTale’s “The Walking Dead series” to control the camera. In this case, we will use Unreal Engine’s “Rolling Ball” template to create our camera system. The spline drives the camera location and rotation so players can focus on platforming.
- Using Unreal Engine 4.7.1
- Blueprint Only
- Uses Recursion
This tutorial assumes familiarity with Unreal Engine 4’s Blueprint system and the idea of recursion.
I also have a TLDR version of only pictures here:
The camera spline actor we are creating will be basic but can be easily augmented based on the needs of your game. The actor isn’t fully featured but it will help create a simple base to build off of later.
The idea is to create a camera actor with two spline components. The camera will follow one spline and another will be placed on the player’s main path. This player spline will never be perfect, we just need to add it around the most likely path. This is because we will compare the player location to the player spline. Once we have the closest point on the player spline we then apply it to the camera spline. This moves the camera to approximately the same spot on its own spline. Finally, the camera is setup to rotate toward the player character to always keep them in view.
The Camera Spline Class:
Step one is to create the base class for our camera system.
Right click in the content browser and create a new Blueprint with the subclass of an Actor. Name this BP_CameraSpline or something descriptive.
Next, open the new blueprint and add the following components:
- Name it something descriptive, I used “Icon” for mine
- Having a larger billboard makes re-selecting a specific camera spline much easier as deselecting the actor will cause the splines to disappear.
- I’ve setup my billboard to use the Spline Icon unreal has.
- In order to find this you must enable Show Engine Content
- This is the camera that will follow along the Camera Spline
- 1st Spline Component
- Rename to Camera Spline
- This is the spline our camera will follow along
- 2nd Spline Component
- Rename to Player Spline
- This is the spline that the character’s location will be compared to
- It will be placed closer to the ground and in a way that the player will be following like a path
Next, select each spline and change the “Editor Unselected Spline Segment Color” to two different colors. This will make figuring out which spline is which much easier. I’ve changed the player spline to a green and the camera to a purple.
After, move the Player Spline down 100 Units so they do not start on top of one another. Again this makes editing faster and easier.
Activate & Deactivate Functions
Lets begin programming our spline actor by opening up the event graph and adding in two functions.
These two functions are simple and either start or stop the tick code from being called. This will also make sure we don’t activate if we don’t have a player character to follow. The tick we will setup later uses the player variable as a simple bool check. Doing this makes sure we have a player to update, and if not, the camera spline will do nothing.
We can either setup the player variable as the specific player class or simply an actor or pawn, I choose to use the pawn class in this case. The code we will be writing later simply needs the actor’s location and nothing more specific.
These functions will be called from the player character in order to change which cameras are being used at any given time. The player character will have its own similar function which will become active through hitting trigger volumes or by specific events within the Level Blueprint.
The Construction Script
Within the construction script we want to setup two debug functions so we can more easily setup our camera splines in the editor. One will allow us to scrub through the spline, moving the camera from the start to the end so we can see exactly how the view will look in game. The second will be show debug numbers above each point of the splines helping us see how each spline point lines up from the player spline to the camera.
Set Scrub Camera Time
Inside Scrub Camera Time, place both spline variables and get their world location based on time. In order to give control on where the camera should be scrubbing, create a new variable called “Camera Debug Time” and hook it up as shown. Make sure to make this variable editable so it can be changed in editor and set the slider and range to 0 – 1 to make scrubbing easier as a splines time is always from 0 to 1. Continue to hook up the blueprint as shown to set the camera’s location and rotation.
The Boolean created called “ConstantVelocity” is an editable variable so each camera spline can determine if it should use constant velocity or not. When enabled, the spline movement from 0 to 1 happens at a constant rate, meaning the midpoint is directly in the center, however, when disabled the midpoint is dictated by the spline points and the rate changes between each point based on its length and tangents.
Note that this is how we will be updating our time later on as well as checking distances. By checking the time on the Player Spline over and over we can find a “Best Time” to use.
Setup Debug Numbers
Within the Setup Debug Numbers function we will check if debugging is active on this spline by creating an editable Boolean. If we are not debugging we won’t show the numbers, making less clutter while we edit. After this we will create a new function called “Add Spline Numbers” and create a spline component input for it. We are going to reuse this function twice, once for each spline.
Creating individualized functions like this may seem like extra work but can be beneficial in the end. It saves on space and allows modification to be done much faster. Changing how the function works once means that it gets applied to every copy. It also keeps each function more focused on a single task and easier to understand.
Next, setup the Add Spline Numbers function as shown below, using a For-loop with the Last Index as the Get Number of Spline Points – 1. We subtract one because we start at 0 and the spline number count starts at 1, you can also set the for-loop to start at 1 instead of subtracting if desired but the programmer in me feels like starting at 0 here.
At the start of the for-loop is a local variable called Index, I created it so it can be used later without crossing wires too often. The combine rotator is added to the spline tangent as the original tangent given causes the numbers to be view backward when moving forward along the spline, this simply corrects it.
Within the properties of the Add Text Render Component size can be changed to a bit larger, making it easier to read from further away. I have mine set to 64 currently.
This is where we update everything, find the closest distance on the player spline and move / rotate our camera.
Although it isn’t necessary I tend to put any larger group of functions, such as my update or tick code into a collapsed group. This just helps keep everything separated and easy to find. Collapsing too much can become difficult to sift through so I try to keep everything at a good level.
Here is what our final Tick Group will look like:
This is a quick overview of what each function does
- Set Delta Time
- Will be used later within specific functions
- Check to see if we have a player
- If not, do nothing!
- Recursive Distance Check
- This attempts to find the closest point between the player spline and the player character
- To do this, the function checks two points and then recursively calls itself in order to get closer and closer by keeping the closer time and then checking it against the halfway mark between the first two points
- Once complete, it sends the best time it found from the lowest level of recursion up and out to the initial call seen in the image above: Setting our Desired Time variable
- Update Desired Time
- Although we have the best time we want to update the camera slowly to avoid jitters and jumps
- Uses a Current Time Variable and increments toward the desired each tick
- Update Camera
- Update the camera based on its current time and rotate to face the player
- Debug Spline Movement
- Visualize the movement so we can debug it easier
Note that the recursive distance check takes in a number of variables, initially the two points in time we are going to test for, in the initial case is 0 and 1 to test the end points, as well as an iteration max count. This is the max number of times we will be recursively checking for the closest point. This is needed as, in most cases, our character isn’t going to be close enough to the spline to get a perfect result.
My Iteration Max Count variable is set at 10 and does very well even on larger splines.
Recursive Distance Check
This is a larger function that works by check the distance between two points on the Player Spline and the Player Character. This is opposed to a brute force method of cutting up the spline into a number of sections and testing each of them to find the best distance.
- The larger the spline is, the more iterations need to be checked in order to get an accurate location.
- This can range upward to 50+ distance checks if large enough
- Lower number of iterations on a large spline can cause the camera movement to jump between sections
- Checking from the start to end causes all points to be checked before finding the best location
- If the player is at the end of the spline it will do every check before finding the best location
- Checking distances on a curved spline can cause the camera to jump between different sections as the player moves between the start and end locations quickly
Although some better techniques can help the way this iterative check works, I’ve opted to use the recursive method which seems to work better but is not without its flaws.
- Recursive Method tends to jump around and get slightly different locations on the spline every once in a while and depending on the spline curve
- This however is combated by using a current time variable and increment it toward the desired time, avoiding jumps or jitters
- As always checking distances on a curved spline can cause the camera to jump between different sections as the player moves between the start and end locations quickly
Now we need to create the Recursive Distance Check Function and set it up as follows, making sure to set the input and output as shown as well as the local variables shown in the second image.
Initially the function simply sets up a number of the local variables as well as calculating the distances between the start and end locations based on the spline’s time. Passing in the start and end time as well as its current iteration number makes sure when we call the function recursively next, it works properly.
At the branch, the function checks to see which side is closer and keeps that one, either moving up or down to the correct version. This section can be broken up a bit better into multiple functions or setup with more local variables to cut back on code but for now is just fine for us.
NOTE: That our distance check can be changed out for a Length Squared check instead. This omits the square root needed to get the true distance, giving us a much larger number. However, because we are simply checking them against one another, skipping the square root is slightly faster which is nice if you need to save on calculations. Later when we do a threshold check, the amount must be squared if using the Length Squared.
At this point the path will check the iteration number to make sure we are not at our final recursion check. If we are, we will take the closest point on the spline as the best and send it back up the chain. If not, we will continue forward and do the recursive call subtracting one from the iteration amount and passing it along.
After this we do a threshold check to see if our player character is close enough to our current time location. If so we can stop our recursion, sending back up our current time as the best. The distance threshold variable is setup so the player must be standing very close or on top of the spline point to get an accurate result. This however, does not happen too often and may work better if we check the distance based on the ground, subtracting a small amount from our character location to get closer as opposed to checking from his origin, normally around his waist. For this version of the function it works well enough as is. My current ball character has a radius of 50 and I created the threshold variable to check for 60 units.
If neither of the previous sections stop us, we continue on and call the recursive function. The input for this function takes the closer distance and the half way mark between the two. Using a Lerp, we can easily find the middle of the two floats and plug it into the other time input. We also need to make sure to input our new iteration count as well to keep it counting down.
When we do hit our final iteration or are close enough, we can send the best time back up through the chain through the float output, each recursive call feeding into the previous and back out into our tick group where we save the best time into the Desired Time variable.
If you don’t have this variable yet, promote it to one so we can use it in the next function as well as the debug section.
Update Current Time
In order to keep our camera smoothly moving along the spline we can use a current time that slowly gets incremented or decremented as it approaches the “Desired Time”. Doing this makes sure we stay on the spline as closely as possible while moving from our current point to the desired. This also helps stop jittering or jumping if the best distance moves around too quickly.
I have also added in a Time Difference Threshold, in case the player moves from one end of the spline to the other extremely quickly. Instead of rapidly moving though the entire spline, the camera will shift right to the new location.
However, this really isn’t the best way to fix the problem and more must be done later to create a smooth transition if things don’t go as planned. The threshold variable is set to 0.75, meaning if the player somehow jumps from the current time to 75% in either direction of the spline it will no longer follow and simply move to the Desired Time directly.
Instead of calculating the world location during the next function I set it here to a variable called Desired Location. You can either do this or output the vector and use it as an input in the Update Camera function instead.
No Threshold Active
With the 0.75 Threshold Active
As seen from above, the camera does jump directly to the new location, skipping the movement through the entire spline which feels a bit better. For now it works but it will need a better solution later as it is still far to fast.
The easiest function out of them all, we simply are moving to the current time’s world location and rotating to face the player character. If desired, some camera rotation lag can be added by using an RInterp node instead of directly setting the rotation here.
Debug Spline Movement
In order to help with debugging I have a final function after all is said and done. It simply draws spheres at each location on the splines to visualize what is occurring and when. Having this information makes understanding how everything works much easier.
In order to set which spline is active, we need to add a few new functions to our Player Character, one will get called from the level blueprint, allowing nice flexibility to trigger different camera angles when appropriate. The other sets up the player controller so we can easily transition between cameras.
Set Player Controller
Before we are able to activate the spline we need our player controller variable. Add a Possessed event to the character graph and cast it to a player controller and new variable. I placed my cast within a setup player function, as I have other code here used for different gameplay, taking the controller from my Possessed Event as input.
Set Active Spline
Create the Set Active Spline function within you character blueprint and create the local variable for New Camera. Again, the local variable isn’t required but do make it look nicer and easier to maintain.
You will also need to create an Current Spline variable for the player character to always keep track of, set its type to our newly created Camera Spline actor class.
Within the function we need to check if the New Camera is the same as our Current Camera, if so we don’t need to do anything at all.
Next we check if we even have a Current Camera spline, and if we do, we need to deactivate it. Here we call our previously made function with our Camera Spline Class called deactivate.
Now that our previous camera spline is deactivated we can set our new one to the Current Camera variable and activate it. Note that we check to see if the variable is now valid or if it is null before activating it. If we pass a null variable we can still make it this far. This is useful if you want to use the default player camera as well. By branching off of the IsValid node, we can tell the player controller to use the actor, use the actor’s SELF variable, to return to the default camera if one exists. I have no need for this in the current game so I left it out.
Finally we have a View Target Blend that will shift our camera based on the time input to our function. If we set it to 0 the camera will instantly jump to the new one. Otherwise it will take the number as seconds and slowly shift over time. You can play with the settings to get the transition you like but I left them as shown for simplicity.
Most player movement is set to orient based on the default camera, however, we want it to be based on any camera! I do this by getting the camera manager from the player controller and using it’s rotation instead of the controllers.
Do this for all of your movement input. Here it is the ball’s torque but it is the same concept for other movement or vehicles.
Player Controller -> Camera Manager -> Get Camera Rotation -> Rotate Vector based on Yaw only -> Apply to Movement
Setting the Spline from the Level Blueprint
Now we are all set and done! Set the level blueprint to use a Camera Spline from within the level and it should be good to go!
In the instance below I use a small delay for the level BeginPlay event as the player controller for the character will not be created yet on level load. A slight delay makes sure it is ready. The other two events are from triggers placed in the level and referenced with OnBeginOverlap Events. This just means when the player rolls over a trigger it checks to see if its our player and then sets a specific camera also referenced from the level.
Again this method for checking where the player is according to a spline may not be the best but it is my current iteration with a simple camera spline system. Things that need to be added:
- Make sure camera transitions smoothly when we jump large distances within a spline
- Fix recursive check to be smoother
- Small glitches need to be found and fixed
- Make sure looping splines work
- Haven’t tried them yet and I’m sure will need something changed in order to work properly
- Allow the user to slightly rotate the camera
- Rotating slightly up, down, left and right allows some basic viewing of different areas. Control isn’t required by the player but allowing them to look around a bit is a nice touch!
- Create system that can use branching splines
- This may have to wait and see if Epic Games supports them like they did in UDK
- Clean up blueprint code
UPDATE: Hey all, I’ve started to go back through my previous posts to help improve any difficult parts as well as post the Blueprint assets. This post should be getting updated soon but here are the current assets tested in Unreal 4.8.3
NOTE: That this is not an Unreal Project but just the required .uassets for the camera system. It includes the Spline Camera and rolling ball assets. You will have to unzip the folder and add it to your own project’s content folder. If it isn’t working let me know and I’ll see what I can do to help!
Feel free to use the blueprints, learn from them, and make them better! Like I stated above these are incomplete and still have bugs to be fixed and features to be implemented.
If I get a chance I will update the assets with a project and comment everything out. Should have commented while I was creating it!
A special thanks to Pavel in the comments! For steering me toward learning more programming techniques. If you would like to improve the current system check out his comment below and see if you can create a true binary search!
Hope you enjoy and feel free to send feedback or questions as I moved over basic things quite quickly at times!