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

相关推荐
ttod_qzstudio13 小时前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊14 小时前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云17 小时前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质
__water2 天前
RHA《Unity兼容AndroidStudio打Apk包》
android·unity·jdk·游戏引擎·sdk·打包·androidstudio
两水先木示3 天前
【Unity3D】微信小游戏适配安全区域或胶囊控件(圆圈按钮)水平高度一致方案
unity·微信小游戏·安全区域·ui适配·胶囊控件·safearea
枯萎穿心攻击3 天前
ECS由浅入深第三节:进阶?System 的行为与复杂交互模式
开发语言·unity·c#·游戏引擎
不绝1913 天前
怪物机制分析(有限状态机、编辑器可视化、巡逻机制)
网络·游戏·unity·游戏引擎
unicrom_深圳市由你创科技3 天前
Unity开发如何解决iOS闪退问题
unity·ios·蓝桥杯