Search Unity

Timeline events?

Discussion in 'Timeline' started by Buzzdev, Jun 24, 2017.

  1. Buzzdev

    Buzzdev

    Joined:
    Sep 25, 2014
    Posts:
    54
    Hey,

    I'm checking out the beta for the first time and love timeline

    I'm wanting to call a function at a certain point in the animation, I was hoping timeline events were a thing similar to the animation events but it doesn't look like they are?

    Wondering what you guys are doing for custom events being called in the timeline. Or if you even use timeline for this kind of thing.

    EDIT: for e.g. , calling a new sequence after one has finished.

    Cheers!
     
    Last edited: Jun 24, 2017
  2. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    965
    Hi,

    We've tried a few iterations with events, but nothing we were happy with, and it's something we are looking into putting into a future release. For now, one option is to poll the state and/or time members of PlayableDirector from a MonoBehaviour.
     
  3. Buzzdev

    Buzzdev

    Joined:
    Sep 25, 2014
    Posts:
    54
    Cheers for the reply!

    Yeah that's fair, cool to know it's being considered too. Nothing a custom editor can't handle I guess.
    Thanks again!
     
  4. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    188
    This is actually a huge deal and big omission. Many uses of the new timeline will require the ability to trigger functions when animations are complete and it should not have come out of beta without it.
    Back to square one again.
     
  5. The-Britain

    The-Britain

    Joined:
    Mar 31, 2015
    Posts:
    714
    I'm probably attacking an ant with a hammer, but I ended up writing a custom frame-by-frame animator with event triggering. I too agree that timeline events are a must.
     
    hippocoder and Ziplock9000 like this.
  6. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    188
    Whats even worse is you can right-click on the timeline track, select "Edit in Animation Window".. Add an event, bind it to a function.. But the function never gets called.
     
  7. The-Britain

    The-Britain

    Joined:
    Mar 31, 2015
    Posts:
    714
    I also HATE to interject my whining into this thread, but, PLEASE add the ability to set and get animation frames from code. That 0-1 division nonsense is not accurate enough. *whining over*
     
    Necronomicron likes this.
  8. ajsmith22

    ajsmith22

    Joined:
    Jun 20, 2015
    Posts:
    9
    Yeah, I just tried doing Animation events in timeline myself and found out they didn't work.

    In the meantime, can someone suggest a workaround? I'm not quite sure what "poll the state and/or time members of PlayableDirector from a MonoBehaviour" means exactly.
     
  9. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    188
    It means you should set up code in Update() that on every frame checks to see if the animation is done or not.. or at the frame you wanted.
    How the hell this is supposed to work with multiple timelines running in parallel with many of them nested within other nested timelines without looking like utter scribble I don't know.
    The whole point is that all of this was abstracted and managed by Unity.. by having to write your own nested polling routines it makes it pointless.
     
    andreyefimov2010 and arachnidLeo like this.
  10. The-Britain

    The-Britain

    Joined:
    Mar 31, 2015
    Posts:
    714
    It's not always accurate either. Even in the update function the "last frame" can get called multiple times, so you need a nest of checks for each time you check to make sure you don't fire the same function multiple times. You also get this same problem when you're working with catching a frame in between. When I was working on SSP, I made a custom animation controller just to solve this issue.
     
    Ziplock9000 likes this.
  11. ajsmith22

    ajsmith22

    Joined:
    Jun 20, 2015
    Posts:
    9
    Yeah, for now I'm just going to try making a list of unassigned Animation Events and iterate through them in update comparing time to PlayableDirector time and firing a function if PlayState is playing and the time of the event is equal to the PlayableDirector time. Still, I can see a lot of issues that will probably come up with this.
     
  12. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    188
    In this demo they have an animation that is to be played when a collectable game item has been collected by the player. Naturally at the end of that animation you'd destroy the object (or intelligent caching) instead of just having 1000's of them hidden in your scene. Either way a script/trigger/event needs to be called.
    If you notice, due to the lack of events / triggers they don't do that. So the example they have is completely impractical. I just don't understand how they they have released this out of beta.
     
  13. arachnidLeo

    arachnidLeo

    Joined:
    Nov 22, 2012
    Posts:
    4
    Timeline is awesome for cutscenes, but not having events makes it obsolete for most gameplay purposes. The API for dealing with timelines is clunky at best, and when events work so smoothly in traditional animation clips it's hard to find a reason to move to timeline.
     
    AndrewKaninchen and Ziplock9000 like this.
  14. Roy-Massaad1

    Roy-Massaad1

    Joined:
    Jun 30, 2015
    Posts:
    16
    yes we need this feature

    til then im using a workaround to deal with this issue as well

    like u, i want to trigger something/stuff at one point in the timeline, one or multiple times as needed

    so what i did was create one or more animation clips that would animate through the timeline one or more checkbox exposed by a script i wrote placed on a dummy object that is monitoring the checkboxes

    the animation clip would consist of 3 frames that animate the needed checkbox, off/on/off, with looping disabled

    so for instance, the script would have a list of checkboxes like this 1-open door event, 2-play canvas animation, 3-move something

    as all these checkboxes are monitored by the script, they call their corresponding functions as needed when 'played' by the timeline

    its not as simple as the timeline having this feature built in, but try it out it works

    ps: the reason the the setup is 3 frames consisting of off/on/off with looping disabled is to avoid the checkboxes firing incorrectly early on timeline load/initialization and to avoid looping problems that confuse the monitoring script
     
  15. Buzzdev

    Buzzdev

    Joined:
    Sep 25, 2014
    Posts:
    54
    For some reason I wasn't getting emails for this thread and missed all the comments.

    Yeah it's such a pain it's not there. I used this to get around it:

    TimelineEvent.cs (multiple events, time to fire during playback, type etc.. )
    TimelineHandler.cs (list of events that monitors the time to fire, and the current duration of the playable)
    TimelineHandlerEditor.cs (an awful editor window that looks messy but lets me just create new events like a list)

    I'm a complete novice so this is probably a terrible, expensive, inaccurate, pain in the ass to use method haha, but posting in case it helps someone like myself. Was just handy for triggering different UI during cut sequences in my case.

    The handler:
    Code (csharp):
    1.  
    2.     public PlayableDirector playableDirector;
    3.     public List<TimelineEvent> eventList;
    4.  
    5.     private void Update()
    6.     {
    7.  
    8.         if (playableDirector.isActiveAndEnabled)
    9.         {
    10.  
    11.             if (eventList != null && eventList.Count > 0)
    12.                 foreach (TimelineEvent t in eventList)
    13.                     if (playableDirector.state == PlayState.Playing)
    14.                     {
    15.  
    16.                         if (!t.isActive)
    17.                             if (playableDirector.time >= t.time)
    18.                                 t.FireEvent();
    19.  
    20.                     }  else {
    21.  
    22.                         t.isActive = false;
    23.  
    24.                     }
    25.  
    26.         }  
    27.        
    28.     }
    29.  
     
    Ziplock9000 likes this.
  16. zmacbean

    zmacbean

    Joined:
    Jul 2, 2017
    Posts:
    4
    Oh the rage ... I'm there as well. Maybe it's time for a different mindset. From another perspective: Let's forget about what we would expect for a minute: A little gadget you can click to add an event, just like you used to do. Okay, now let's envision it from a fresh mindset: OMG! We have an activation track now! So I says to myself ... why not use it to generate an event that will cause another action to be taken, but wait ... how 'bout just execute the event right there? Are you with me on this?

    As a simple example, a hang this little script off of an object and drag the object onto the timeline as an Activation track, then adjust the clip to where you want the event to happen. If the timeline is a loop this will happen once per loop.
    public class animEvent : MonoBehaviour {
    void OnEnable()
    {
    Debug.Log("animEvent called at: " + Time.time); // put as much code as you want here.
    gameObject.SetActive (false);// enabled = false;
    }
    }
    Hmm, okay I can live with that. It even looks like a little 'event gadget' on the time line now.
    Hope this helps someone else get past this.
     
  17. zmacbean

    zmacbean

    Joined:
    Jul 2, 2017
    Posts:
    4
  18. zmacbean

    zmacbean

    Joined:
    Jul 2, 2017
    Posts:
    4
    note: I just figured out you want to zoom in on the timeline and make that activation clip very narrow so you it doesn't get called multiple times per pass.
     
  19. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    188
    Good effort. But this can get messy quickly with the extra objects and references you have to set up. They need to make adding events to timelines the #1 priority besides critical bugs and introduce them in the next month or two. It's become too acceptable to have to hack your way around Unity's badly design limitations for too long.
     
    StaffanEk and awesomedata like this.
  20. zmacbean

    zmacbean

    Joined:
    Jul 2, 2017
    Posts:
    4
    Thanks. It's my first post to the community. Would you agree maybe there's two schools of thought on 'messy.' I.E. Some may argue it's better to have the freedom to get messy rather than being constrained to one int, one float, one string, etc.
     
  21. HypothicEntropy

    HypothicEntropy

    Joined:
    Mar 24, 2014
    Posts:
    17
    I came up with a solution but requires some extra steps. This method will ignore Timelines Animation features (the red record button) and instead just play Animations (old school Animation window that lets you use Events). For each timeline, you need a new animator controller and animation, you then add the event call you need in the animation, have it triggered by the animator, and have Timeline play the animation file. This requires the script you are trying to call be on the game object with the animator on it. If you want to use Timeline to edit the animation, you need to have that on a separate track and make sure to mute the animation when you are not editing it, it should be played on a separate (non-editable in Timeline) animation track.

    Essentially what this is doing is ignoring Timeline and just playing the animation file, so Timelines role is to just start the animation and that is all. This causes problems with syncing because now, Timeline and the Animation are decoupled and just play, so if you want to pause or skip the timeline around, you need to do it to both the Animations Animator and the Timeline.

    Here's some code I wrote to do that, this is called as an event in the animation:
    Code (CSharp):
    1.  
    2. internal PlayableDirector playable;
    3. internal Animator animator;
    4. public void PausePlayablePlayback()
    5.     {
    6.         Debug.Log("PAUSE PLAYABLE CALLED");
    7.         playable.Pause();
    8.  
    9.         animator.speed = 0;
    10.     }
    11.  
     
  22. Andy-Touch

    Andy-Touch

    A Moon Shaped Bool Unity Technologies

    Joined:
    May 5, 2014
    Posts:
    1,009
    Well, there are different ways to currently do this as 'workarounds' of not having 'Event Triggers' on the Timeline:
    - When the Timeline is played via script, set off a 'timer' (Coroutine or Ticker, for example) that will then fire code when a certain time is reached. I actually do this in the talk (that you shared) for disabling player input at the beginning of a sequence and enabling at the end of the sequence (I get the Playable Director's duration and use that as the wait time of a Coroutine)
    - Write a Custom Track/Playable that binds to a script and trigger a method on the script at the start or end of the clip.
    - Create an Empty GameObject that has a script with 'OnEnable' and 'OnDisable' methods in it that fire off different desired code/events. Bind this object to an Activation Track and move/adjust the clip to when you want to fire those methods. Ok, this last one is a bit 'hacky' but it completely works as a short-term solution. Im using it right now for a tutorial im putting together to disable/enable player input during a sequence.

    So yeah, its a shame that there is no Event Track currently in 2017.1; but there are potential solutions to trigger methods at points in the Timeline. Its up to you which you want to use. :)
     
  23. Ziplock9000

    Ziplock9000

    Joined:
    Jan 26, 2016
    Posts:
    188
    >Write a Custom Track/Playable that binds to a script and trigger a method on the script at the start or end of the clip.
    I was thinking about this one the other night. It's quite "clean". I bet someone has already written one or will shortly.
     
  24. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    Would anyone mind sharing one here for those of us who don't yet have a full grasp on the intricacies of the new Playables system who simply want to use Timeline right now?
     
  25. adamgryu

    adamgryu

    Joined:
    Mar 1, 2014
    Posts:
    63
    Hey, I have been trying to create timeline events using a custom playable, but I've been having some difficulties. Here's what I've come up with so far:

    Code (CSharp):
    1. public class ExtendedPlayableBehaviour : PlayableBehaviour {
    2.     private bool hasRun = false;
    3.  
    4.     public override void OnBehaviourPlay(Playable playable, FrameData info) {
    5.         base.OnBehaviourPlay(playable, info);
    6.         if (!hasRun) {
    7.             hasRun = true;
    8.             OnClipBegin(playable, info);
    9.         }
    10.     }
    11.  
    12.     public override void OnGraphStart(Playable playable) {
    13.         base.OnGraphStart(playable);
    14.         double timelineTime = playable.GetGraph().GetRootPlayable(0).GetTime();
    15.         if (timelineTime <= 0) {
    16.             hasRun = false;
    17.         }
    18.     }
    19.  
    20.     /// <summary>
    21.     /// Called only when the clip begins to play.
    22.     /// </summary>
    23.     public virtual void OnClipBegin(Playable playable, FrameData info) {
    24.         // Override this.
    25.     }
    26. }
    This approach has a few problems:
    1. If the timeline time is set manually to the middle of this clip, OnBehaviourBeginPlay() will play anyways, even though we really want it to only play at the beginning of the clip.
    2. If the timeline loops or is reset while playing, the events will not be played again. The timeline has to end normally (this causes the time variable to reset to 0) and then have Play() called again to trigger OnGraphStart().
    I feel like the right solution to this problem is to poll from ProcessFrame() to check if the current frame is inside the clip and the previous frame was before the clip. However, when I tried implementing this, I found that the timeline API didn't expose anything I thought I could do this with. I feel like there must be a way, but playable.GetTime() only updates once the clip is already playing and I can't find a method that is like playable.GetStartTimeOnTimeline()

    Can a Unity developer shed some light on how to do this with the API?
     
    Last edited: Aug 15, 2017
  26. tezza2k1

    tezza2k1

    Joined:
    Feb 16, 2017
    Posts:
    13
    Here is what I do. There are probably many ways to skin a cat. I use the Activation Track to capture OnEnable() and OnDisable()

    I create a gameobject and attach TimelineEnableTriggerAction ( below ) to it ( red arrows ).

    The listener class then implements the inner interface EnableListener

    This method can pass some arbitrary text (blue arrow ) and an arbitrary gameobject ( second blue arrow ) for the listener class implementing the EnableListener interface ( purple arrow ).

    The Gameobject which translates Enable events:

    timeline-triggers-15-08-2017 21-05-30.png

    How I reference the above gameobject => script in my timeline:

    timeline-triggers-timeline-15-08-2017 21-15-25.png

    Code (csharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class TimelineEnableTriggerAction : MonoBehaviour {
    7.  
    8.     public interface EnableListener {
    9.  
    10.         void OnEnableChanged( TimelineEnableTriggerAction source, string payloadText, GameObject payloadGO, bool enabled );
    11.     }
    12.  
    13.     public GameObject listener     = null  ;
    14.  
    15.     public string     payloadText  = null  ;
    16.     public GameObject payloadGO    = null  ;
    17.  
    18.  
    19.  
    20.     private void OnEnable()  {
    21.         DoNotifyListener( true  );
    22.     }
    23.  
    24.     private void OnDisable() {
    25.         DoNotifyListener( false );
    26.     }
    27.  
    28.  
    29.  
    30.     private void DoNotifyListener ( bool enabled ) {
    31.  
    32.         if( listener != null ){
    33.  
    34.             foreach( var component in listener.GetComponents<Component>() ){
    35.  
    36.                 if( component is EnableListener ){
    37.  
    38.                     EnableListener _listener = component as EnableListener ;
    39.  
    40.                     _listener.OnEnableChanged( this, payloadText, payloadGO, enabled );
    41.                 }
    42.             }
    43.        
    44.         }
    45.     }
    46.  
    47. }
     
    Last edited: Aug 15, 2017
    hobostew likes this.
  27. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    Have you tried ScriptPlayable<T0> instead?

    It seems you can get the duration of the clip using PlayableExtensions.GetDuration to get the duration of the current custom playable.

    No idea if there's something special about custom playables vs. regular timeline ones, but it's possible it might work.

    If it does, do you mind sharing the code for it with others here in case nobody else posts another method for this?


    @Andy-Touch

    Would you mind please giving us an example of how this should be setup with the current API?
     
  28. WikkidEdd1

    WikkidEdd1

    Joined:
    Nov 17, 2015
    Posts:
    10
    I've been noodling around with the Timeline system for the last couple of days and like you guys found the lack of support for one shot events a big blocker. So getting a solution for that has been priority number one for us.

    Below is version 1 which allows event triggering from customs tracks (Sorry for the wall of code). I've tried to cut down on the amount of boilerplate code you have to write for each event type.

    EventTrack.cs
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Playables;
    4. using UnityEngine.Timeline;
    5.  
    6. [TrackColor(0.855f, 0.8623f, 0.87f)]
    7. [TrackClipType(typeof(EventTrackClip))]
    8. public class EventTrack : TrackAsset
    9. {
    10.     public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    11.     {
    12.         ScriptPlayable < EventTrackMixerBehaviour> result = ScriptPlayable<EventTrackMixerBehaviour>.Create(graph, inputCount);
    13.         result.GetBehaviour()._clips = GetClips() as TimelineClip[];
    14.         return result;
    15.     }
    16. }
    17.  
    EventTrackMixerBehaviour.cs
    Code (CSharp):
    1. using UnityEngine.Playables;
    2. using UnityEngine.Timeline;
    3.  
    4. public class EventTrackMixerBehaviour : PlayableBehaviour
    5. {
    6.     public TimelineClip[] _clips;
    7.  
    8.     // NOTE: This function is called at runtime and edit time.  Keep that in mind when setting the values of properties.
    9.     public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    10.     {
    11.         int inputCount = playable.GetInputCount ();
    12.         double time = playable.GetGraph().GetRootPlayable(0).GetTime();
    13.         for (int i = 0; i < inputCount; i++)
    14.         {
    15.             ScriptPlayable<EventTrackBehaviour> inputPlayable = (ScriptPlayable<EventTrackBehaviour>)playable.GetInput(i);
    16.             EventTrackBehaviour input = inputPlayable.GetBehaviour ();
    17.          
    18.             double start = _clips[i].start;
    19.             input.Trigger(time, _clips[i].start);
    20.         }
    21.     }
    22. }
    EventTrackClip.cs
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Playables;
    4. using UnityEngine.Timeline;
    5.  
    6. [Serializable]
    7. public class EventTrackClip : PlayableAsset, ITimelineClipAsset
    8. {
    9.     public bool _ExecuteInEditMode = false;
    10.     public ClipCaps clipCaps
    11.     {
    12.         get { return ClipCaps.None; }
    13.     }
    14.  
    15.     public override Playable CreatePlayable (PlayableGraph graph, GameObject owner)
    16.     {
    17.         var playable = ScriptPlayable<EventTrackBehaviour>.Create (graph);
    18.         EventTrackBehaviour clone = playable.GetBehaviour ();
    19.         clone._EventClip = this;
    20.         clone._Resolver = graph.GetResolver();
    21.         return playable;
    22.     }
    23.  
    24.     public virtual void Trigger(IExposedPropertyTable resolver)
    25.     {
    26.         Debug.Log("Event Triggerered");
    27.     }
    28. }
    29.  
    EventTrackBehaviour.cs
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Playables;
    4.  
    5. [Serializable]
    6. public class EventTrackBehaviour : PlayableBehaviour
    7. {
    8.     public EventTrackClip _EventClip;
    9.     public IExposedPropertyTable _Resolver;
    10.     double _lastTime = -1;
    11.    
    12.     public void Trigger(double time, double start)
    13.     {
    14.         if (_EventClip._ExecuteInEditMode || Application.isPlaying)
    15.         {
    16.             if (_lastTime < start && time >= start ||
    17.                 _lastTime > time && time == start)
    18.             {
    19.                 _EventClip.Trigger(_Resolver);
    20.             }
    21.             _lastTime = time;
    22.         }
    23.     }
    24. }
    25.  
    So once you have all that in your project you can easily create new event type by creating two new class. One that inherits from EventTrack specifying the clip type (and colour if you want) and one which inherits from EventTrackClip adding any customs variables for the event and overriding the Trigger function which will get called at the start of the clip.

    Code (CSharp):
    1. using UnityEngine.Timeline;
    2.  
    3. [TrackColor(0.855f, 0.8623f, 0.87f)]
    4. [TrackClipType(typeof(MyCustomEvent))]
    5. public class MyCustomEventTrack : EventTrack
    6. {
    7. }
    8.  
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class MyCustomEvent : EventTrackClip
    4. {
    5.     public ExposedReference<Transform> _Transform;
    6.     public float _AmountToMoveUp = 1f;
    7.  
    8.     public override void Trigger(IExposedPropertyTable resolver)
    9.     {
    10.         // You code here
    11.         Transform transform = _Transform.Resolve(resolver);
    12.         Vector3 position = transform.position;
    13.         position.y += _AmountToMoveUp;
    14.         transform.position = position;
    15.     }
    16. }
    As mentioned above this is V1 it manages to strip out 2 of the 4 files you are expected to write for customs playables. I've got a V2 locally which creates an event track which can contain different clip types, this makes not only the timeline itself more manageable, but you also only need to 1 class/file for each new event type (See top track in image below). It requires modifying the timeline dll in the unity install folder so will break with new versions of unity so don't want to share it on here

     

    Attached Files:

  29. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    965
    @WikkidEdd1 That's a really good implementation.

    to reply to @adamgryu
    1. That's a bit tricky. I'm posting another example here that uses the Evaluation type of the frame info, and that makes it happen only when the start point is crossed. Only works in playmode though.
    2. OnGraphStart() isn't re-called on loops. Using OnBehaviourPlay() (in the example attached) works except if clip takes up the whole timeline.

    In the example we use UnityEvents as the callback mechanism, which is a bit tricky since they can't be serialized with an exposedreference, so we attach a component to the player to serialize the events.
     

    Attached Files:

    Ian094, Hs_Jack, ledbetterman and 3 others like this.
  30. WikkidEdd1

    WikkidEdd1

    Joined:
    Nov 17, 2015
    Posts:
    10
    @seant_unity Thanks, I'm just trying to get it as steamlined as possible. We've been using an in house timeline-esque tool for 6+ years and have had games that use 1000+ timelines so I've already got in my head how I like to work with them and I'm trying to get unity's timelines to match that. Events play a big part in that.

    Is that package meant for a particular version of Unity? I'm getting a compile error in EventClipInspector as it's failing to find TimelineEditor.playableDirector which doesn't appear to be available in 2017.1
     
  31. adamgryu

    adamgryu

    Joined:
    Mar 1, 2014
    Posts:
    63
    @seant_unity Thanks for sharing this example project. One issue with using OnBehaviourPlay() to trigger events, is that if the timeline is paused while the clip is active, the event will be called again when the timeline resumes. This was a problem for me, since one of my events pauses the timeline to wait for user input.

    @WikkidEdd1 That is very clever! The API I needed for a polling solution was was _clip.start and GetClips(). Thanks a lot for sharing :)
     
    awesomedata likes this.
  32. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    965
    Ah right, it works in the 2017.2 beta.
     
  33. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    502
    Here's my current solution using SendMessage on target GameObjects (similar to the regular animation component).

    Code (CSharp):
    1. [Serializable]
    2. public class PlayableMethodCallBehaviour : PlayableBehaviour
    3. {
    4.     public GameObject target;
    5.     public string methodName;
    6.  
    7.     public override void OnBehaviourPlay(Playable playable, FrameData info)
    8.     {
    9.         if (target && Application.isPlaying)
    10.             target.SendMessage(methodName, SendMessageOptions.RequireReceiver);
    11.     }
    12. }
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.Playables;
    4. using UnityEngine.Timeline;
    5.  
    6. [Serializable]
    7. public class PlayableMethodCallClip : PlayableAsset, ITimelineClipAsset
    8. {
    9.     public PlayableMethodCallBehaviour template = new PlayableMethodCallBehaviour ();
    10.     public ExposedReference<GameObject> target;
    11.  
    12.     public ClipCaps clipCaps
    13.     {
    14.         get { return ClipCaps.None; }
    15.     }
    16.  
    17.     public override Playable CreatePlayable (PlayableGraph graph, GameObject owner)
    18.     {
    19.         var playable = ScriptPlayable<PlayableMethodCallBehaviour>.Create (graph, template);
    20.         PlayableMethodCallBehaviour clone = playable.GetBehaviour ();
    21.         clone.target = target.Resolve (graph.GetResolver ());
    22.         return playable;
    23.     }
    24. }
    Code (CSharp):
    1. using UnityEngine.Playables;
    2.  
    3. public class PlayableMethodCallMixerBehaviour : PlayableBehaviour
    4. {
    5.     // Empty and could be omitted
    6. }
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Playables;
    3. using UnityEngine.Timeline;
    4.  
    5. [TrackColor(0.855f, 0.8623f, 0.87f)]
    6. [TrackClipType(typeof(PlayableMethodCallClip))]
    7. public class PlayableMethodCallTrack : TrackAsset
    8. {
    9.     public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    10.     {
    11.         return ScriptPlayable<PlayableMethodCallMixerBehaviour>.Create (graph, inputCount);
    12.     }
    13. }
    This is of course no fully-fledged solution, but it's a quick way to just trigger a function on some GameObject. I'm wondering, I can probably remove the unused mixer track. SendMessage could be replaced with reflection, in case it was critical to cache the call as a delegate, like UnityEvents do.

    It would be nice to create a custom drawer instead of the string field for the method name, as UnityEvents do, but I had problems retrieving the target GameObject from within the PropertyDrawer. Is is possible to get a hold of any exposed references from within the PlayableBehaviour PropertyDrawer?

    A second, more type-safe solution uses a reference to a custom component I call PlayableEventTrigger, which is supposed to work similar to the UI EventTrigger component. I just call the OnTrigger method from the timeline and forward the call to UnityEvents which are exposed in the inspector of the scene component.
     
    Last edited: Aug 17, 2017
  34. WikkidEdd1

    WikkidEdd1

    Joined:
    Nov 17, 2015
    Posts:
    10
    As per @seant_unity's solution you don't need implement a mixer nor the CreateTrackMixer override as there is default implementation in the base class.


    You can add the following into the PlayableBehaviour PropertyDrawer

    Code (CSharp):
    1.  
    2. PlayableMethodCallClip clip = property.serializedObject.targetObject as PlayableMethodCallClip;
    3. GameObject gameObject = clip.target.Resolve(property.serializedObject.context as IExposedPropertyTable);
     
    mgeorgedeveloper and Xarbrough like this.
  35. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    @seant_unity

    I've been trying to sort out a way to use Timeline to execute an arbitrary script at any point in the timeline that must pause the execution of the script-calling timeline itself (preferably being able to specify/control whether execution takes place before or after it advances a frame.)

    Is this possible to do with any of the above-mentioned implementations?

    Also, are there any plans to officially support script-execution-style events for Timeline either pre- or post-release of 2017.2?
     
  36. hwaet

    hwaet

    Joined:
    Mar 21, 2015
    Posts:
    23
    Thanks for sharing this! Thanks to everyone, really. There's a bunch of cool solutions here.
     
    awesomedata likes this.
  37. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    965
    If I understand right, you want control of where the script takes place in the player loop - for example, before or after MonoBehaviour Update() (currently all scripts run after).

    The only mechanism for that is to not call Play() on the playable director, but manually set the time and call Execute() from a monobehaviour. The drawback is you will lose the ability to play audio (the same as scrubbing in the editor window). Better control of where Scripted Tracks play inside the player loop is something we'd like to do in the future.

    We have plans for events, but it is definitively post-2017.2.
     
    awesomedata likes this.
  38. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    @seant_unity

    Being able to control when scripts execute is vital for my projects, but mainly I'm wanting to have frame-level control over their execution (sort of like a state transition -- i.e. control whether I go to the next state before the current frame, or whether I should wait until this frame is done and go there then, possibly stacking multiple scripts in a single frame if desired.)

    Does this make sense?

    Is it possible for now to at least play, then pause until a script is done executing on some other gameobject (script in question is triggered by the previously-playing but now paused Timeline). When finished executing, that script was passed a reference or something to the Timeline that lets it unpause the previously paused Timeline when it finishes.

    How (or can) one accomplish this right now?
     
    Last edited: Aug 22, 2017
  39. seant_unity

    seant_unity

    Unity Technologies

    Joined:
    Aug 25, 2015
    Posts:
    965
    There are few ways you can do that. You can pause the timeline using PlayableDirector.Pause() (and resume it with .Play()).

    One approach would be to have a monobehaviour that controls the pause/play of the playable director, and bind that to a custom track that it signals when it's time to pause.
     
  40. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    Sorry, edited my previous post for clarity just as you replied @seant_unity ...

    The way I'm looking at it, I need something like a universal Timeline setup where I can execute many different gameobjects' unique Timelines from a single Timeline that can be played/paused/resumed at various points, specified by points on the specific gameobjects' Timeline itself.

    What would be necessary to do this?
     
  41. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    5,824
    I'm having an "extra snark day" so feel free to ignore me, but it seems in many aspects we've gone from working around the complete lack of a feature, to working around the incomplete implementation of a feature. And I find the second one more infuriating than the first.
     
  42. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    @AcidArrow

    I understand your frustration there man. -- Timeline events were definitely an expected feature for me since the Animator already included them, so the logical conclusion was that Unity saw the need for them once before in the Animator window, so why wouldn't they put them into a more robust version of that Animator window by default?

    Like you likely do, I really get tired of writing code or having to rely on assets for 'workarounds' on stuff like this.

    This is one of those "it's not complete but it's really cool, so let's release it to build hype for Unity" types of features. I feel mecanim was one of those features too (I mean no generic retargeting of skeletons?)

    The problem with big stylish (--but incomplete!) features like these (aside from them just being a tease) is that they often never get "finished" without lots of users being forced into begging for (or buying from the Asset Store) the rest of the very basic (missing!) features they need to really use the system for what they needed it for in the first place. In the meantime, feature creep sets in until (I guess?) the devs get bored and decide to revamp the whole thing again or completely abandon it for "more important" Unity features, since the Asset Store devs can handle any missing features now. Great for Asset Store profits, but very bad for your customers -- and even worse for business.

    Don't get me wrong, I'll work around this issue as much as I can, but just like word of mouth can sell your product -- it can also destroy it over time too!

    I hope Unity wakes up to the fact that making games is frustrating enough, and when the tool you use to do it frustrates you even more (by requiring you to jump through hoops to do what you expect it to be able to do already), more people get to hear about those frustrations more than the toolmaker (Unity, in this case) would probably like them to.

    I love Unity, but this is how tools like Unreal gets such a huge following. They aren't better tools than Unity at all -- they just don't put more on the user's shoulders than they have to in order to accomplish what the user expects to be able to accomplish. A very simple rule, but one Unity has apparently not yet learned.
     
    Last edited: Aug 28, 2017
    StaffanEk, Chrisad and Ziplock9000 like this.
  43. Chrisad

    Chrisad

    Joined:
    Mar 12, 2013
    Posts:
    40
    Is there any chance to have timeline as a github/bitbicket project?
     
  44. mgeorgedeveloper

    mgeorgedeveloper

    Joined:
    Jul 10, 2012
    Posts:
    51
    Soooo close to being usable... but the lack of events make it inappropriate for anything but the most basic cut-scene.

    I'm hoping to replace our current hacked together cut-scenes, but unfortunately the same problems will prevail without simple event triggers :(

    Please Unity team, at the very least, when you click through to the underlying Animation timeline, make those events fire - I can add them right there! They just don't fire. You're so close to having this fixed!

    I will buy you a coffee if you fix it! Should take about 10 minutes.

     
  45. thierry_unity

    thierry_unity

    Unity Technologies

    Joined:
    Jun 10, 2015
    Posts:
    178
    @mgeorgedeveloper ,
    this is actually a bug, we are working on a fix for that specific issue.
     
    krougeau and mgeorgedeveloper like this.
  46. JakubSmaga

    JakubSmaga

    Joined:
    Aug 5, 2015
    Posts:
    416
    Watched video from GDC 2017 and noticed that in the old version of the Timeline there were events, why were they removed?

     
    Lars-Steenhoff and Ziplock9000 like this.
  47. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    Very good question....

    My guess is that it has to do with the way AnimationEvents appear to be just rolled into Timeline directly from the Animator window -- i.e. it appears you could record position and rotation inline right from the Timeline window, so it makes sense you could set Animation events there too (see "SprayCleanerVFX" right there in the screenshot you posted!)

    Timeline Events on the other hand...

    If I had to guess, they didn't want to copy-paste the entire Animator window into Timeline and decided to take a more data-oriented method instead (to allow for blending multiple .anim clips)

    It does look like you can even set curves right there in Timeline though!

    Very cool feature, but perhaps this indicates the reason they removed it -- it also seems to be inherently incompatible with just grabbing a ".anim" file and inserting that to let it blend with another entirely different (arbitrary!) ".anim" or ".cs" file, considering the two would not be able to be treated as isolated animations internally since it'd look internally like they were just in the same animation window.

    This is all theory, and I may well be very wrong! -- but please, anyone from Unity clear it up for me if you would!
     
  48. julienb

    julienb

    Unity Technologies

    Joined:
    Sep 9, 2016
    Posts:
    127
    Before Timeline released, we had an internal version with Timeline Events. It was mostly working, but we had problems with UI and timing (in some cases events were triggered when they should not have been). We cut that feature right before release because it wasn't at a quality level we judged appropriate. We really wanted that feature though, so expect to see it return in the future (although I can't provide a time frame).

    You can set the curves in Timeline in the released version. You can do that on a clip recorded in Timeline.
    ffaf677938d95eb508eccb8c8e52a066.png
     
    Last edited: Sep 12, 2017
  49. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    813
    Wow, you guys were father along than i gave you credit for! -- Nice!

    @julienb

    One question -- was it a UI design issue, or was it something code-wise internally, like a bug, with the UI system?

    I can see a bug or missing/hackey underpinning code being pretty nasty and time consuming, but design issues can be a nightmare and take far longer to solve than one wants to admit -- especially -- on features you want to expand on in the future.

    I'm pretty skilled at practical ui workflow designs (see Snapcam in the WIP asset forum for an example -- it's not flashy, but it's a simple and unique tool that is a breeze to use and gets the job done), so I thought I'd offer a critical eye on your current ui design (if you wanted it!) just in case you guys are having ui/workflow/design concerns. That's totally my forte, and I'd love to help make Unity better for everyone. Timeline is such a great tool and if I can help people get an awesome feature more quickly, I'm totally up for doing whatever it takes. :)
     
    Last edited: Sep 13, 2017
  50. julienb

    julienb

    Unity Technologies

    Joined:
    Sep 9, 2016
    Posts:
    127
    No, it wasn't a bug in the UI system. Editing events through the UI itself proved harder than we thought; we wanted to use UnityEvent but we could not make it work properly. We tried a couple other solutions but none were satisfactory.

    Thank you for offering your help; once we have a proper UI workflow for events, I'll post here so we can validate our design with the community.
     
    awesomedata likes this.