Search Unity

Swapping PlayableDirector Cinemachine Brain At Runtime

Discussion in 'Cinemachine' started by digitalThorns, Feb 25, 2019.

  1. digitalThorns

    digitalThorns

    Joined:
    Apr 7, 2014
    Posts:
    8
    I need to swap the cinemachine brain on a timeline at runtime, but haven't been able to quite figure out how. I found this code, but it depends on Editor classes, so it doesn't work on device. Any help would be very appreciated.


    Code (CSharp):
    1. public static class PlayableDirectorExtensions
    2.   {
    3.     public static void SetCinemachineBrain(this PlayableDirector director, CinemachineBrain brain)
    4.     {
    5.       var obj = new SerializedObject(director);
    6.       var bindings = obj.FindProperty("m_SceneBindings");
    7.       SerializedProperty binding;
    8.       SerializedProperty sceneObj;
    9.       for(int i = 0; i < bindings.arraySize; i++)
    10.       {
    11.         binding = bindings.GetArrayElementAtIndex(i);
    12.         sceneObj = binding.FindPropertyRelative("value");
    13.         if(sceneObj.objectReferenceValue is CinemachineBrain)
    14.         {
    15.           var trackProp = binding.FindPropertyRelative("key");
    16.           director.ClearGenericBinding(trackProp.objectReferenceValue);
    17.           director.SetGenericBinding(trackProp.objectReferenceValue, brain);
    18.         }
    19.       }
    20.     }
    21.   }
     
  2. julienb

    julienb

    Unity Technologies

    Joined:
    Sep 9, 2016
    Posts:
    177
    PlayableDirector has a method called GetGenericBinding; you give it a timeline track and it will return you the object bound to it. Something like this will work, provided that the asset bound to the PlayableDirector is indeed a TimelineAsset:

    Code (CSharp):
    1. public static void SetCinemachineBrain(this PlayableDirector director, CinemachineBrain brain)
    2. {
    3.     var timeline = director.playableAsset as TimelineAsset;
    4.     if (timeline == null) return;
    5.  
    6.     //iterate on all tracks that have a binding
    7.     foreach(var track in timeline.GetOutputTracks())
    8.     {
    9.         //get the object bound to the track, and change it if it's a CinemachineBrain
    10.         var boundBrain = director.GetGenericBinding(track) as CinemachineBrain;
    11.         if (boundBrain != null)
    12.             director.SetGenericBinding(track, brain);
    13.     }
    14. }
     
  3. talhakaya

    talhakaya

    Joined:
    Jun 7, 2013
    Posts:
    6

    Thank you so much, this was just what I needed.
     
  4. JaredThomas

    JaredThomas

    Joined:
    Nov 6, 2020
    Posts:
    1
    I have now seen two different Unity employees use this method as the proper way to set the bindings.

    I must be missing something obvious, but I am getting an error when trying to cast playable asset as a timeline asset. It says i can't convert a Playable asset to a Timeline asset. I have been trying for hours to set the camera to a cinemachine track at runtime, but there is very little online resources for this. Any help is much appreciated!
     
  5. Gregoryl

    Gregoryl

    Unity Technologies

    Joined:
    Dec 22, 2016
    Posts:
    7,711
    You should copy/paste the error message so we can read it. Also show your code. Otherwise your question is just too vague. What happens when you use the exact code posted here? https://forum.unity.com/threads/swa...machine-brain-at-runtime.635200/#post-4266934
     
  6. Lasovar

    Lasovar

    Joined:
    Dec 21, 2020
    Posts:
    10
    For people still having this issue or want to know a better implementation...

    There is a flaw with the script that Gregoryl provided... which is what if the Cinemachine brain was already null? it happened for me because I have a multiple scene setup which means I can't reference the cinemachine brain to begin with so it's always null.

    This is how I solved it and it seems to be working well as far as I tested:

    Code (CSharp):
    1. public static void SetCinemachineBrain(this PlayableDirector director, CinemachineBrain brain)
    2. {
    3.     var timeline = director.playableAsset as TimelineAsset;
    4.     if (timeline == null) return;
    5.  
    6.     //iterate on all tracks that have a binding
    7.     foreach(var track in timeline.GetOutputTracks())
    8.     {
    9.         if (track is CinemachineTrack)
    10.             director.SetGenericBinding(track, brain);
    11.     }
    12. }
    It will just check if the track is a cinemachine track, if so it will just set the brain.
     
    axg_wf likes this.