Hi, I need to do some sort of Ability System for my game. I think Timeline could be a really cool thing to use for that. In the big lines I need for instance: - Play animation - Play Fx @ time x - Move Fx to Target - Play Die animation on Target Works fine in editor, but of course, you can fire an ability on any target in the game. I was wondering if it's possible to instanciate a Director with the right Timeline asset, and being able to modify a reference. In my case, I would need to change the Target, to reflect the one you are targeting. I saw that the Director has SetGenericBinding and SetReferenceValue. First, I'm not quite sure the difference between the two. And Second, it asks for a key, and don't know where to reference that... Thanks
you definitely can do this - the demo I'm working on for the Vision Summit next week uses this quite extensively. Here is a sample script that does dynamic timeline binding of tracks: Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; namespace PixelWizards.Shared.Utiltiies { public class DynamicTimelineBinding : MonoBehaviour { public List<GameObject> trackList = new List<GameObject>(); public PlayableDirector timeline; public TimelineAsset timelineAsset; public bool autoBindTracks = true; // Use this for initialization private void Start() { if (autoBindTracks) BindTimelineTracks(); } public void BindTimelineTracks() { Debug.Log("Binding Timeline Tracks!"); timelineAsset = (TimelineAsset)timeline.playableAsset; // iterate through tracks and map the objects appropriately for( var i = 0; i < trackList.Count; i ++) { if( trackList[i] != null) { var track = (TrackAsset)timelineAsset.outputs[i].sourceObject; timeline.SetGenericBinding(track, trackList[i]); } } } } } What this looks like is this: Basically the 'track list' on the dynamic binding script mirrors the tracks in the timeline sequence. You can modify this base script to do specific lookups in the scene to bind to specific tracks etc. Currently the tracks are just an array you can access, so if you change the order of tracks, it breaks the binding, which isn't ideal, but otherwise works well. Something we are considering / investigating going forward is having tags for tracks (similar to the general unity tags) so you can mark a track as 'character 1' or something and then look it up while binding - in a similar manner to the GameObject.FindByTag() methods that are used throughout. Hope this helps!
Hey, Thanks for the answer. At the end I figure it out, but I'm using the tracks list names. I also needed to modify Reference in some PlayableTrack , not very easy... Code (CSharp): public class Test01 : MonoBehaviour { public GameObject Player; public TimelineAsset TimlineAsset; public string SourceTrackName; public string TargetTrackName; public string MoveParticleTrackName; private RaycastHit hit; void Update() { if (Input.GetMouseButtonDown(0)) { if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)) { var director = new GameObject("Ability").AddComponent<PlayableDirector>(); director.playableAsset = TimlineAsset; var target = hit.collider.gameObject; foreach (var playableAssetOutput in director.playableAsset.outputs) { if (playableAssetOutput.streamName == SourceTrackName) { director.SetGenericBinding(playableAssetOutput.sourceObject, Player); } else if (playableAssetOutput.streamName == TargetTrackName) { director.SetGenericBinding(playableAssetOutput.sourceObject, target); } else if (playableAssetOutput.streamName == MoveParticleTrackName) { var tr = playableAssetOutput.sourceObject as PlayableTrack; foreach (var clip in tr.GetClips()) { var t = clip.asset as MoveObjectPlayable; director.SetReferenceValue(t.LerpMoveTo.exposedName, target.transform); director.SetReferenceValue(t.LerpMoveFrom.exposedName, Player.transform); } } } director.Play(); } } } } I would agree that a Tag system would be nice. By the way, what's the "Control Track" ? Thanks
nice - yeah looking up track names is a good way to do this. Control Track is a special track that lets you control Timeline sequences in other game objects. You can think of it as a 'macro' timeline track. You can organize scenes in a movie, each as their own track (for example) and then add a control track to a master timeline, which then drives the other timelines. Or you could have different team members working in their own timelines and combine them together to create your master shot sequence. How to use: 1) create control track 2) create control track shot clip (right click on track -> create) 3) select shot clip 4) add the game object with the 'other' timeline into the 'game object' reference on the inspector Now when you scrub the control track, it will play the timeline sequence in the references gameobject.
Not to be that person, lol, but I'm using my own class to manage timelines that I'm playing back during runtime as opposed to the PlayableDirector. So in my case, I don't have a PlayableDirector to do SetGenericBindings(...) for me -- instead, I'm managing my own PlayableGraph and inserting the playables generated by the TimelineAsset manually like this: Code (CSharp): //You can assume these variables existed already and were properly set PlayableGraph graph = //...; TimelineAsset timeline = //...; Playable timelinePlayable = timeline.CreatePlayable(graph, gameObject); However, some of the AnimationTracks in my timeline sequence don't seem to be properly binding to my scene objects. Is there a way for me to set the bindings via the ScriptPlayable<TimelinePlayable> that was inserted into my PlayableGraph? I saw some stuff about UnityEngine.PlayableBinding, but it only seems to contain the key, which was weird to me -- where's the value associated with that key?
After the timelinePlayable is created, you can bind (or change bindings) on the PlayableOutputs of the graph. Each track (non-group, non-override track) creates a corresponding output in the graph. (side note: This is where timelineAsset.GetOutputTracks() get it's naming). Use AnimationPlayableOutput.SetTarget, AudioPlayableOutput.SetTarget for animation and audio tracks respectively, and PlayableOutput.SetUserData for all other track types (which are all ScriptPlayableOutputs).
Ah, seant, you've come to my rescue again and again with the Timeline/Playables stuff! Thank you for your answer, I like the intuition that's behind those names, it makes more sense when you put it that way
So i should call AnimationPlayableOutput.SetTarget on what? I want to have a timeline object in my scene which is referenced in my Player class. From the player class i want to tell the timeline to change the actor of the animation. Both uses humanoid rig's i just want to play the same animation with different models depending on certain conditions. Is this doable at all? Im having trouble understanding the naming and how the different components interact with eachother.
Let's see if I can clarify it. The playable director stores bindings between tracks and targets (i.e. animators, gameObjects, audioSources). When playableDirector.Play() is called it creates a PlayableGraph, which can be thought of as an instance of a timeline. Each track, creates a corresponding PlayableOutput in the graph, and the playable director sets the target of each PlayableOutput. This means that you can change targets prior to playing the graph using PlayableDirector.SetGenericBinding(track, target); Once the graph is playing, this will have no effect until you Stop (not Pause) the graph, and Play again - this is generally true of any changes to the timeline itself. Once the graph is playing, you can switch targets by modifying the PlayableOutputs on the graph directly. For example, if you know the first track is always an animation track, you can change the target using the following: var animationOutput = (AnimationPlayableOutput) playableDirector.playableGraph.GetOutput(0); animationOutput.SetTarget(myAnimator); Hopefully that makes it a bit more clear.
EDIT: Apparently AnimationPlayableOutput is not a recognized type. Both AnimationPlayableAsset and AnimationPlayableUtilities is recognized, i have included the assembly references: using UnityEngine.Playables; using UnityEngine.TimeLine; This seems very strange... EDIT: Im using unity 2017.3
So i've managed to create a function which sets the Actors of animationclips inside the timeline at runtime. It works fine if you only want to set the actors once. Code (CSharp): using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; public class DynamicActors : MonoBehaviour { public PlayableDirector playableDirector; public PlayableAsset playableAsset; public GameObject[] newTarget = new GameObject[2]; public void ChangeActor() { var outputs = playableAsset.outputs; foreach (var itm in outputs) { Debug.Log(itm.streamName); if(itm.streamName == "Animation Track") { playableDirector.SetGenericBinding(itm.sourceObject, newTarget[0]); } else if(itm.streamName == "Animation Track1") { playableDirector.SetGenericBinding(itm.sourceObject, newTarget[1]); } } } } Thanks for the help Seant, this will do in my case, although i wonder why i cant access AnimationPlayableOutput. Cheers
Hey, I really Enjoy to use and learn about the Timeline features, It looks like a great way to create tools for content and graphic designers. Can you please help out? How can I do a binding to a specific Object (GameObject) that is located on specific playable ? I dont want to bind a track but a playable: same as it is done on the control track. Thanks in advance Adi
Control Tracks clips use exposed references for bindings. The exposed reference is simply an ID, and the actually binding is stored inside the PlayableDirector using Set/GetReferenceValue.
HIHI~How can I set the Animation Clip (Override) at RunTime?? Is it only possible in custom track? I want use TimeLine as a template.Want to feed character ,animationclip,specific position at running time ~~
By default, timeline is a template, and the bindings are specific to the playable director playing it. You can modify the content of the timeline using the API - e.g. walk the timeline using TimelineAsset.GetOutputTracks(), TrackAsset.GetClips(), TimelineClip.asset, etc.. - and make changes before playing the timeline. That means you can have multiple playable directors modifying the timeline, playing it, and each will play the same timeline with their own parameters. If you want to modify it while it's playing, you can try using PlayableDirector.RebuildGraph() after making changes. Otherwise you need to modify the playable graph (the compiled instance of the timeline), which can be tricky.
Thanks to seant_unity,You help me a lot.But a wierd thing is Where is the Graph??I never find the interface in unity editor.I can find the window" Playable Graph" in the image above
UnityEditor.Playables.Utility.GetAllGraphs() will get you all the current playable graphs. I believe that is what the visualizer is using.
Sorry I put the code line UnityEditor.Playables.Utility.GetAllGraphs() in void Start() .But noting shows!! The is the visualizer window " Playable Graph" ????
I am doing something to auto build timeline ControlPlayableAsset use code. Code (CSharp): void BlendTimeLine2Main(GameObject root, PlayableBinding item, int startIndex) { float fTime = 0; for (int i = startIndex; i < cameraInfos.Count + startIndex; i++) { var track = item.sourceObject as ControlTrack; var clip = track.CreateDefaultClip(); clip.displayName = cameraInfos[i - startIndex].nameCamera; var clip1 = clip.asset as ControlPlayableAsset; //clip1.name = cameraInfos[i - startIndex].nameCamera; var ERValue = new ExposedReference<GameObject>(); ERValue.defaultValue = root.transform.GetChild(i).gameObject; clip1.sourceGameObject = ERValue; clip.duration = cameraInfos[i - startIndex].time; clip.start = fTime; //clip1.sourceGameObject.Resolve( // timelineMain.GetComponent<PlayableDirector>().playableGraph.GetResolver()); fTime += cameraInfos[i - startIndex].time; } } I create a default clip and set the ControlPlayableAsset param in editor,but when i remove the test scene(with this plugin) in Hierarchy ,and drag the scene back ,the param in ControlPlayableAsset goon. Is there miss something? (unity 2018.3.5f1)
The defaultValue of the exposed reference won't serialize scene objects. You need to bind it using the PlayableDirector, using a name. e.g. clip1.sourceGameObject.exposedName = "myExposedName" + i; director.SetReferenceValue("myExposedName" + i, root.transform.GetChild(i).gameObject);
This worked in 2018.2, but I'm having various issues with binding to the timeline in this way in 2019.1: Using PlayableDirector.SetGenericBinding() on the *second* item in TimelineAsset.outputs results in binding to the *first* track in the timeline window (when I would expect it to bind to the second track in the timeline window). For a timeline with 2 tracks in the timeline window, TimelineAsset.outputs.Count() returns 3, when I would expect it to return 2. (PlayableDirector.playableGraph.GetOutputCount() correctly returns 2) It seems like there is some new inconsistency between how a timeline's tracks are represented in the timeline window vs how TimelineAsset.outputs is structured. (It also seems odd that TimelineAsset.outputs.Count() != PlayableDirector.playableGraph.GetOutputCount()) What would the recommended way be to bind to a particular timeline track at runtime (and before the graph is played) in 2019.1? Here's a sample unity 2019.1.2f1 project that reproduces the issue with SetGenericBinding: https://www.dropbox.com/s/34vpf07zbbjuj5k/Timeline 2019.1 Binding Test.zip?dl=0
Yes, there is! The inconsistency comes from animation tracks with humanoid characters. There are extra outputs in the playable graph that put a consistent T pose for the character inside the editor. In the player, or playmode, they outputs should be the same. You can iterate using timelineAsset.GetOutputTracks(). That gives a list of tracks that produce PlayableOutputs (i.e. excludes groups, override tracks, etc...). As in your code above the track is the input for SetGenericBinding.
I'm not sure I understand... I'm seeing that timelineAsset.GetOutputTracks().Count() returns 3 for a timeline with two animation tracks: Are you saying that timelineAsset.GetOutputTracks().Count() should return 2 in this case? I'm still not sure how I would correctly iterate timelineAsset.GetOutputTracks() to bind to this timeline.
Ah, there is an output track for the signals in 2019.1 as well. It's a hidden track that's exposed by the pin button.
Oh, I see, that makes sense! Part of my confusion was because our unity project has many timelines created before signals were introduced, so our new timelines have the signals output track and our old ones don't. I was able to get things to work by checking if the first item in TimelineAsset.GetOutputTracks() was a MarkerTrack or not, and skipping over it if it is. Thanks again @seant_unity, you saved me again
Is there some way to "tag" (string value?) a (animation) track somehow in the timeline window other than giving it a specific name? My timeline contains multiple animation tracks and for some of them I want to use SetGenericBinding to set a specific animator at runtime, with the above method I can get all the animation tracks, but I need a way to check for these specific animation tracks.
There isn't anything that I can think of on the track other than name to identify them. However, I do have a couple suggestions: Put them inside a group, and use the group track name. Inverse the relationship - you can keep a list of animation tracks that you'd like to bind at runtime in a list. They are scriptable objects, so they are can be referenced like any other scriptable objects. This is something you would need to script, Create a menu item callback that is active when the current selection is an animation track The item can check TimelineEditor.inspectedDirector gameObject for a monobehaviour that contains a list of AnimationTracks, and add it to the list (If you are using timeline 1.4, you could instead use a TrackAction to put it in the context menu for the animation track) You can unhide the animation tracks in the project window by removing HideFlag.hideInHierarchy on the hideFlags property (make sure to Save the Project afterwards or call AssetDatabase.SaveAssets()). They will appear as sub-objects of the timeline, similar to AnimationClips. They can then be used in drag and drop operations to store references to the tracks.
Hello, I've been using this base code to my own timeline binder since version 2018.x But it's acting weird since the timeline signals have been introduced to 2019.x Do you guys have an updated version of the script that works properly on 2019.x? Thank you in advance!
The problem is likely that the built-in marker track has pushed eveything down one item on the list. If it exists, it's the first track reported. Replacing line 33 with something like Code (CSharp): var track = (TrackAsset) ((timelineAsset.markerTrack == null) ? timelineAsset.outputs[i].sourceObject : timelineAsset.outputs[i+1].sourceObject); might fix it.
Thank you very much! It works with: Code (CSharp): var track = (TrackAsset)((timelineAsset.markerTrack == null) ? timelineAsset.GetOutputTrack(i) : timelineAsset.GetOutputTrack(i+1));
Thanks to the information provided I was able to create a playable director that is able to find two game objects by tag name and bind them to the appropriate tracks. The key is making sure the track names in the timeline match the gameobject tag names. Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; public class TimelineBinder : MonoBehaviour { public PlayableDirector playableDirector; //the playable director attached to this object public Animator firstObjectToBind; //the first game Object in scenes Animator to Bind public Animator secondObjectToBind; //the second game Object in scenes Animator to Bind private void Start() { //Find the game objects in the scene with the tags "Team1" and "Team2" firstObjectToBind = GameObject.FindGameObjectWithTag("Team1").GetComponent<Animator>(); secondObjectToBind = GameObject.FindGameObjectWithTag("Team2").GetComponent<Animator>(); //Find the playable director components attached to the object playableDirector = GetComponent<PlayableDirector>(); } private void Update() { // Call the PlayTimeLine method PlayTimeLine(); } public void PlayTimeLine() { //The tracks in the playable director timeline foreach (var playableAssetOutput in playableDirector.playableAsset.outputs) { // Debug.Log(playableAssetOutput.streamName.ToString()); if (playableAssetOutput.streamName == firstObjectToBind.tag.ToString()) { playableDirector.SetGenericBinding(playableAssetOutput.sourceObject, firstObjectToBind); } else if (playableAssetOutput.streamName == secondObjectToBind.tag.ToString()) { playableDirector.SetGenericBinding(playableAssetOutput.sourceObject, secondObjectToBind); } playableDirector.Play(); } } }//end class
If multiple playable directors share the same TimelineAsset and modify it, won't they play the same unique Timeline ? What do you mean by "own parameters" ? Do you mean the bindings ? It seems to me that, to use one TimelineAsset but modify the timeline for multiple playable directors, we need to edit the PlayableGraph. Sorry for the necro-bump, I am still getting a grasp on Timeline.
Hi! I think this whole system might not work anymore in unity 2021.3 because timelineAsset.outputs[ i ] cannot be accessed this way because outputs is no longer a list, but an IEnumerable. Also, it turns out the bindings are saved now based on the referenced timeline, so you can't really hotswap timelines in a PlayableDirector because the bindings are gone. I'm trying to implement a localization system that switches the timeline depending on the language needed. I would like to keep all the old bindings of the old PlayableDirector with the new TimelineAsset. Seems like I should be able to use the theory in this thread to transfer the bindings from script, but I am afraid the API has changed quite a bit and I can't find my way anymore. Any pointers? By the way, track references could also be AudioSources, not just GameObjects. LE: I have tried to save the references by sourceObject and apply them to a new timeline asset. It sometimes works, but sometimes breaks, especially when the number of tracks is not the same, or the order has changed, and I suspect it does not always remember the order so it's not really a reliable method. Maybe the logic of it is flawed - after all, the timeline asset doesn't know about the objects it references except through the tracks. But it would be nice if we had a way to reference the tracks besides drag and drop, say by name or unique ID or something, then we could make our own scripts to do this kinda stuff.