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后这个如何自定使用,后面有时间再研究吧。

相关推荐
Sitarrrr1 小时前
【Unity】ScriptableObject的应用和3D物体跟随鼠标移动:鼠标放置物体在场景中
3d·unity
极梦网络无忧1 小时前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
逐·風9 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
_oP_i11 小时前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
Leoysq20 小时前
【UGUI】实现点击注册按钮跳转游戏场景
游戏·unity·游戏引擎·ugui
_oP_i1 天前
unity中 骨骼、纹理和材质关系
unity·游戏引擎·材质
Padid1 天前
Unity SRP学习笔记(二)
笔记·学习·unity·游戏引擎·图形渲染·着色器
Tp_jh1 天前
推荐一款非常好用的C/C++在线编译器
linux·c语言·c++·ide·单片机·unity·云原生
dangoxiba2 天前
[Unity Demo]从零开始制作空洞骑士Hollow Knight第十八集补充:制作空洞骑士独有的EventSystem和InputModule
游戏·unity·c#·游戏引擎·playmaker
无敌最俊朗@2 天前
unity3d————屏幕坐标,GUI坐标,世界坐标的基础注意点
开发语言·学习·unity·c#·游戏引擎