Camera Follow Player Using Path

Hi, it is been a while that I don’t come here, so I need you help with a question about a code that I trying to implement for the past 2 weeks. I saw a lot of tutorials and tryed a lot of things but I am not a code expert. I have this first code that I learned in this tutorial.

 public Transform[] nodes;
       private int nodeCount;
       public int nearstnode;
  
  
       // Use this for initialization
       private void Start () {
  
           nodeCount = transform.childCount;
           nodes = new Transform[nodeCount];
  
           for (int i = 0; i < nodeCount - 1; i++) {
               nodes [i] = transform.GetChild (i);
           }
  
       }
      
       // Update is called once per frame
       private void Update () {
           if (nodeCount > 1) {
               for (int i = 0; i < nodeCount - 1; i++) {
                   Debug.DrawLine (nodes[i].position, nodes[i+1].position, Color.green);
               }
           }
       }
  
       public Vector3 ProjectPositionOnRail(Vector3 pos){
           int ClosestNodeIndex = GetClosestNode(pos);
           if (ClosestNodeIndex == 0) {
               return ProjectOnSegment (nodes [0].position, nodes [1].position, pos);
           } else if (ClosestNodeIndex == nodeCount - 1) {
               return ProjectOnSegment (nodes [nodeCount-1].position, nodes [nodeCount-2].position, pos);
           } else {
               Vector3 LeftSeg = ProjectOnSegment (nodes [ClosestNodeIndex - 1].position , nodes [ClosestNodeIndex].position, pos);
               Vector3 RightSeg = ProjectOnSegment (nodes [ClosestNodeIndex + 1].position, nodes [ClosestNodeIndex].position, pos);
               Debug.DrawLine (pos, LeftSeg, Color.red);
               Debug.DrawLine (pos, RightSeg, Color.blue);
  
               if ((pos - LeftSeg).sqrMagnitude <= (pos - RightSeg).sqrMagnitude) {
                   return LeftSeg;
               } else {
                   return RightSeg;
               }
           }
       }
  
       private int GetClosestNode(Vector3 pos){
           int ClosestNodeIndex = -1;
           float ShortestDistance = 0.0f;
           for (int i = 0; i < nodeCount; i++) {
               float sqrDistance = (nodes [i].position - pos).sqrMagnitude;
               if (ShortestDistance == 0.0f || sqrDistance < ShortestDistance) {
                   ShortestDistance = sqrDistance;
                   ClosestNodeIndex = i;
               }
           }
           nearstnode = ClosestNodeIndex;
           return ClosestNodeIndex;
       }
  
       public Vector3 ProjectOnSegment(Vector3 v1, Vector3 v2, Vector3 pos){
           Vector3 v1ToPos = pos - v1;
           Vector3 segDirection = (v2 - v1).normalized;
           float DistanceFromV1 = Vector3.Dot (segDirection, v1ToPos);
           if (DistanceFromV1 < 0.0f) {
               return v1;
           } else if (DistanceFromV1 * DistanceFromV1 > (v2 - v1).sqrMagnitude) {
               return v2;
           } else {
               Vector3 fromV1 = segDirection * DistanceFromV1;
               return v1 + fromV1;
           }
       }

and the code to control the camera along this rail:

    public SCRIPTS_CameraRail Rail;
       public Transform Player;
       public float MoveSpeed = 5.0f;
       public Transform thisTransform;
       public Vector3 LastPosition;
   
       // Use this for initialization
       private void Start () {
           thisTransform = transform;
           LastPosition = thisTransform.position;
   
       }
   
       // Update is called once per frame
       private void Update () {
   
   
       
           LastPosition = Vector3.Lerp (LastPosition, Rail.ProjectPositionOnRail (Player.position), Time.deltaTime * MoveSpeed);
           thisTransform.position = LastPosition;
   
       }

this is a pretty god system but I was trying to make this rail becomes smoother with curves, so I saw this tutorial:

and tryed to implement the curve calculation in the rail system, the math works pretty fine, but I never figured out how to implement the calculation in the rail system code.

some weeks later I found this tutorial:

and implemented the code:

   public Transform[] nodes;
       private int nodeCount;
   
       // Use this for initialization
       void Start () {
           
           nodeCount = transform.childCount;
           nodes = new Transform[nodeCount];
   
           for (int i = 0; i < nodeCount; i++) {
               nodes [i] = transform.GetChild (i);
           }
           
       }
       
       // Update is called once per frame
       void Update () {
   
       
           
       }
   
       public Vector3 LinearPosition(int seg, float ratio){
           Vector3 p1 = nodes [seg].position;
           Vector3 p2 = nodes [seg + 1].position;
   
           return Vector3.Lerp (p1, p2, ratio);
       }
   
   
       public Vector3 CatmullPosition(int seg, float ratio){
   
           Vector3 p1, p2, p3, p4;
   
           if (seg == 0) {
               p1 = nodes [seg].position;
               p2 = p1;
               p3 = nodes [seg + 1].position;
               p4 = nodes [seg + 2].position;
           } else if (seg == nodes.Length - 2) {
               p1 = nodes [seg - 1].position;
               p2 = nodes [seg].position;
               p3 = nodes [seg + 1].position;
               p4 = p3;
           } else {
               p1 = nodes [seg - 1].position;
               p2 = nodes [seg].position;
               p3 = nodes [seg + 1].position;
               p4 = nodes [seg + 2].position;
           }
   
           float t2 = ratio * ratio;
           float t3 = t2 * ratio;
   
           float x = 0.5f * ((2.0f * p2.x)
               + (-p1.x + p3.x)
               * ratio   + (2.0f * p1.x - 5.0f * p2.x + 4 * p3.x - p4.x)
               * t2 + (-p1.x + 3.0f * p2.x - 3.0f * p3.x + p4.x)
               * t3);
   
           float y = 0.5f * ((2.0f * p2.y)
               + (-p1.y + p3.y)
               * ratio + (2.0f * p1.y - 5.0f * p2.y + 4 * p3.y - p4.y)
               * t2 + (-p1.y + 3.0f * p2.y - 3.0f * p3.y + p4.y)
               * t3);
   
           float z = 0.5f * ((2.0f * p2.z)
               + (-p1.z + p3.z)
               * ratio + (2.0f * p1.z - 5.0f * p2.z + 4 * p3.z - p4.z)
               * t2 + (-p1.z + 3.0f * p2.z - 3.0f * p3.z + p4.z)
               * t3);
   
           return new Vector3 (x, y, z);
       }
   
       public Quaternion Orientation(int seg, float ratio){
   
           Quaternion q1 = nodes [seg].rotation;
           Quaternion q2 = nodes [seg + 1].rotation;
   
           return Quaternion.Lerp (q1, q2, ratio);
       }

and the camera controller:

    public SCRIPTS_CameraRail2 Rail;
       public Transform PlayerTransform;
   
       public int currentSeg;
       private float transition;
       private bool isCompleted;
       public float PlayerToNode1Distance, PlayerToNode2Distance, NodeDistance, d;
       public Vector3 Line, LineStart, LineEnd, Point, Dir;
   
   
       // Update is called once per frame
       void Update () {
           if (!Rail) {
               return;
           }
           if (!isCompleted) {
               Play ();
           }
       }
   
       private void Play(){
           
   
           transition += Time.deltaTime * 1 /2.5f;
           if (transition > 1) {
               transition = 0;
               currentSeg++;
           }else if(transition < 0){
               transition = 1;
               currentSeg--;
           }
           transform.position = Rail.CatmullPosition (currentSeg, transition);
           transform.rotation = Rail.Orientation (currentSeg, transition);
       }

In this second code I know that I need to change the transition value to move the camera, but dont know how to make this happen based on the player position.

So I have one code that moves based on the players position but have no curves, and a second code that move with curves but dont move based on the players position. how can I make this 2 things in only one code? move based on players position true the rail with curves to make the camera movement smoother.

thanks for any help!

1 Like

I would suggest using Cinemachine for this. The track and dolly feature does exactly what you’re looking for.

Check out the Track & Dolly tutorial here:

If you’re using Unity 2018.1 or higher, you should pick up Cinemachine from the Package Manager.

2 Likes

Hi Intense_Gamer9,

thanks for your help I will take a look in the cinemachine, but I was trying to make one code for this to larn how to do. so I will wait a bit longer to see if any one can help with the code.

thanks!

Just going to leave this here…
https://docs.unity3d.com/ScriptReference/Vector3.Slerp.html

Hi Collin,

In the slerp I have the same problem that in the second code I posted, How make the transition happens based on players position?

Thanks in advance!

If that doesn’t do it for you, I would suggest Cinemachine as well. I believe it has functionalities to track an object along a path and will do those curves you are wanting.

Hi Intense_Gamer94,

I was taking a deeper look at Cinemachine and this asset is great, but I have some doubts and dont found any tutorial about this. I tryed to make what I need and the result was this
https://imgur.com/Qs6YX22

I need the camera view follow the dolly track, like you can see in the image the camera view is pointing to z world direction and I need make this curves in the game and need the camera follow this curves with the view.

here is an exemple of the kind of camera system I am trying to do.

https://www.youtube.com/watch?v=OyFmYvVqJAA

Edit: I found now in the cinemachine a function called dolly track with cart, and this is what I looking, but now I need to know how use this based on the player position. if anyone know I will be glad.

thanks in advance!

I think this might be what your looking for.
https://unity3d.college/2017/07/20/unity-cinemachine-unity-2017-auto-dolly-camera/

Hi Collin,

I did the same as the tutorial before and this result does not work for me, I need the camera view stay fixed on the dolly track so if the track has a curve, the camera view will point to this new direction after the curve, is exactly what I found on the dolly track with cart function, but in this function I dont found the option to move the camera based on the player position…

thanks in advance!

Hi Collin and Intense_Gamer94,

I found what I need, I just changed the option from default to path and the cinemachine did the trick. thanks a lot for your help!

I will let the image here just in case someone need the same behavior that I was trying to make.

thanks again!

1 Like

Take a look at this example I made. load it into a project with Cinemachine installed.

Edit: Looks like you beat me to it. I’m still going to leave this here for anyone else who needs an example.

3556333–286273–CinemachineTestPackage.unitypackage (75.2 KB)

1 Like

One quick question on Cinemachine… by flicking through the documentation I got the impression that it was only used for generating scripted/animated cut scenes. Can it also be used for in-game camera control? I soon need to implement a camera system with a lot of motion along rails, and some follow behavior, and stationary camera shots, plus smooth switching between them. Would Cinemachine be something to look into for this?

I don’t see why not. Don’t let the name fool you, it’s quite versitile. It’s essentually just procedural camera animation by following a player/object. What makes it useful for cinematics is the ability to link the cameras in a timeline to enable/disable them for different shots. Other than that, they make easy camera controllers.

I think the name put me off, and the size… it looks rather intimidating. But I’ve been playing with it all afternoon and learned a lot already :slight_smile:

2 Likes

Many thanks, This is exactly what I was looking for.
After miserably failing for 2 days straight, writing the camera + waypoint script. I was able to create a waypoint system and camera that follows the player. But the camera could never move at correct speed as per the speed of my player.

I got this working within minutes using cinemachine > Dolly camera with track > turn on Auto dolly.

Thanks a lot!