>>> Control Track
动态赋值:
var playableDirector = default(PlayableDirector);
var controlTrack = default(ControlTrack);
foreach (var bind in playableDirector.playableAsset.outputs) {
if (bind.sourceObject is ControlTrack) {
if (bind.streamName == "MyControlTrack") {
controlTrack = bind.sourceObject as ControlTrack;
foreach (var clip in controlTrack.GetClips()) {
var asset = clip.asset as ControlPlayableAsset;
var er = new ExposedReference<GameObject> {
defaultValue = obj//(your OBJ)
};
asset.sourceGameObject = er;
break;//只赋值第一个clip
}
}
}
}
获取Clip的值
var playableDirector = default(PlayableDirector);
var controlTrack = default(ControlTrack);
foreach (var bind in playableDirector.playableAsset.outputs) {
if (bind.sourceObject is ControlTrack) {
if (bind.streamName == "MyControlTrack") {
controlTrack = bind.sourceObject as ControlTrack;
foreach (var clip in controlTrack.GetClips()) {
var o = clip.asset as ControlPlayableAsset;
if (o == null) continue;
//Editor状态下
GameObject obj = playableDirector.GetReferenceValue(o.sourceGameObject.exposedName, out bool idValid) as GameObject;
//Play状态下
GameObject obj = o.sourceGameObject.Resolve(playableDirector) as GameObject;
}
}
}
}
>>> 动态绑定轨道值
playableDirector.SetGenericBinding(bind.sourceObject, youValue)
var playableDirector = default(PlayableDirector);
var controlTrack = default(ControlTrack);
foreach (var bind in playableDirector.playableAsset.outputs) {
if (bind.streamName == "AnimTrack") {
var anim = default(Animator);
playableDirector.SetGenericBinding(bind.sourceObject, anim);
}
if (bind.streamName == "PositionAnim") {
var anim = default(Animator);
playableDirector.SetGenericBinding(bind.sourceObject, anim);
}
if (bind.streamName == "ObjActive") {
var obj = GameObject.Find("MyObj");
playableDirector.SetGenericBinding(bind.sourceObject, obj);
}
}
>>> 动态绑定/注册SignalTrack里的SignalEmitter的自定义响应事件
1.使用SignalTrack
1)创建轨道,指定事件接收者,添加时间帧
2)创建SignalAsset
3)设置Receiver里的Signal
至此,Signal事件帧设定完成,接着为动态给Signal注册事件响应
2.动态注册事件响应
1)获取到轨道里的Signal对应的接收器SignalReceiver
这一步很重要,必须是获取到对应的接收器
var receiver = director.GetComponent<SignalReceiver>();
2)通过SignalReceiver.GetReactionAtIndex() 获取到接收器里对应Signal的响应UnityEvent,index为SignalReceiver里的Signal响应序号
var evt = receiver.GetReactionAtIndex(index);
3)通过SignalReceiver.ChangeReactionAtIndex()可以设置接收器里对应Signal的响应UnityEvent,index为SignalReceiver里的Signal响应序号
receiver.ChangeReactionAtIndex(index, evt);
4)完整代码
/// <summary> /// /// </summary> /// <param name="director">Timeline载体对象,为了从它身上获取到SignalReceiver组件</param> /// <param name="index">SignalReceiver里的Signal序号</param> /// <param name="action">需要注册的事件回调</param> /// <param name="isAdd">是否为追加事件回调</param> /// <param name="isClearOnUsed">是否在触发调用后清除所有回调</param> private void ChangeSignalEventAtIndex(PlayableDirector director, int index, UnityAction action, bool isAdd = false, bool isClearOnUsed = true) { var receiver = director.GetComponent<SignalReceiver>(); //获取轨道里的Signal对应的接收器SignalReceiver var evt = receiver.GetReactionAtIndex(index); //通过SignalReceiver.GetReactionAtIndex()获取接收器里对应Signal的响应UnityEvent,index为SignalReceiver里的Signal响应序号 evt ??= new UnityEvent(); if (!isAdd) evt.RemoveAllListeners(); void Act() { action?.Invoke(); if (isClearOnUsed) { evt.RemoveAllListeners(); receiver.ChangeReactionAtIndex(index, evt); } } evt.AddListener(Act); receiver.ChangeReactionAtIndex(index, evt); //通过SignalReceiver.ChangeReactionAtIndex()可以设置接收器里对应Signal的响应UnityEvent,index为SignalReceiver里的Signal响应序号 }
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.Playables; using UnityEngine.Timeline; public class SignalTest : MonoBehaviour { public PlayableDirector Director; void Update() { if (Input.GetKeyDown(KeyCode.Alpha1)) { ChangeSignalEventAtIndex(Director, 0, Action1); Debug.Log("Action1"); } else if (Input.GetKeyDown(KeyCode.Alpha2)) { ChangeSignalEventAtIndex(Director, 0, Action2); Debug.Log("Action2"); } else if (Input.GetKeyDown(KeyCode.Alpha3)) { ChangeSignalEventAtIndex(Director, 0, Action3); Debug.Log("Action3"); } else if (Input.GetKeyDown(KeyCode.Alpha4)) { ClearEventListeners(Director, 0); Debug.Log("ClearEventListeners"); } if (Input.GetKeyDown(KeyCode.Space)) { Director.Stop(); Director.Play(); } } /// <summary> /// /// </summary> /// <param name="director">Timeline载体对象,为了从它身上获取到SignalReceiver组件</param> /// <param name="index">SignalReceiver里的Signal序号</param> /// <param name="action">需要注册的事件回调</param> /// <param name="isAdd">是否为追加事件回调</param> /// <param name="isClearOnUsed">是否在触发调用后清除所有回调</param> private void ChangeSignalEventAtIndex(PlayableDirector director, int index, UnityAction action, bool isAdd = false, bool isClearOnUsed = true) { var receiver = director.GetComponent<SignalReceiver>(); //获取轨道里的Signal对应的接收器SignalReceiver var evt = receiver.GetReactionAtIndex(index); //通过SignalReceiver.GetReactionAtIndex()获取接收器里对应Signal的响应UnityEvent,index为SignalReceiver里的Signal响应序号 evt ??= new UnityEvent(); if (!isAdd) evt.RemoveAllListeners(); void Act() { action?.Invoke(); if (isClearOnUsed) { evt.RemoveAllListeners(); receiver.ChangeReactionAtIndex(index, evt); } } evt.AddListener(Act); receiver.ChangeReactionAtIndex(index, evt); //通过SignalReceiver.ChangeReactionAtIndex()可以设置接收器里对应Signal的响应UnityEvent,index为SignalReceiver里的Signal响应序号 } private void Action1() { Debug.Log("[Signal] Action1"); } private void Action2() { Debug.Log("[Signal] Action2"); } private void Action3() { Debug.Log("[Signal] Action3"); } private void ClearEventListeners(PlayableDirector director, int index) { var receiver = director.GetComponent<SignalReceiver>(); //receiver.ChangeReactionAtIndex(index, null); //可以直接置空 var evt = receiver.GetReactionAtIndex(index); evt ??= new UnityEvent(); evt.RemoveAllListeners(); receiver.ChangeReactionAtIndex(index, evt); } }
5)例子工程地址:
>>> 常用API
using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Events; using UnityEngine.Playables; using UnityEngine.Timeline; using XLua; namespace TimelineAPI{ public static partial class TimelineUtils { public static void ForeachOutputs(IEnumerable<PlayableBinding> outputs, System.Action<PlayableBinding> action) { foreach (var item in outputs) { action?.Invoke(item); } } //动态绑定指定名轨道的值 public static void SetTimelineBinding(PlayableDirector director, string name, Object value, bool isErrorWhenNotFound = false) { var obj_keys = GetTimelineBindingKey(director, name); if (obj_keys.Count != 0) { foreach (var key in obj_keys) { director.SetGenericBinding(key, value); } } else { if (isErrorWhenNotFound) { throw new KeyNotFoundException(string.Format("The given key \"{0}\" was not find in the timeline.", name)); } } } //动态获取指定名的所有轨道 public static List<Object> GetTimelineBindingKey(PlayableDirector director, string name) { List<Object> keys = new List<Object>(); foreach (var bd in director.playableAsset.outputs) { if (bd.streamName == name) { keys.Add(bd.sourceObject); } } return keys; } //动态绑定指定名的动画轨道的animationClip public static void ChangeTimelineAnimationClip(PlayableDirector director, string trackName, string clipName, AnimationClip targetClip) { var tracks = GetTimelineBindingKey(director, trackName); if (tracks.Count <= 0) { return; } for (int i = 0; i < tracks.Count; ++i) { var anim_track = tracks[i] as AnimationTrack; if (anim_track == null) { continue; } foreach (var item in anim_track.GetClips()) { var asset = item.asset as AnimationPlayableAsset; if (asset.name == clipName) { asset.clip = targetClip; } } } } public static void SetClipSourceAsset(PlayableDirector director, string trackName, string clipName, GameObject value) { foreach (var bd in director.playableAsset.outputs) { if (bd.streamName == trackName) { var ct = bd.sourceObject as ControlTrack; var clips = ct.GetClips(); foreach (var c in clips) { if (c.displayName == clipName) { var asset = c.asset as ControlPlayableAsset; var o = new ExposedReference<GameObject>(); o.defaultValue = value; asset.sourceGameObject = o; } } } } } public static GameObject GetControlTrackClipValue(PlayableDirector director, ControlTrack controlTrack) { foreach (var clip in controlTrack.GetClips()) { var o = clip.asset as ControlPlayableAsset; if (o == null) continue; GameObject obj = o.sourceGameObject.Resolve(director) as GameObject; return obj; } return null; } public static void ChangeEventAtIndex(PlayableDirector director, int index, UnityAction action) { var receiver = director.GetComponent<SignalReceiver>(); var evt = receiver.GetReactionAtIndex(index); evt ??= new UnityEvent(); evt.RemoveAllListeners(); void Act() { action?.Invoke(); evt.RemoveAllListeners(); receiver.ChangeReactionAtIndex(index, evt); } evt.AddListener(Act); receiver.ChangeReactionAtIndex(index, evt); } } }