Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Track a character that runs around a tower

Discussion in 'Cinemachine' started by AntLewis, Mar 17, 2021.

  1. AntLewis

    AntLewis

    Joined:
    Feb 2, 2010
    Posts:
    254
    Hey there, wondering what would be the best way of setting up a VCam, so that it tracks a character's movement, as that character runs around a cylinder - similar to Mr Crab.
    Cheers for any help!
     
  2. marc_tanenbaum

    marc_tanenbaum

    Unity Technologies

    Joined:
    Oct 22, 2014
    Posts:
    637
    So this was a fun puzzle. Before launching in, I should say that you should ask yourself if it wouldn't make more sense to simply rotate the CYLINDER and leave the camera stationary, but that's not what you asked for, so here we go!

    I can't guarantee that this is the best way to make this happen, but I ended up solving it using a combination of three techniques:
    1. A dolly cam
    2. A (very simple) Cinemachine Extension
    3. A Target Group
    Step 0: Set up my environment
    I did everything with primitives, except the staircase, for which I used ProBuilder. Good enough to get a setup similar to your example. My main character is called "Hero Sphere". That'll be relevant later, but I wrote a simple character controller to allow it to jump, and to move left/right/forward/back relative to the camera.

    Screen Shot 2021-03-20 at 3.22.23 PM.png

    Step 1: Add a Circular Dolly Cam
    First technique: I Added a Dolly Camera With Track. Note that I checked the box that says "Looped" since what I want is for the camera to be able to perfectly orbit the tower.

    Screen Shot 2021-03-20 at 3.25.39 PM.png

    Step 1a: Wire up the camera to follow the Hero Sphere
    The Follow and LookAt should both be assigned to the hero sphere. Here are my settings...but you can pretty much ignore the 'Aim' section...we'll come back to that in Step 3. One important thing, though: ensure that you enable 'Auto Dolly'. This will make the camera always move as close as it can to the Hero Sphere, which keeps the camera on the correct side of the tower.

    Screen Shot 2021-03-20 at 3.31.47 PM.png

    At this stage, things work OK: the camera will follow the Hero Sphere and sweep around the track just fine, but there are two things not quite right. The first of these is that as you start ascending the tower, the camera will tilt up, and that's not what you want. You want it to move up with the Hero. This brings us to the second technique.

    Step 2: Write a (very simple) CinemachineExtension
    A CinemachineExtension is a bit of code that allows you to do something that Cinemachine doesn't do already. The team has built a LOT into Cinemachine, but there are too many possibilities for them to have built for every possible use case. So writing an extension allows you to get exactly what you need when it's not available off the shelf.

    In this case, I want my 'y' coordinate to follow the Hero Sphere, rather than be stuck to the dolly track. So here's some code that does this. I call this class DollyFreeY.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using Cinemachine;
    4. using UnityEngine;
    5.  
    6. public class DollyFreeY : CinemachineExtension
    7. {
    8.  
    9.     protected override void PostPipelineStageCallback(CinemachineVirtualCameraBase vcam, CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    10.     {
    11.         if (stage == CinemachineCore.Stage.Body)
    12.         {
    13.             Vector3 v3 = state.RawPosition;
    14.             v3.y = vcam.Follow.position.y;
    15.             state.RawPosition = v3;
    16.         }
    17.     }
    18. }
    As you can see, there's not a lot of code here. I'm just overwriting the 'y' coordinate with the position of whatever it is that the vcam is following.

    On your VCam, choose Add Extension (near the bottom of the Inspector window). You'll see our shiny, new DollyFreeY extension in the list. Select it and it'll be added to your Vcam.

    At this stage, we're really in pretty good shape. I could leave it here and call it done, but it doesn't quite feel right to me: the camera always follows the Hero Sphere, so it's possible for the tower to get completely lost from view if the Hero rolls away. I want to correct that, so I have one last technique to bring in.

    Step 3: Set up a Target Group
    They say you can't look at two things at once, but Cinemachine can, using a Target Group. I want the camera to always have both the Hero Sphere and the tower in the shot at all times.

    In order to accomplish this, I created a GameObject and added a CinemachineTargetGroup component to it. I added two targets. The first, of course, is the Hero Sphere, but what about the second? If I just look at the Tower, the camera will try to keep the origin of that GameObject in view, which is no good as the Hero ascends the Tower. We want the camera to always see the tower, at the same y position as the Hero. I suppose I could have written another extension to get this effect, but I found a much simpler way to do it.

    I created another GameObject called 'Center Target'. It's an invisible GameObject onto which I attached another really simple script:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class MaintainHeight : MonoBehaviour
    6. {
    7.     // What to watch (in our case, the Hero Sphere)
    8.     public Transform target;
    9.     void Update()
    10.     {
    11.         // Make our y position match the target
    12.         var v3 = new Vector3(transform.position.x, target.transform.position.y, transform.position.z);
    13.         transform.position = v3;
    14.     }
    15. }
    The Center Target GameObject is placed inside the center of the tower, and its 'y' position is always the same height as its target, in our case, the Hero Sphere.

    Now we wire up our TargetGroup like so...

    Screen Shot 2021-03-20 at 3.53.22 PM.png


    Step 3b: Aim the VCam at the TargetGroup
    ...and change our camera to look at the TargetGroup. Remember before when I said not to worry too much about the Aim settings? This is why. We're going to change our Aim from Composer to Group Composer, which ensures that everything the TargetGroup tracks (Hero Sphere and Center Target) will get taken into account.

    Note that for my setup, I found it particularly helpful to adjust the Minimum FOV to an appropriately comfortable distance. Your setup might be different.

    Screen Shot 2021-03-20 at 4.02.15 PM.png

    The net result is, I think, a pretty decent tower camera system.

    Here it is in action:


    It was a fun project, hope it's helpful to you and to others.
     
    Last edited: Mar 20, 2021
    Reanimate_L and Gregoryl like this.
  3. bishylewis

    bishylewis

    Joined:
    Oct 10, 2013
    Posts:
    1
    Wow! Thanks for much for going into all this detail, what an absolutely fantastic response (and solution)! Much appreciated, I learned a lot!
    Many Thanks
     
    marc_tanenbaum likes this.
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    @marc_tanenbaum That's a good solution and a most informative answer. For completeness, I'd like to hint at an alternative solution, consisting of 2 parts:
    1. A vcam with a Framing Transposer following the crab
    2. A custom CM extension that sets the camera yaw to always point to the tower
    One benefit of this approach is that you can use the FT's dead and soft zones to get a softer follow on the crab.
     
    marc_tanenbaum likes this.
  5. AntLewis

    AntLewis

    Joined:
    Feb 2, 2010
    Posts:
    254
    Any chance you could provide a script example for that extension @Gregoryl ? Minimal experience writing cinemachine extensions!
    Thanks!
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    Sure. Here you go. Drop it in your project, and it will be available in the vcam's Extensions dropdown.
    Code (CSharp):
    1. using UnityEngine;
    2. using Cinemachine;
    3.  
    4. [AddComponentMenu("")] // Hide in menu
    5. [ExecuteAlways]
    6. public class PanToTarget : CinemachineExtension
    7. {
    8.     [Tooltip("Rotate the camera horizontally to look at the column defined by this object")]
    9.     public Transform Target;
    10.  
    11.     /// <summary>Callback to tweak the settings</summary>
    12.     /// <param name="vcam">The virtual camera being processed</param>
    13.     /// <param name="stage">The current pipeline stage</param>
    14.     /// <param name="state">The current virtual camera state</param>
    15.     /// <param name="deltaTime">The current applicable deltaTime</param>
    16.     protected override void PostPipelineStageCallback(
    17.         CinemachineVirtualCameraBase vcam,
    18.         CinemachineCore.Stage stage, ref CameraState state, float deltaTime)
    19.     {
    20.         if ((stage == CinemachineCore.Stage.Aim || stage == CinemachineCore.Stage.Body) && Target != null)
    21.         {
    22.             var dir = Target.position - state.CorrectedPosition;
    23.             dir.y = 0;
    24.             var yaw = Vector3.SignedAngle(Vector3.forward, dir, Vector3.up);
    25.             state.RawOrientation = Quaternion.Euler(0, yaw, 0);
    26.         }
    27.     }
    28. }
    29.  
    Use it like this:

    upload_2021-3-26_10-25-8.png
     
    Last edited: Mar 26, 2021
  7. AntLewis

    AntLewis

    Joined:
    Feb 2, 2010
    Posts:
    254
    Absolutely superb thanks!
     
    Gregoryl likes this.
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    Hang on - found a small glitch. Edited the code in my previous post.