Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Can't get my State Driven Camera to behave correctly

Discussion in 'Cinemachine' started by ZeroThreeZero, Aug 30, 2020.

  1. ZeroThreeZero

    ZeroThreeZero

    Joined:
    Jul 12, 2020
    Posts:
    9
    First of all, thanks for all the hard work on Cinemachine. The tool is incredibly flexible and really lowers the bar for newbees like me to work with high-end camera setups.

    I'm building my first Unity game (a platformer) using a CM Freelook camera as the third person camera. I would like to recreate the exact same effect as seen in this video:


    In the video you can see the camera following the player on the X/Z axis instantly, but as soon as the player jumps, the camera does not follow the player until the players starts to get outside of the screen boundaries. I tried recreating this effect by using a State Driven Camera. So I duplicated my third person camera, renamed it to "Third Person Air Camera", changed the death zone height to 0.7 for all three rigs and created a blend tree:
    cm-blend-tree.png

    This works... kind of. Here is the result:


    As you can see, I'm not quite there yet. The biggest issue is that the air camera is still looking at (but not following) the player inside of the death zone. The second issue is that the state transitions feel jerky. I've spend quite some time tweaking the settings. Unfortunately, I'm unable to reproduce the camera movement as seen in the first video.

    Really hoping someone could share some light on how this can be done. Maybe my approach is off as well.

    Thanks in advance!
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,233
    A few things you can try:
    • Replace the FreeLook with a simple vcam with FramingTransposer in the Body, and POV in the aim (simpler, better performance, and closer in feeling to the original video)
    • Give some dead zone, so the character won't always get always recentered to the same spot
    • Enable InheritPosition in the vcams to smooth the transitions
    Something like this:

    upload_2020-8-31_18-31-41.png
     
  3. ZeroThreeZero

    ZeroThreeZero

    Joined:
    Jul 12, 2020
    Posts:
    9
    Thank you @Gregoryl . I'm now using two VCams and after some (alot) tweaking I managed to get very close to what I'm trying to achieve. However, there are some issues which I can't seem to fix with configuration. I made a video to help visualize the issues as they are somewhat difficult to write out.

    Setup
    So I have two VCams with a Framing Transposer as body and POV aim. The first cam "VCam" has a deadzone of zero and is the default player camera. The second cam "VCamAir" is activated when the player is airbone. This cam has the exact same settings, expect for the Dead Zone height and Y damping, which is set to a higher number. The idea is: when the player jumps, the camera won't follow instantly.

    The player can aim by touching/dragging the screen, so the Input Axis Name is set to "Look X/Y" (custom input delegate) for both cams. If I increase X/Y Damping (> 0) or increase the Deadzone Width (> 0) the aim is very jerky. Not yet sure what is causing this, so I went for no damping and no deadzone width for now.

    Demo

    From 0:20 to 0:30 you can see the desired camera effect in action, looks pretty cool if you ask me!

    Issues
    1) The air camera is zooming out slightly when the player jumps (0:05 - 0:10), which is not desired and causes a small flickering when the air camera is activated. I tried adjusting the Dead Zone Depth, but that causes other issues when moving forward, like you see me trying at (0:40 - 1:00). Basically, I want the camera to keep following the player on the X/Y axis, but not/slighty follow the player on the Z axis. Almost feels like I need to lock an axis, but that is oversimplified thinking :)

    2) Camera transitions can be very jerky when aiming whilst transitioning or when the VCam > VCamAir transition is still playing (0:35 - 0:40). I played with different state transitions and wait time, but can't get it to behave properly.

    Hope you can help me out.

    Thanks in advance!
     
    Last edited: Sep 2, 2020
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,233
    Nice progress!

    I assume you misspoke here, and that in fact you want the camera to steadily follow player on the Z axis. Am I correct?

    1) This is a known issue with FramingTransposer when the camera is at an oblique angle relative to target movement (the algorithm tries to preserve the worldspace distance camera->target, which changes with target vertical movement, when what you're wanting to to is to preserve the distance as projected onto the XZ plane). It can be prevented if you give it just enough Z dead zone to absorb this movement, but if you do that you should also do it on the normal player vcam, not just on the airborne one, to keep it consistent when it transitions. However, there are side effects to this, as you point out. I think it wouldn't be too hard to make a slightly modified FramingTransposer to add the ability to consider distance only on the XZ plane.

    2) It's because you're cutting to airborne when in the middle of transitioning airborne->normal. Does it help to set the transition airborne->vcam to cut? Or give a small (equivalent) blend to both?
     
    Last edited: Sep 2, 2020
  5. ZeroThreeZero

    ZeroThreeZero

    Joined:
    Jul 12, 2020
    Posts:
    9
    Good catch, that's correct indeed.

    1) That sounds like the solution. Is that something I can do? How would I go about making a custom FramingTransposer?

    2) But shouldn't the airborne camera be positioned in the same position as the cut when inherit position is enabled? That would be a really nice feature. Cutting the airborne to normal transition would lose the whole effect you're seeing from 0:20 to 0:30, I believe. I'll try to play with the transitions a bit more
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,233
    Let me play with it a bit and get back to you. Don't want to send you down any rabbit-holes.

    Yes, you're right, that should work. Let me look into it. It might be a bug.
     
  7. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,233
    @ZeroThreeZero Unfortunately I was overly-optimistic in thinking that the necessary change to the FramingTransposer is a simple one. It's not. The fact is that the dead zones on the FramingTransposer were intended to be used in a 2D context, and in 3D when the view is oblique it's going to give you some undesired up-down motion when the target moves within the dead zone. Fixing that would require changing the whole algorithm. So for oblique views such as this you'll need to set your vertical dead zone to 0.

    Which means we need to implement the jumping dead zone another way.

    Here's an idea: create an invisible GameObject to serve as an "anchor" for your player. By that I mean that every frame it positions itself to match the player, then drops a raycast downwards, stopping at a maximum distance or a collision surface, whichever comes first. That point becomes the Follow target for your vcam - effectively implementing a vertical dead zone.

    I made a little experiment to try it out, it seems to work well. I set it up like this:

    Anchor is a child of Player (doesn't need to be, but that's how I set it up):

    upload_2020-9-2_21-21-14.png

    Vcam targets Anchor instead of Player:

    upload_2020-9-2_21-23-34.png

    Anchor has one script:

    upload_2020-9-2_21-21-36.png

    It looks for surfaces on the specified layers, ignoring any colliders present in Player. Anchor chain length is specified here. This is the script:

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class PlayerAnchor : MonoBehaviour
    4. {
    5.     /// <summary>Attach anchor to this object.</summary>
    6.     [Tooltip("Attach anchor to this object")]
    7.     public Transform Player;
    8.  
    9.     /// <summary>Objects on these layers will be detected.</summary>
    10.     [Tooltip("Objects on these layers will be detected")]
    11.     public LayerMask CollideAgainst = 1;
    12.  
    13.     /// <summary>Will not get farther below the target than this</summary>
    14.     [Tooltip("Will not get farther below the target than this")]
    15.     public float ChainLength = 3;
    16.  
    17.     void LateUpdate()
    18.     {
    19.         if (Player == null)
    20.             return;
    21.  
    22.         var pos = Player.position;
    23.  
    24.         // Raycast down
    25.         var rayLength = ChainLength;
    26.         var ray = new Ray(pos, Vector3.down);
    27.         const float Epsilon = 0.001f;
    28.         float extraDistance = 0;
    29.         while (Physics.Raycast(
    30.             ray, out RaycastHit hitInfo, rayLength, CollideAgainst,
    31.             QueryTriggerInteraction.Ignore))
    32.         {
    33.             if (!hitInfo.transform.IsChildOf(Player))
    34.             {
    35.                 pos.y -= hitInfo.distance;
    36.                 transform.position = pos;
    37.                 return;
    38.             }
    39.  
    40.             // Ignore Player hits.  Pull ray origin forward in front of obstacle
    41.             Ray inverseRay = new Ray(ray.GetPoint(rayLength), -ray.direction);
    42.             if (!hitInfo.collider.Raycast(inverseRay, out hitInfo, rayLength))
    43.                 break;
    44.             float deltaExtraDistance = rayLength - (hitInfo.distance - Epsilon);
    45.             if (deltaExtraDistance < Epsilon)
    46.                 break;
    47.             extraDistance += deltaExtraDistance;
    48.             rayLength = hitInfo.distance - Epsilon;
    49.             if (rayLength < Epsilon)
    50.                 break;
    51.             ray.origin = inverseRay.GetPoint(rayLength);
    52.         }
    53.         pos.y -= ChainLength;
    54.         transform.position = pos;
    55.     }
    56. }
    57.  
    So now, vcam will look at the anchor, which will always be on the floor (or chain length units below the player if jumping high enough)

    upload_2020-9-2_21-28-41.png

    You can add an offset in Framing Transposer to put the yellow dot back on the player center, to match vcams that follow the player itself with no dead zone:

    upload_2020-9-2_21-29-53.png
     
    Last edited: Sep 3, 2020
    ShawnPile and ZeroThreeZero like this.
  8. ZeroThreeZero

    ZeroThreeZero

    Joined:
    Jul 12, 2020
    Posts:
    9
    Again, thank you very much for your time and effort.

    This feels like a more stable solution. First thing I will try after work.
     
    Gregoryl likes this.
  9. ZeroThreeZero

    ZeroThreeZero

    Joined:
    Jul 12, 2020
    Posts:
    9
    @Gregoryl Somehow I don't see the "Tracked Object Offset" option on the Framing Transposer body (see attachment).

    Just to make sure I'm following you correctly; I should get rid rid of the air camera, right? Just one vcam following the anchor. Also, am I suposed to lerp the Y position of the anchor to smoothen the camera movement?
     

    Attached Files:

  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,233
    Tracked Object Offset is a very new feature, only available in CM 2.6.2 and up (2.6.2 is available in preview only, but it's exactly the release candidate that will be the real 2.6.2, so it's safe).

    Use a vcam aimed at the anchor anywhere you would want a vcam to look at the player with a vertical dead zone. If you don't want a dead zone for a vcam, then use the player as a target instead of the anchor.

    I wouldn't lerp the anchor - let the vcams do their own damping (give them soft zones, but not dead zones). That way you can have different vcams with different settings.

    EDIT: Just thought of something. I don't know if your project is physics-based or not. If it is, and if you don't have interpolation on your player, then you'll need to modify the anchor script to work in FixedUpdate, or you might get juddery motion.

    ANOTHER EDIT: If you don't want to upgrade CM, then you can just build the vertical offset right into the anchor script (just add the offset in when setting the anchor's position).
     
    Last edited: Sep 3, 2020
    ZeroThreeZero likes this.
  11. ZeroThreeZero

    ZeroThreeZero

    Joined:
    Jul 12, 2020
    Posts:
    9
    Still running into some issues, I'll post an update when I made progress.

    "But shouldn't the airborne camera be positioned in the same position as the cut when inherit position is enabled?
    Yes, you're right, that should work. Let me look into it. It might be a bug."


    I found the cause of this issue, it happens when the camera's have damping enabled. The inherit transition option does not seem to take the current position into account while transitioning from a camera that has not reached its target/final position (i.e. its still damping). Instead, the new camera seems to start at the final position.

    To reproduce, I have two camera's (VCam and VCamAir). VCam has Y Damping set to 0, VCamAir has Y Damping set to 5. If the state changes while the transition is still playing, the active camera starts at the position where the previous camera would have been if the transition would not have been interrupted. Not sure it it's a bug though, but it feels a bit odd.
     
    Last edited: Sep 3, 2020