Timeline

该文详细介绍了如何在Unity中动态地赋值和管理Timeline中的ControlTrack和Clip,包括设置和获取Clip的值,以及动态绑定轨道值。同时,文章还讲解了如何动态注册和管理SignalTrack中的SignalEmitter事件响应,提供了完整的代码示例来变更Signal事件响应并处理回调。
摘要由CSDN通过智能技术生成

>>> 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)例子工程地址:

https://download.csdn.net/download/smile_otl/87959593

 >>> 常用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);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值