Unity Timeline学习笔记(4) - 自定义轨道OnCreateClip和CreateTrackMixer用法上的区分

前面我们第二篇文章Unity Timeline学习笔记(2) - PlayableTrack是一个初步的PlayableTrack使用方法,有时候可能会个性化定制专属轨道。

OnCreateClip的例子

下面我们做一个例子:

首先是轨道

csharp 复制代码
//FeatureTrack.cs
using System.ComponentModel;
using UnityEngine.Timeline;

[DisplayName("(Feature)开关轨道")]
[TrackClipType(typeof(FeaturePlayableAsset))]
[TrackColor(0.53f, 0.0f, 0.08f)]//表示在编辑器中,Track轨道前端的标识颜色(不重要啦)
public class FeatureTrack : TrackAsset
{
    protected override void OnCreateClip(TimelineClip clip)
    {
        base.OnCreateClip(clip);
        clip.displayName = "Clip显示名称";
    }
}

这里有TrackClipType表示我们只能添加类型为FeaturePlayableAsset的对象。

我们把PlayableAsset代码也放上:

csharp 复制代码
//FeaturePlayableAsset.cs
using System;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;

[DisplayName("(Feature)开关Clip")]
[Serializable]
public class FeaturePlayableAsset : PlayableAsset
{
    public string featureName;
    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    {
        var p = ScriptPlayable<FeaturePlayableBehaviour>.Create(graph);

        var b = p.GetBehaviour();
        b.featureName = featureName;

        return p;
    }
}

public class FeaturePlayableBehaviour : PlayableBehaviour
{
    public string featureName;
    // 当进入区域内触发Play
    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        Debug.Log($"触发 OnBehaviourPlay ,name: {featureName}");
    }

    // 当出了区域触发,或者开始的时候触发,或者停止运行的时候
    public override void OnBehaviourPause(Playable playable, FrameData info)
    {
        Debug.Log($"触发 OnBehaviourPause , name: {featureName}");
    }

     在区域内每个Frame地方都会触发

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        var p = (float)(playable.GetTime() / playable.GetDuration());
        //Debug.Log($"{featureName} - {p.ToString("F2")} , time:{playable.GetTime()} ,dur:{playable.GetDuration()}");
    }

}

我们这时候可以在轨道上点击鼠标右键,就只能看到只有这个对象了。

OnBehaviourPlay进入clip就触发。

OnBehaviourPause出了clip就触发。

当然Pause在运行时候会触发一次。

当前滑块可以用playable.GetTime() / playable.GetDuration()的方式获得到百分比。

这种就是OnCreateClip的用法。

下面我们来看CreateTrackMixer的用法

CreateTrackMixer的用法

因为目前我没有用到需要Mixer的算法,可能认识上有点偏差,我的理解是Mixer就是如图

交叉的这部分,如果某个值需要混合可以按照自己的需求去计算数值,权重可以这样获取:

csharp 复制代码
float inputWeight = playable.GetInputWeight(i);

我们先放上代码,然后再做对比。

首先是轨道

csharp 复制代码
//FeatureMixerTrack.cs
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[DisplayName("(FeatureMixer)开关轨道")]
[TrackClipType(typeof(FeatureMixerPlayableAsset))]
[TrackColor(0.53f, 0.0f, 0.08f)]//表示在编辑器中,Track轨道前端的标识颜色(不重要啦)
public class FeatureMixerTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount)
    {
        var temp = ScriptPlayable<FeatureMixerPlayableBehaviour>.Create(graph, inputCount);
        temp.GetBehaviour().fixedData = "传入的数据";
        return temp;
    }
    
}

这里调用了CreateTrackMixer的方法。

csharp 复制代码
//FeatureMixerPlayableAsset.cs
using System;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;

[DisplayName("(FeatureMixer)开关Clip")]
[Serializable]
public class FeatureMixerPlayableAsset : PlayableAsset
{
    public FeatureBehaviour template = new FeatureBehaviour();
    public ClipCaps clipCaps
    {
        get
        {
            return ClipCaps.Blending;
        }
    }
    public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
    {
        return ScriptPlayable<FeatureBehaviour>.Create(graph, template);
    }
}

[Serializable]
public class FeatureBehaviour : PlayableBehaviour
{
    public string featureName;  //开启某个feature
    public Material material;   //对应的材质球
    public AnimationCurve alphaCurve;   //修改某个参数

    [HideInInspector]
    public float passtime;  //计算当前块的播放进度
    [HideInInspector]
    public bool started;  //是否刚进入
}


public class FeatureMixerPlayableBehaviour : PlayableBehaviour
{
    public string fixedData;

    // 当进入区域内触发Play
    public override void OnBehaviourPlay(Playable playable, FrameData info)
    {
        Debug.Log("触发 OnBehaviourPlay");
    }

    // 当出了区域触发,或者开始的时候触发,或者停止运行的时候
    public override void OnBehaviourPause(Playable playable, FrameData info)
	{
	    int inputCount = playable.GetInputCount();
	    for (int i = 0; i < inputCount; i++)
	    {
	        ScriptPlayable<FeatureBehaviour> inputPlayable = (ScriptPlayable<FeatureBehaviour>)playable.GetInput(i);
	        FeatureBehaviour input = inputPlayable.GetBehaviour();
	        if (input.started)
	        {
	            input.started = false;
	            Debug.Log($"{input.featureName}出去了!!");
	        }
	    }
	}

    // 在区域内每个Frame地方都会触发

    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        int inputCount = playable.GetInputCount();
        for (int i = 0; i < inputCount; i++)
        {
            ScriptPlayable<FeatureBehaviour> inputPlayable = (ScriptPlayable<FeatureBehaviour>)playable.GetInput(i);
            FeatureBehaviour input = inputPlayable.GetBehaviour();

            float inputWeight = playable.GetInputWeight(i);

            if (!Mathf.Approximately(inputWeight, 0f))
            {
                input.passtime += info.deltaTime;

                // maxTime 当前clip的时长
                float maxTime = (float)PlayableExtensions.GetDuration(playable.GetInput(i));
                // curTime 当前clip的执行到哪个时刻
                float nowValue = input.alphaCurve.Evaluate(input.passtime / maxTime);
                //Debug.Log("" + input.featureName + "," + (input.passtime / maxTime) + ",nowValue:" + nowValue);

                //更新材质的变量
                //input.material.SetFloat("xxxx", nowValue);
                if (!input.started)
                {
                    input.started = true;
                    Debug.Log($"{input.featureName} 进入了");
                }
            }
            else
            {
                input.passtime = 0f;
                if (input.started)
                {
                    input.started = false;
                    Debug.Log($"{input.featureName} 出去了");
                }
            }
        }
    }

}

Mixer轨道在使用中发现和普通的轨道有区别:

函数OnBehaviourPlay和OnBehaviourPause失效

OnBehaviourPlay函数在TimeLine播放就执行了,并不会在clip进入才播放

OnBehaviourPause函数在整个TL播放结束才会进入。所以这两个函数失效了。

解决办法

我是这样解决的:

所以这里如果有进入和退出clip的时候要处理逻辑的,我研究可下加了两个变量来处理

started : 表示开始播放了

passtime :表示过去了多久

如果是Frame里的最后一帧刚好可能执行不到关闭,所以再OnBehaviourPause里也添加了结束.

具体就不描述了,参考代码中的ProcessFrame函数。

那么我们如果要处理每个clip,需要通过遍历来处理:

csharp 复制代码
int inputCount = playable.GetInputCount();
for (int i = 0; i < inputCount; i++)

获取当前的有几个inputCount也是clip,然后遍历进行访问计算。

本文写道这里就结束,其实很多地方还没搞懂,例如ClipCaps设置为Blending后这个如何自定使用,后面有时间再研究吧。

相关推荐
AT~2 小时前
unity 使用Socket和protobuf实现网络连接
unity·游戏引擎
怣疯knight9 小时前
Cocos creator判断节点是否能用的方法
unity·cocos2d
tealcwu9 小时前
Google Play的Keystore不可用时的解决方法
unity
呼呼突突9 小时前
Unity使用TouchSocket的RPC
unity·rpc·游戏引擎
qq 180809511 天前
从零构建一个多目标多传感器融合跟踪器
unity
平行云1 天前
实时云渲染支持在网页上运行UE5开发的3A大作Lyra项目
unity·云原生·ue5·webgl·虚拟现实·实时云渲染·像素流送
鹏飞于天1 天前
Shader compiler initialization error: Failed to read D3DCompiler DLL file
unity
wonder135791 天前
UGUI重建流程和优化
unity·游戏开发·ugui
那个村的李富贵1 天前
Unity打包Webgl后 本地运行测试
unity·webgl
nnsix1 天前
Unity OpenXR开发HTC Vive Cosmos
unity·游戏引擎