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

How do you get the main active camera when using virtual cameras?

Discussion in 'Cinemachine' started by TheCelt, Oct 14, 2020.

  1. TheCelt

    TheCelt

    Joined:
    Feb 27, 2013
    Posts:
    741
    As per the title, when using virtual cameras my "Camera.main" is null.

    So how do i get the correct camera reference ?
     
  2. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    Cinemachine does not affect Camera.main, it should still work if you tag your camera as the main camera.

    upload_2020-10-14_7-37-6.png
     
  3. TheCelt

    TheCelt

    Joined:
    Feb 27, 2013
    Posts:
    741

    So this will point to the active virtual camera at any given time? What about when two virtual cameras have the same priority such as if you wanted split screen? Or does main camera not connect to virtual cameras but rather the gameobject that has the tag?

    I'm looking to obtain the active cameras specifically, so was not sure if I can still use camera.main or not for virtual cameras.
     
  4. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    A virtual camera is not a camera. It controls a camera. In a single-screen game, there is only one camera (the one with the CM brain on it). In a split screen game, there are two cameras, each with a CM Brain, and each being controlled by its respective virtual cameras.
    See here: https://docs.unity3d.com/Packages/com.unity.cinemachine@2.6/manual/CinemachineUsing.html

    Camera.main applies to the Unity camera, not to the virtual cameras. To get a current active virtual camera, get the CM Brain from the Unity camera, then query the CM Brain for the active virtual camera.
     
    Last edited: Oct 15, 2020
    TheCelt likes this.
  5. TheCelt

    TheCelt

    Joined:
    Feb 27, 2013
    Posts:
    741
    Ah okay, thank you :)
     
  6. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    If you have code that depends on the Camera position, you have to make sure to execute that code after CMBrain has positioned the camera. This happens in CinemachineBrain.LateUpdate. Note that CinemachineBrain has a late execution order, so you need to do this in LateUpdate in a script that has an execution order set greater than CinemachineBrain's. Alternatively, you can use CinemachineCore.CameraUpdatedEvent, which fires after the camera has been positioned.
     
    jeango and AndreaMar like this.
  7. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    @Gregoryl I've removed my previous post because I thought I've resolved it but I've not.

    Ok, I've tried to use the CameraUpdatedEvent to set the new position for my object but it's still flickering (only when the camera moves).

    Here's my code.
    Code (CSharp):
    1.     private Vector3 offset = Vector3.forward * 2f;
    2.     private readonly float duration = 2f;
    3.     private float time = 0f;
    4.     private void ShowToCamera (CinemachineBrain brain)
    5.     {
    6.         transform.position = Vector3.Lerp(transform.position, brain.OutputCamera.transform.TransformPoint(offset), time / duration);
    7.  
    8.         time += Time.deltaTime;
    9.         if (time > duration)
    10.         {
    11.             CinemachineCore.CameraUpdatedEvent.RemoveListener(ShowToCamera);
    12.  
    13.             GetComponent<Animator>().SetTrigger("ScaleDown");
    14.  
    15.             Destroy(gameObject, 1f);
    16.         }
    17.     }
     
  8. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    It's hard to tell what's going on without a little context.
    Can you explain how this script is being used? A video of the issue would also help.
     
  9. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    Here's a video. It's the gem I want to hook on the camera.

    When the player enters the trigger attached to the gem then I add the above code as a listener to the UpdatedCameraEvent.

    Attached here you can find the inspector for that gem and its child (a FBX model).
     

    Attached Files:

    Last edited: Jan 29, 2023
  10. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    Here is what I understand:
    • Gem is on the tile.
    • When player enters the tile, the gem should scale up and, over the course of "duration" should smoothly move towards a position 2 units in front of the camera.
    • At the end of "duration", gem stops tracking the camera and some kind of animation starts, and then the gem is destroyed one second later
    Is that correct?
     
  11. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    Not exactly.
    • The gem is on the tile.
    • When player enters the trigger attached to the child of the gem object (rigidbody is on the parent = gem object), then the gem doesn't scale up but move fast two units away in front of the camera and stays there for a moment.
    • Yes, at the end the gem stops tracking the camera and scales down (it takes 1 sec) before being destroyed.
    Here's how I set up Cinemachine.
     

    Attached Files:

  12. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    This code says that the transition to the point in front of the camera will take "duration":
    Code (CSharp):
    1. transform.position = Vector3.Lerp(transform.position, brain.OutputCamera.transform.TransformPoint(offset), time / duration);
    2.  
    However, I'm guessing that the problem is related to aliasing between render frames and FixedUpdate frames.
    What happens when you remove the RigidBody from the gem?
     
  13. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    Yes, right. :) However, the result feels like it's moving fast in front of the camera and stays there for a moment, because the starting point of the Vector3.Lerp() is the transform.position itself and it gets nearer and nearer. Not a very precise code but I like the result... unless it's the cause of the flickering o_O


    I've tried to destroy the rigidbody component as soon as it's triggered the gem collection. Same behaviour.
     
    Last edited: Jan 29, 2023
  14. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    @Gregoryl Ah. It only flickers if the frame rate is consistently lower than 1 / fixedTimeStep (50 cps). I've first noticed this behaviour on mobile, then reproduced this on Mac by setting the Application.targetFrameRate to 30 fps.

    I've then tried to debug it via Debug.Logs but I couldn't understand why it happens. There's more or less one Update cycle every two FixedUpdates. The target position hooked to the camera is changing every update and remains the same for FixedUpdate cycles. But now I'm using your CinemachineCore.updatedCameraEvent system.
     
    Last edited: Jan 29, 2023
  15. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    I don't understand why you need the RigidBody on the gem. You have a trigger SphereCollider on the child, right? And when the player enters that it launches your thing? What's the gem's RigidBody for?

    I ask about it because I'm trying to simplify the problem. You manipulating the transforms in LateUpdate on a RigidBody spooks me.
     
  16. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    Because if it is not for the Rigidbody I would not be able to have my OnTriggerEnter function called, since the Trigger Collider is on the child and I'm receiving the call OnTriggerEnter with a script attached to the parent.

    The reason why the trigger collider is on the child (with the mesh renderer) is because it was once a MeshCollider, which I then changed for a SphereCollider without really thinking about simplifying the object.
    Now I've tried to move the SphereCollider from the child to the parent, and I've also removed the Rigidbody from the parent. Just a SphereCollider on the parent, which has also the script attached. Same behaviour.
     
  17. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    What happens when you change the brain's BlendUpdateMode to FixedUpdate? That will cause your event handler to get called during FixedUpdate.
     
  18. AndreaMar

    AndreaMar

    Joined:
    Oct 29, 2019
    Posts:
    35
    That simply and beautifully works.

    @Gregoryl Why was that? Isn't that setting for blending between two different virtual cameras? I'm not changing camera here.

    I remember there was a reason why I didn't want to mess up with those settings. But I don't remember why I needed them that way. Well, let's see what comes up.

    Big thanks.
     
    Last edited: Jan 29, 2023
    Gregoryl likes this.
  19. Hazneliel

    Hazneliel

    Joined:
    Nov 14, 2013
    Posts:
    305
    Hello. I tried
    cinemachineBrain.ActiveVirtualCamera

    But this is not returning the active virtual camera. Instead it returns the parent of the virtual cameras which holds the CinemachineStateDrivenCamera.

    What is the correct way to get the active virtual camera?
    Thanks
     
  20. antoinecharton

    antoinecharton

    Unity Technologies

    Joined:
    Jul 22, 2020
    Posts:
    189
    Hey,

    StateDrivenCamera is an exception. In the code it is a Camera driven by other cameras.

    You could do something like that to find your camera.
    Code (CSharp):
    1. var currentCamera = CinemachineBrain.ActiveVirtualCamera().VirtualCameraGameObject;
    2. if(currentCamera != null) {
    3.    var nestedCamera =. currentCamera.GetComponent<CinemachineStateDrivenCamera>();
    4.    if(nestedCamera != null) {
    5.      currentCamera = nestedCamera.LiveChild
    6.    }
    7. }
    Let us know if this works for you.
     
    Hazneliel and Gregoryl like this.
  21. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,658
    I think you mean "it is a camera that drives other cameras".
    Note also that StateDrivenCameras can drive other StateDrivenCameras so the nesting can be deeper, depending on your situation.
     
    Hazneliel and antoinecharton like this.
  22. Hazneliel

    Hazneliel

    Joined:
    Nov 14, 2013
    Posts:
    305
    Thanks, this will help.
    And to Gregoryl's point, if I were to use nested StateDrivenCameras I understand I need to make a loop to traverse the hierarchy until I find the LiveChild.

    Thanks again for your help.
     
    antoinecharton likes this.
  23. jeango

    jeango

    Joined:
    Dec 19, 2012
    Posts:
    109
    Thank god, finally a good answer to this question. I keep seeing suggestions of using Script Execution order (doesn't work, and bad practice anyways), or people saying "For me, setting Blend Method to FixedUpdate solved it" (it does solve the issue, but it's a terrible solution).

    CameraUpdatedEvent solved this entirely for us. No need to fiddle with anything, just plain simple event-driven clean solution.
     
    Gregoryl likes this.