前言
在使用TimeLine的过程中,需要一个在其clip开始或者结束仅执行一次自定义方法的功能。
解决方案
- 首先引用【Default Playables】的插件来方便自定义事件的开发
- 再在创建的自定义脚本中绑定指定事件即可
感谢B站用户【小小故事汇】TimeLine的进阶玩法,用Playable自定义轨道_哔哩哔哩_bilibili
- 下面解决仅在clip的开始或者clip的尾部执行自定义方法
- 跟着步骤创建完自定义轨道脚本后找到相应的【Behaviour】脚本例如【DeductiveStoryBehaviour】向该脚本添加事件
[Serializable] public class DeductiveStoryBehaviour : PlayableBehaviour { // 这里的UnityEvent也可以改为Action, 使用UnityEvent是为了方便看 public UnityEvent OnStart; public UnityEvent OnFrame; public UnityEvent OnEnd; }
- 找到相应的【MixerBehaviour】脚本例如的【DeductiveStoryMixerBehaviour】修改内容
private float[] weights; private bool initWeight = false; public override void PrepareFrame(Playable playable, FrameData info) { base.PrepareFrame(playable, info); int inputCount = playable.GetInputCount(); if (!initWeight && inputCount > 0) { weights = new float[inputCount]; initWeight = true; } for (int i = 0; i < inputCount; i++) { float inputWeight = playable.GetInputWeight(i); ScriptPlayable<DeductiveStoryBehaviour> inputPlayable = (ScriptPlayable<DeductiveStoryBehaviour>)playable.GetInput(i); DeductiveStoryBehaviour input = inputPlayable.GetBehaviour(); if (weights[i] == 0 && inputWeight > 0) { input.OnStart?.Invoke(); } input.OnFrame?.Invoke(); if (weights[i] > 0 && inputWeight == 0) { input.OnEnd?.Invoke(); } weights[i] = inputWeight; } }
- 跟着步骤创建完自定义轨道脚本后找到相应的【Behaviour】脚本例如【DeductiveStoryBehaviour】向该脚本添加事件
测试
在TimeLine中创建自定义轨道并创建Clip后在监视版中即可查看到结果,并执行
对Director的播放、暂停、恢复,停止的拓展
这里我先是创建了一个名为【TimeLineDirector】的脚本,脚本中仅有对TimeLine的基本操作
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
/****************************************************
文件:TimeLineDirector.cs
作者:GTY
日期:2024/4/27 15:24:27
功能:TimeLine
*****************************************************/
public enum TimeLineDirectorEventType
{
Play,
Stop,
Pause,
Resume,
}
public class TimeLineDirector : MonoBehaviour
{
public PlayableDirector playableDirector;
public Action onFinish;
public Action<PlayableDirector> onPlayed;
public Action<PlayableDirector> onStopped;
public Action<PlayableDirector> onPaused;
public float GetDuration { get => (float)playableDirector.duration; }
public float Time { get => (float)playableDirector.time; set { playableDirector.time = value; } }
private void Awake()
{
if (playableDirector == null) playableDirector = GetComponent<PlayableDirector>();
playableDirector.played += onPlayed;
playableDirector.stopped += onStopped;
playableDirector.paused += onPaused;
}
public float Play()
{
playableDirector.Play();
float duration = (float)playableDirector.duration;
Invoke("FinishInvoke", duration);
return duration;
}
public float Play(PlayableAsset playableAsset)
{
playableDirector.Play(playableAsset);
float duration = (float)playableDirector.duration;
Invoke("FinishInvoke", duration);
return duration;
}
public void Stop()
{
playableDirector.Stop();
}
public void Pause()
{
playableDirector.Pause();
}
public void Resume()
{
// 暂停后才能用
playableDirector.Resume();
}
void FinishInvoke()
{
Debug.Log("TimeLine播放结束");
onFinish?.Invoke();
}
}
接着我创建了一个名为【TimeLineDirectorCtrl】的自定义轨道
在【Behaviour】中添加
[Serializable]
public class TimeLineDirectorCtrlBehaviour : PlayableBehaviour
{
public TimeLineDirectorEventType timeLineDirectorEventType;
}
在【MixerBehaviour】中获取到绑定的【TimeLineDirector】并添加如下代码
public class TimeLineDirectorCtrlMixerBehaviour : PlayableBehaviour
{
private float[] weights;
private bool initWeight = false;
void BindingEvent(TimeLineDirector trackBinding, TimeLineDirectorCtrlBehaviour behaviour)
{
switch (behaviour.timeLineDirectorEventType)
{
case TimeLineDirectorEventType.Play:
trackBinding.Play();
break;
case TimeLineDirectorEventType.Pause:
trackBinding.Pause();
break;
case TimeLineDirectorEventType.Resume:
trackBinding.Resume();
break;
case TimeLineDirectorEventType.Stop:
trackBinding.Stop();
break;
}
}
// NOTE: This function is called at runtime and edit time. Keep that in mind when setting the values of properties.
public override void ProcessFrame(Playable playable, FrameData info, object playerData)
{
TimeLineDirector trackBinding = playerData as TimeLineDirector;
if (!trackBinding)
return;
int inputCount = playable.GetInputCount();
if (!initWeight && inputCount > 0)
{
weights = new float[inputCount];
initWeight = true;
}
for (int i = 0; i < inputCount; i++)
{
float inputWeight = playable.GetInputWeight(i);
ScriptPlayable<TimeLineDirectorCtrlBehaviour> inputPlayable = (ScriptPlayable<TimeLineDirectorCtrlBehaviour>)playable.GetInput(i);
TimeLineDirectorCtrlBehaviour input = inputPlayable.GetBehaviour();
if (weights[i] == 0 && inputWeight > 0)
{
// input.OnStart?.Invoke();
BindingEvent(trackBinding, input);
}
// input.OnFrame?.Invoke();
// if (weights[i] > 0 && inputWeight == 0)
// {
// input.OnEnd?.Invoke();
// }
weights[i] = inputWeight;
}
}
}
这样就会在该Clip一开始或者结束时执行播放、停止、暂停,恢复等操作
结尾
这个功能应该能很好的解决默认Clip一直执行的问题(确信)。