Transition between free look cams issue, and State Camera out-of-sync with animator

Hello! My name is Nicky – I run the channel iHeartGamedev over on youtube. I’ve made some in-depth tutorials over the past year covering Cinemachine and how amazing it is. My most recent video covered the Free Look Camera:

I’m currently attempting to recreate Mario’s camera system in Super Mario Odyssey and my analysis has brought me to the conclusion that we can recreate it with two separate “Free Look Cameras”. The main camera uses “Simple Follow with World Up” and the second uses “Lock To Target on Assign” to transition into a top down effect.

I have two issues that I’m trying to work through and I think that they are both bugs.

Using “inherit position” this setup works pretty well! The problem that I’m running into is that when the transition occurs, there is a single frame where the camera is in its previous position. Please see the following Gfycat where I step through each frame.

Frame by Frame:
reasonableunfitfennecfox

In Motion:
elaborateremarkableeelelephant

This happens in all orbit settings outside of “Simple Follow With World Up”. I’m uncertain if it’s occurring because of inherit position or something more specific to the orbit setting. But ideally when we transition from the “Simple Follow With World Up” camera to the “Lock to Target on Assign”, the camera has already moved to the correct position.

The second issue that I am having is related to the State Driven Camera. I’ve created two separate behaviors for each of these states (TopDown and ThirdPerson). In order to transition from one state to the other, we are check the Y Axis value and the Input Value Gain. If the conditions are met, we’ll switch to the other state.

My question here is related to getting a reference to the active camera. Ideally, we can do this in the OnEnterState method of the behavior by accessing the Cinemachine brain. That code looks something like this:

    CinemachineStateDrivenCamera _stateCam;
    CinemachineFreeLook _freeLookCam;
  
    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        _stateCam = Camera.main.GetComponent<CinemachineBrain>().ActiveVirtualCamera.VirtualCameraGameObject.GetComponent<CinemachineStateDrivenCamera>();
            _freeLookCam = _stateCam.LiveChild.VirtualCameraGameObject.GetComponent<CinemachineFreeLook>();
    }

However, I noticed that the brain is out-of-sync with the animator’s state machine. If we transition from one state to the other using animator.Play("TopDown"), the live camera of the Cinemachine State Driven cam will still be the “ThirdPerson” cam in the OnStateEnter method of the “TopDown”'s behavior.

I’ve gotten around this by checking the !_stateCam.IsLiveChild(_freeLookCam) in the OnStateUpdate method… but that is definitely not a good practice and I feel like the Brain should be updated by the time OnEnterState is called so that we can just get the reference there.

Is there a better way to get the reference to the camera that is associated with each state and state behavior of the State Driven Camera?

Appreciate the help in advance!

Hi Nicky,

First of all your video is awesome, thanks for that! We really appreciate the work you’re putting into this, and the excellent job you are doing at explaining Cinemachine.

The one-frame glitch definitely looks like a bug. We haven’t seen that before. What version of Cinemachine are you using? Would you be willing to share the test project with me? I’d like to step through it myself.

For the state-driven thing, I’m guessing that you’re checking the CMBrain too early in the frame. Because Cinemachine must respond to the movement of its targets, it updates very late in the frame to ensure that all game action has already taken place. It happens in the CMBrain’s LateUpdate function, and CMBrain by default has a late execution order.

A good way to use Cinemachine is to treat the cameras as slaves rather than sources. You set up game objects and move them around, then assign them as targets to the cameras and let the cameras do their thing. That leaves you free to tweak the behaviour of the cameras without affecting game logic. It’s unusual to be basing game logic on the state of the cameras, so can I ask: why do you need to get the current active FreeLook? Maybe there is a better way to accomplish what you’re trying to do.

Thanks very much! I’m happy to hear you all enjoy the videos.

Is there an easy way to upload a file? I’m getting an error that the zip is too large.

Did you delete the Library folder before zipping? It’s huge and Unity can regenerate it as needed. If the file is still too big after that, you can upload it to google docs or some other sharing site

Awesome. I did not know that about the Library folder.

I have gone ahead and created a brand new example project and was able to reproduce the issue. It is less consistent in the example project than in my own where it is happening 9 times out of 10, but it does still happen.

To summarize, there are two Free Look cameras: “ThirdPersonFreeLookCam” which uses SimpleFollowWithWorldUp and “TopDownFreeLookCam” which uses LockToTargetOnAssign. I have created a new input provider with a callback paired with the “C” key on your keyboard. Pressing “C” will switch the priorities of the cameras. You can see the logic that does this in the “CameraSystem” script which is attached to the “CameraSystem” gameobject.

The issue can be replicated when switching from the “ThirdPersonFreeLookCam” to the “TopDownFreeLookCam”. It does not happen when switching from TopDown to ThirdPerson.

Please let me know if this project opens correctly for you and if you have any questions.

You can see at 2.35 in this gyfcat there is a single frame where the camera isn’t correctly rotated, and it corrects itself in the following frame. It is easier to see it if you slow the playback down as much as possible.
defensivecloudyjanenschia

Interesting perspective to avoid using the cameras as sources. For the use-case I have here, it’s a little odd not to use their values though. You can see the logic I’m using to switch between these two cameras in the “CameraSystem” script, as well. It is just commented out. Essentially once we pass a certain threshold on the m_YAxis.Value, we switch to the other camera. Is this a bad practice?

Edit: Oh and the Cinemachine version is 2.8.6 in both projects.

8381586–1105242–FreeLookTest.zip (86.1 KB)

Thank you for that upload. Indeed there is a 1-frame glitch in the FreeLook transition. We will fix it.

Instead of using the CMBrain as a state variable, you can keep a separate state or just use the thirdPerson/topDown priority values to deduce the state. That way you’re decoupled from any lag in the brain.