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

Get all behaviours from a timeline

Discussion in 'Timeline' started by Ether141, Apr 30, 2022.

  1. Ether141

    Ether141

    Joined:
    Jul 30, 2018
    Posts:
    21
    Hello guys. I'm currently working on a dialogue system in my RPG game and I would like to add possibility for player, to skip scenes during cutscenes. A player should be able to skip currently playing dialogue scene, by press a key on a keyboard. As long as I don't have any problems with this part, I don't know how to deal with one issue that comes next. In my timeline I have some custom tracks that perform some operations, like setting position/rotation of objects, changing behaviours of characters' look at system, adding item to the player's inventory etc. When I just skip a whole timeline to the end, all of those tracks are omiited and those required operations are not performed and for example player didn't get an item. Due to this, when player skips part of cutscene, unexpected things start to happen and everything looks like it shouldn't. I came up with an idea, to get all behaviours from timeline, when the player wants to skip cutscene, and manually call all required methods, but I have no idea how can I get all my custom behaviours from timeline in script.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Playables;
    3.  
    4. [System.Serializable]
    5. public class SetLookAtBehaviour : PlayableBehaviour
    6. {
    7.     [HideInInspector] public Transform targetToLookAt;
    8.     [SerializeField] private Vector3 offset;
    9.     [SerializeField] private LookAtController.LookAtType lookAtType;
    10.     [SerializeField] private bool resetLookAt;
    11.  
    12.     public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    13.     {
    14.         if (Application.isPlaying)
    15.         {
    16.             LookAtController lookAtController = (LookAtController)playerData;
    17.  
    18.             if (resetLookAt)
    19.             {
    20.                 lookAtController.RemoveLookAtTarget();
    21.                 lookAtController.EnableRandomLook();
    22.             }
    23.             else
    24.             {
    25.                 lookAtController.SetLookAtTarget(targetToLookAt.position + offset, lookAtType);
    26.             }
    27.         }
    28.     }
    29.  
    30. }
    31.  
    This is my example, custom behaviour. I want to get access to it and then manually call a ProcessFrame method. Or maybe there is a better way to deal with my problem.

    Thanks for help!
     
  2. JeffG

    JeffG

    Joined:
    Oct 21, 2013
    Posts:
    84
    I use a class that plays at the end of a timeline. I place all required function calls there

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Playables;
    3. using UnityEngine.Events;
    4. using Cinemachine;
    5.  
    6. public class PlayableDirectorEventManager : MonoBehaviour
    7. {
    8.     private PlayableDirector director;
    9.     public GameObject[] objects;
    10.     public UnityEvent OnEnd;
    11.  
    12.     public void Awake()
    13.     {
    14.         director = GetComponent<PlayableDirector>();
    15.     }
    16.     void OnEnable()
    17.     {
    18.         director.stopped += OnPlayableDirectorStopped;
    19.     }
    20.  
    21.     void OnPlayableDirectorStopped(PlayableDirector aDirector)
    22.     {
    23.         Logging.Log(string.Format("CutScene {0} is stopped.", this.gameObject.name));
    24.         DisableAllObjectsInCaseOfSkip();
    25.  
    26.         if (OnEnd.GetPersistentEventCount() > 0)
    27.             OnEnd.Invoke();
    28.     }
    29.  
    30.     public void DisableAllObjectsInCaseOfSkip()
    31.     {
    32.  
    33.         for (int i = 0; i < objects.Length; i++)
    34.             objects[i].gameObject.SetActive(false);
    35.     }
    36.  
    37.     void OnDisable()
    38.     {
    39.         director.stopped -= OnPlayableDirectorStopped;
    40.     }
    41.  
    42. }
    43.  
     
  3. Ether141

    Ether141

    Joined:
    Jul 30, 2018
    Posts:
    21
    That's not exactly what I was looking for. Anyway, I figured out how to deal with my problem, but thanks.
     
  4. Jzubah

    Jzubah

    Joined:
    Jan 26, 2018
    Posts:
    4
    How? I have the same issue atm. Did you manage to get a reference to all the playables?
     
  5. Ether141

    Ether141

    Joined:
    Jul 30, 2018
    Posts:
    21
    This is my utility class that I use:

    Code (CSharp):
    1. using System;
    2. using System.Linq;
    3. using UnityEngine.Playables;
    4. using System.Collections.Generic;
    5.  
    6. using Obj = UnityEngine.Object;
    7. using UnityEngine;
    8.  
    9. public static class PlayableUtilities
    10. {
    11.     public static void GetLastBehaviourAndBinding<T>(PlayableDirector director, Dictionary<Obj, T> behavioursAndBindings) where T : class, IPlayableBehaviour, new()
    12.     {
    13.         Dictionary<Obj, List<T>> behavioursAndBindingsTmp = new Dictionary<Obj, List<T>>();
    14.         GetBehavioursAndBindings(director, behavioursAndBindingsTmp);
    15.        
    16.         foreach (var behavioursAndBinding in behavioursAndBindingsTmp)
    17.             behavioursAndBindings.Add(behavioursAndBinding.Key, behavioursAndBinding.Value.LastOrDefault());
    18.     }
    19.  
    20.     public static void GetBehavioursAndBindings<T>(PlayableDirector director, Dictionary<Obj, List<T>> behavioursAndBindings) where T : class, IPlayableBehaviour, new()
    21.     {
    22.         PlayableGraph playableGraph = director.playableGraph;
    23.  
    24.         if (!playableGraph.IsValid())
    25.         {
    26.             return;
    27.         }
    28.  
    29.         int numOutputs = playableGraph.GetOutputCount();
    30.         for (int i = 0; i < numOutputs; i++)
    31.         {
    32.             PlayableOutput output = playableGraph.GetOutput(i);
    33.  
    34.             if (!output.IsOutputValid() || !output.IsPlayableOutputOfType<ScriptPlayableOutput>())
    35.             {
    36.                 continue;
    37.             }
    38.  
    39.             int sourceOutputPort = output.GetSourceOutputPort();
    40.             Playable playable = output.GetSourcePlayable().GetInput(sourceOutputPort);
    41.  
    42.             List<T> behaviours = new List<T>();
    43.             GetBehaviours(playable, behaviours);
    44.  
    45.             Obj referenceObject = output.GetReferenceObject();
    46.             Obj binding = null;
    47.  
    48.             if (referenceObject != null)
    49.                 binding = director.GetGenericBinding(referenceObject);
    50.  
    51.             if (behaviours.Count > 0)
    52.                 behavioursAndBindings.Add(binding, behaviours);
    53.         }
    54.     }
    55.  
    56.     public static void GetBehaviours<T>(PlayableGraph playableGraph, List<T> behaviours) where T : class, IPlayableBehaviour, new()
    57.     {
    58.         if (!playableGraph.IsValid())
    59.         {
    60.             return;
    61.         }
    62.  
    63.         int numOutputs = playableGraph.GetOutputCount();
    64.         for (int i = 0; i < numOutputs; i++)
    65.         {
    66.             PlayableOutput output = playableGraph.GetOutput(i);
    67.            
    68.             if (!output.IsOutputValid() || !output.IsPlayableOutputOfType<ScriptPlayableOutput>())
    69.             {
    70.                 continue;
    71.             }
    72.  
    73.             int sourceOutputPort = output.GetSourceOutputPort();
    74.             Playable playable = output.GetSourcePlayable().GetInput(sourceOutputPort);
    75.            
    76.             GetBehaviours(playable, behaviours);
    77.         }
    78.     }
    79.  
    80.     public static void GetBehaviours<T>(Playable playable, List<T> behaviours) where T : class, IPlayableBehaviour, new()
    81.     {
    82.         if (!playable.IsValid())
    83.         {
    84.             return;
    85.         }
    86.  
    87.         int inputCount = playable.GetInputCount();
    88.         for (int i = 0; i < inputCount; i++)
    89.         {
    90.             Type behaviourType = typeof(T);
    91.             Playable input = playable.GetInput(i);
    92.  
    93.             if (input.GetInputCount() > 0)
    94.             {
    95.                 GetBehaviours(input, behaviours);
    96.             }
    97.             else if (input.GetPlayableType() == behaviourType)
    98.             {
    99.                 T behaviour = ((ScriptPlayable<T>)input).GetBehaviour();
    100.  
    101.                 if (!behaviours.Contains(behaviour))
    102.                 {
    103.                     behaviours.Add(behaviour);
    104.                 }
    105.             }
    106.         }
    107.     }
    108. }
     
    fxlange likes this.
  6. saifshk17

    saifshk17

    Joined:
    Dec 4, 2016
    Posts:
    488
    And how do you skip through the dialogues? Possible to get the start frame of next activation/animation track and play timeline from there?