DestroyCinemachineComponent errors in 2.1.12 (wasn't in 2.1.10)

Hi,

Im wondering what the best way to change cinemachine components at runtime is or if I have I found a bug.

Basically, I’ve been setting up a scripted cinemachine environment for a game where every camera has identical settings. The cameras are all generated at runtime by creating CinemachineFreelook cameras and adjusting them as appropriate with common settings (it would be really nice to have something to handle common settings in the released code BTW).

Some of my CinemachineFreelook cameras need to point at groups of GameObjects. This, I found from nosing about in the API was fairly easy to do by swapping out the CinemachineComposer for the CinemachineGroupComposer (which looking at/follows a runtime created CinemachineTargetGroup). The component swapping is done like this example snippet:

  // freeLook is a CinemachineFreelook.
  vcams[i] = freeLook.GetRig(i).GetComponent<CinemachineVirtualCamera>();

  // original method from 2.1.10
  vcams[i].DestroyCinemachineComponent<CinemachineComposer>();
  composers[i] = vcams[i].AddCinemachineComponent<CinemachineGroupComposer>();
                   
 ... go on to setup things...

This worked brilliantly in v2.1.10 and several previous versions, however, in v2.1.12 it fails with a lengthy stack trace.

MissingReferenceException: The object of type ‘CinemachineComposer’ has been destroyed but you are still trying to access it.
Your script should either check if it is null or you should not destroy the object.
Cinemachine.CinemachineComposer.get_IsValid () (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Components/CinemachineComposer.cs:115)
Cinemachine.CinemachineComposer.PrePipelineMutateCameraState (Cinemachine.CameraState& curState) (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Components/CinemachineComposer.cs:172)
Cinemachine.CinemachineVirtualCamera.CalculateNewState (Vector3 worldUp, Single deltaTime) (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Behaviours/CinemachineVirtualCamera.cs:432)
Cinemachine.CinemachineVirtualCamera.InternalUpdateCameraState (Vector3 worldUp, Single deltaTime) (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Behaviours/CinemachineVirtualCamera.cs:133)
Cinemachine.CinemachineCore.UpdateVirtualCamera (ICinemachineCamera vcam, Vector3 worldUp, Single deltaTime) (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Core/CinemachineCore.cs:244)
Cinemachine.CinemachineCore.UpdateAllActiveVirtualCameras (Vector3 worldUp, Single deltaTime) (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Core/CinemachineCore.cs:172)
Cinemachine.CinemachineBrain.UpdateVirtualCameras (UpdateFilter updateFilter, Single deltaTime) (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Behaviours/CinemachineBrain.cs:439)
Cinemachine.CinemachineBrain.LateUpdate () (at /Users/martin/Library/Unity/cache/packages/packages.unity.com/com.unity.cinemachine@2.1.12/Runtime/Behaviours/CinemachineBrain.cs:393)

Now, when the game is running in editor mode, simply clicking on the generated CM camera that is emitting these errors fixes it and the game runs normally. So something else must be happening.

In CM v.2.1.10, inside CinemachineVirtualCamera.cs DestroyCinemachineComponent implementation the method uses Unitys DestroyImmediate() to destroy the component.

However, in CM c.2.1.12 this has been replaced by a RuntimeUtility.DestroyObject() that uses Destroy() if the application is playing. Hacking this change out removes the stack traces and also makes the game run normally [as does having a local re-implementation of the v2.1.10 code in my csharp].

So presumably, the parts of CM in the stack trace above have trouble with not destroying the object immediately.

My question is really, is this a bug introduced by the changes in DestroyCinemachineComponent? or is my approach completely flawed because there is a better way that I am unaware of?

Kind regards,
Martin.

Hi Martin,

Interesting! I’m not able to reproduce this with CM 2.1.12 and Unity 2018.1. I opened the “Scripting” scene in the CinemachineExamples package, and modified it to do what your snippet was doing. It works without a hitch! Normally, Destroy() should be called in runtime instead of DestroyImmediate(), so the change you mentioned should be safe. I’m wondering if you’re doing something elsewhere that could be causing the problem as a side-effect. Could you make a little demo project and send it to me?

For the lots-of-vcams-with-identical-settings thing, have you tried making a prefab?

Regards,
Greg

It could very well be something else. I will have a go reproducing it in a small demo project. Thank you for looking at it. I’m assuming that it is what you are meant to do to change the composer component.

Using a prefab would be far to easy!.. Joking aside, I did try a prefab based approach, my notes mention that I gave up because of some kind of other error that I cant remember at the moment. For me, the idea of changing the composers over and retaining settings seems the most elegant.

Hi,

Not entirely sure how to send you the scene [any hints?]. Im a bit new to these forums.

The following script has the same effect.

  • Create a new Unity project.
  • Import CM 2.1.12 via the package manager.
  • Add in a cube and a sphere spaced a little apart.
  • Add in an empty game object then add the script below to it as a component.
  • Assign the cube and sphere to the cube and sphere public inspector values.
  • Run the script and you will get the null reference exceptions haemorrhaging in the console log.
  • Now (whilst the script is running) click on the freeLook camera object. The exceptions halt in the console
    and the scene now displays a freelook-esque camera that has a cube and sphere in its sights.
using System;
using UnityEngine;

using Cinemachine;

public class CMFreelookBug : MonoBehaviour {


    // Two scene objects.
    public Transform cube;
    public Transform sphere;


    // Camera references.
    public GameObject cameraObject;
    public CinemachineFreeLook freeLook;
    public ICinemachineCamera iCamera;
    public GameObject targetGroupObject;
    public CinemachineTargetGroup targetGroup;


    // Unit Hook.
    void Start()
    {
        // Stuff a brain onto the camera.
        Camera.main.gameObject.AddComponent<CinemachineBrain>();

        // Create a gameObject for the camera.
        cameraObject = new GameObject("Freelook");

        // Add a freelook camera.
        freeLook = cameraObject.AddComponent<CinemachineFreeLook>();

        // Get the interface for this camera.
        iCamera = cameraObject.GetComponent<ICinemachineCamera>();

        // Create a CinemachineTargetGroup for the camera.
        targetGroupObject = new GameObject("TargetGroup");

        // Add in the target group component.
        targetGroup = targetGroupObject.AddComponent<CinemachineTargetGroup>();

        // Earnest plea to have a nicer way to resize the targets array.
        Array.Resize(ref targetGroup.m_Targets, 2);

        // Add our objects into the target group.
        targetGroup.m_Targets[0].target = cube;
        targetGroup.m_Targets[1].target = sphere;
        targetGroup.m_Targets[0].radius = 2f;
        targetGroup.m_Targets[1].radius = 2f;
        targetGroup.m_Targets[0].weight = 1f;
        targetGroup.m_Targets[1].weight = 1f;

        // Setup the freelook.
        iCamera.LookAt = targetGroupObject.transform;
        iCamera.Follow = targetGroupObject.transform;

        // Now we want to change over the Composer componet for the three sub cameras.

        for (int i = 0; i < 3; i++)
        {
            CinemachineVirtualCamera vcam = freeLook.GetRig(i).GetComponent<CinemachineVirtualCamera>();
            vcam.DestroyCinemachineComponent<CinemachineComposer>();
            vcam.AddCinemachineComponent<CinemachineGroupComposer>();
        }

    }


    // Update is called once per frame
    //void Update () {
    //}

}

Thanks again for looking at it. CM is utterly wonderful.

Martin.

Hi Martin,

Thanks so much for putting together that example. I was able to reproduce the bug with it - and a bug indeed it is. I’ll work on getting a fix into the next release.

In the meantime, there is a simple workaround for you. Change your loop to this:

        // Now we want to change over the Composer componet for the three sub cameras.
        for (int i = 0; i < 3; i++)
        {
            CinemachineVirtualCamera vcam = freeLook.GetRig(i);
            UnityEngine.Object.DestroyImmediate(vcam.GetCinemachineComponent<CinemachineComposer>());
            vcam.AddCinemachineComponent<CinemachineGroupComposer>();
        }

and the errors won’t appear.

For future reference, you can export a subset of a project (scene, and related assets if any) using the Assets/Export Package menu, then select the things you want to include. It will generate a .unitypackage file, which I can then import into an empty project.

Thanks again for your effort and detective work, it’s much appreciated!
Greg

1 Like

Great. Also much appreciated!

Also thanks for the hint about the exporting

Thanks,
Martin.

Greg,

This appears to be fixed in CM 2.2.0. So thanks!

Martin.

1 Like