Let’s Make: UE4: Camera Spline System
A Camera that follows a user created spline based on the player location
Overview
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.
https://docs.unrealengine.com/latest/INT/Engine/Blueprints/Overview/index.html
https://en.wikipedia.org/wiki/Recursion_(computer_science)
I also have a TLDR version of only pictures here:
Setup
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:
- Billboard
- 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
- Camera
- 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.
In game the rotation won’t always look at the spline but the player character. However here, looking at the spline location gives us an approximation of what the view will be.
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.
Checking Debugging on each spline now allows us to see the numbers above each point.
The Tick
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 reasons to avoid the brute force are fairly apparent:
- 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.
Note that we use a number of local variables to make the graph look a bit nicer, just be careful not to use the wrong variable for the wrong purpose.
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.
Update Camera
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.
Player Character
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.
Player Movement
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
Download Assets:
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!
at times!
i have a couple questions:
1. Recursive Distance Check Function:
– How do you bring up that node that connects the actor location and the world location together for the vertex length?
2. In the “Upgrade Group”:
– How do you get the “Inputs” and “Outputs” at the beginning and end?
– How do you apply the “best time” variable into the “Recursive Distance Check”?
3. Would you happen to be willing to do Blueprints for a Survival horror game that me and my very small team are working on (Super Indy Status) for Industry Credit (with potential money on the back end of course). We’re looking to get on Greenlight soon, but we want to create a really good trailer first.
Thank you,
James
Hey James!
1. I believe you are talking about getting the spline world location and the actor location to get the distance between the two, correct?
If so we use our SplinePlayer variable and drag a line out, letting go over empty space. This brings up the list of functions we can call from the variable and in our case we will use the GetWorldLocationAtTime function. This just returns a vector location we can use to check against our actor location.
Once we have both of those we are simply subtracting (Vector-Vector) and using the Length node to get the distance between the two locations. This distance is what we want to check against, looking for the closet point on the spline to the player.
2.Whenever we need to add inputs or outputs on a function or group we simply select the group. Once selected the details panel will show categories for both and allow you to add new inputs or outputs / name them and give them a variable type. Once added you can connect them as shown!
2A. When it comes to how to apply the BestTime, we are returning it out to the variable just outside of the Recursive function named SplineDesiredTime and use it in the next few functions. While within the function itself we simply keep track of it as a local or temporary variable that doesn’t exist outside of the function. We use the return BestTime at the end to make sure it gets set outside of the function to our SplineDesiredTime.
Let me know if that makes sense!
3. Right now I have a number of contracts I am currently investing most of my time with. However, if you need help with any blueprints or want to get feedback I can steer you in the right direction; but I am unable to take on another job at the moment!
Feel free to email me at greg@gregmladucky.com
Cheers!
Your “Recursive Check” is just a binary search – a fine choice. But instead of using iteration count it may be better to use an error tolerance value that computes the difference from your current and last value candidates and you last value candidate is ether the latest min, or max, value in the binary search loop iteration.
Using error tolerance value instead of iteration count will give you better results and, given reasonable error tolerance value, the algorithm will converge in logarithmic time and it’ll be fast enough.
Look up binary search on wikipedia, there are many iterative implementation shown there. It’ll probably also result in a lot less blueprint node once the implementation is done.
Very cool! I’ll have to check it out and see if I can implement it soon!
I’m normally a designer but have slowly picked up a bit of programming over the years so I’m always glad learn better ways to do what I need!
I’ll check it out and see if I can update my post / create a new one as I figure out a better system. I still have a number of other bugs and issues that need to be fixed as well so I might end up doing more research and completing out a nice small camera system.
Hello, I enjoyed the tutorial, managed to create all the functions but I could not do the topic “Set Player Controller,” I lost :), so my camera if mech when I change the position of the node 0 the spline Player. you would have to do the project download? grateful
Hey Claudio, I just updated my tutorial with the current Unreal Assets! Check out the link at the bottom of the tutorial and see if those will work for you! If you have trouble downloading them or if they give errors when added to your project let me know!
The section “Set Player Controller” is setup within my own function. Instead all you need to do is right click in the event graph and add in the Event titled “Possessed”. This event gets called when the player controller or AI controller possesses it. In our case, we are using it to grab the player controller and store it in the variable. So we hook it up to the cast, getting our player controller, and then setting our variable.
After that, the player controller section should work!
Again, check out the files and see if it makes sense to you!
I too am still unclear about the setup player function; mostly because I cannot see the end result of the node chain with the picture given. Not trying to admit that I just use the pics as the main tutorial but I was able to guess a lot of things that you did not mention in your tutorial up till then such as when a new variable is needed or when things went above my head.
Anyway Do I put the Possessed event in the Character player blueprint? or keep it to the camera’s?
Hi Greg, this tutorial was great and I learnt quite abit from it (quite a few things are over my knowledge level though!). I believe that I was able to setup just about everything that you instructed but I cannot seem to get the camera to actually scrub whilst in game.
The camera scrubs fine using the Camera Debug Time in the editor but only tracks the player pawn when in game rather than following it too (The only changes I made were to the player pawn variable which I set to my own pawn BP).
It may be some simple mistake on my end that I missed so I will go over everything again but some help would be appreciated!
Hey Poji glad you liked the tutorial but hopefully I can help you fix the issue.
First off we just need to understand where the problem is coming from. The easiest way is to put in breakpoints and check where our data is being lost or if it’s not being applied correctly.
In your case we have the camera movement not updating but the rotation working fine so its not likely that we aren’t applying it but instead are loosing data from the recursive function or not setting one of the vectors like the Desired Camera Location.
I’d say to place breakpoints in the code at a few points and see where the vector or other data is being lost. (You can do this by selecting a node and pressing F9. Check out more on how to debug blueprints from Epic games if you need help with that: https://docs.unrealengine.com/latest/INT/Engine/Blueprints/BP_HowTo/Debugging/)
Once the break point is hit you can hover over the vector in blueprint and start to figure out where something is going wrong. You will most likely have to add in a few break points in each of the functions to see where the issue is. Make sure to check if the location you are getting from the player and any other inputs are correct as well.
Otherwise feel free to either send me the file and I can check it out / email you when I find the issue or grab my files located at the bottom of the post and see if they help you!
I had the same issue. I had left the max iterations value at zero, setting it to ten solved that issue.
Hi, Thanks for your great tutorial, but why I cannot find get world Location at time function? All I can find is get world location function?
Thanks
Hey Ling, I believe the node was updated by epic so the previous one is now deprecated. The spline location nodes are still there but have been combined into a single node with the option to choose world or local space. Unfortunately I am away from my computer for the next few days but I believe it’s now just called “get location at time”. It works the same way as the previous one, just make sure the node is set to world space and it should work!
Hope that helps and I should be updating these tutorials soon to reflect those engine changes.
Hey!
Great job for this awesome tutorial, it really helped me and everything works fine. You wrote about how to return to the default camera, but I don’t get what nodes I should use. I understand I have to branch it off of the IsNotValid output in the ActivateCamera function, then I CastToPlayerController and I’m not sure what I should do next? Can you help me please? 🙂
Really great job, this is the most useful tutorial on spline camera I found so far!
P.S. I use a default sidescroller project
Nervermind I got it! 😀
Glad you got it! The Camera Spline tutorial is one I’d like to update soon as well. Hopefully making the system much easier to create.
Interesting article that I’m trying to rebuild right now. I stumbled across the RecursiveDistanceCheck when I realized what we are doing. Also saw the logarithm attempt, but the first thing that came into my mind “why aren’t we just taking the direction(vector) of the Player and compare it to the direction to the spline. Depending on the offset (I think it’s just another dot product) we add or subtract the distance on the spline?
Hi! Have you rewritten this article after all? I am curious to check another perspective!
Hello, Greg
Great turorial, but i have some issue adapting this to my project.
Character control is built in a click-to-move way, as in top-down game template.
Camera works fine except one moment – when character reaches destination point given by mouse click, camera twitches a little, trying to adjust rotation.
Any idea what to do with it? Playing with tresholds don’t give desired result at all…
found by myself, issue was caused by delta time in update desired time function
What i do not realize is in truth how you are now not really much more well-liked than you
may be right now. You are so intelligent. You already know thus significantly
on the subject of this topic, produced me personally believe it
from so many varied angles. Its like men and women don’t seem
to be involved unless it is one thing to do with Woman gaga!
Your individual stuffs outstanding. All the time maintain it up!
Hey there. First of all: Thanks for this. I’ve been trying to find an explanation on how this works for ages. However…
I’m experiencing the same error as Mr Poji Chow. My Debug doesn’t move with the player and to add to that the camera doesn’t even aim at the player. But this only happens while looking through the spline camera. Since all events (camera location, debug spheres) seem to be responding as long as I am looking through the standard player camera and have the “Look at track” enabled on the spline Camera. And yes… I’m using the cinema camera.
And that might be the problem? I’ve been creating this with the cinemaCamera and the aim functions of this camera only seem to work when I enable “Look at track”. Might that be messing up the whole blueprint?
Solved! It was a variable problem. The “activate Camera” -function “actor” input had to be BP Camera.
Hi, I followed your tutorial to the end, I’m having a problem setting it up with the default 3rd person character
Can’t seem to figure out how to set the movement of the camera in the Character controller blueprint
okay i did set it up but the camera isn’t following my player it doesn’t even follow the spline if i move it in play mode.
How do I get the camera to actually work? It always uses the default 3rd person camera
the download line doesn’t work anymore, bummer
Hi. Thanks for this amazing tutorial, but I have become stumped on the Recurisve Distance Check.
You say to set up Iteration and Start and End Time variables, but don’t state what they should be set to. I went to download the files you attached to this tutorial to check but they aren’t here any more.
Hopefully you’re still getting messages as I would LOVE to get this solved.
Effectively my num variable is shooting all over the place because at the second branch it somehow thinks it’s both True and False that num <= 0
Any help would be amazing.
Just wanted to thank you for this, it’s got me past a sticking point in my project. I’d attempted various solutions to this issue over the last few evenings and whilst I got close (cameras would move smoothly at times, but stutter when my player stopped moving), I couldn’t quite get there. I’d suspected a second spline would be needed, and sure enough your method works perfectly 🙂