转载于http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=32842,介绍了timeline的轨道扩展

Timeline功能随Unity 2017.1发布后,收到了大量相关反馈,我们发现很多开发者不希望只把Timeline用作简单的序列工具。我们分享过文章《为Timeline创造性地设计脚本》,介绍如何利用Timeline的非常规用途。

本文将介绍如何使用Timeline来推动对话、支持分支功能以及连接游戏的AI系统。

关于Timeline
什么是Timeline?Timeline是一个线性编辑工具,用于序列化不同元素,包括动画剪辑、音乐、音效、摄像机画面、粒子特效以及其它Timeline。它大体上和Premiere®、After Effects®或Final Cut®类似,不同之处在于它为实时播放而设计。

Timeline一开始就以可扩展性为主要目标而设计,开发团队在设计该功能时考虑到,除了内置剪辑和轨道外,用户还会创建自制剪辑和轨道。所以用户提出了很多有关在Timeline使用脚本的问题。

<ignore_js_op>

Playable API
Playables API是一组强大的API,它能让你读取和混合多个数据源。例如:动画、音频等,并通过输出内容进行播放。该系统提供精准的程序控制,它的性能开销较低,并且针对性能进行了优化。

Timeline是基于Playables API实现的。当开始播放Timeline时,会构建一个由Playables节点组成的视图。它们会以PlayableGraph树形结构进行组织。

如果想要在场景中可视化PlayableGraph,例如:Animator和Timeline,你可以下载PlayableGraph Visualizer工具。本文将使用该工具可视化自定义剪辑的视图。更多关于Playable API和Animator相关视图的信息,请阅读:《Timeline功能亮点Playable API》。

演示项目
下文将展示四个简单示例,介绍如何使用Timeline的扩展功能。首先会介绍在Timeline添加脚本的最简单方法。然后再逐步增加更多概念,充分使用Timeline的更多功能。

本文所有示例内容你请访问下面地址下载:

本帖隐藏的内容

链接: https://pan.baidu.com/s/13Uu4NcsZMQXCf-GeI8kVOQ 密码: skzg

请注意,项目中将使用前缀来区分每个示例中的各个类,例如“Simple_”、“Track_”和“Mixer_”等。而在后文代码中,为提高可读性则省略了这些前缀。

示例1:自定义剪辑
第一个示例非常简单,目标是修改Light组件上的颜色和亮度属性,该组件带有自定义剪辑。

创建自定义剪辑需要二个脚本:
1、一个用于存放数据:从PlayableAsset继承
2、另一个用于处理逻辑:从PlayableBehaviour继承

Playable API的核心原则是分离逻辑和数据。所以首先需要创建PlayableBehaviour,使用该API编写行为,代码如下:

[C#] 纯文本查看 复制代码
public class LightControlBehaviour : PlayableBehaviour
{
   public Light light = null;
   public Color color = Color.white;
   public float intensity = 1f;
 
   public override void ProcessFrame(Playable playable, FrameData info, object playerData)
   {
       if (light != null)
       {
           light.color = color;
           light.intensity = intensity;
       }
   }
}

该代码实现了什么功能呢?首先它确定需要修改Light组件的哪些属性。而且PlayableBehaviour 拥有名为ProcessFrame 的方法可供重写。

每次更新时都会调用ProcessFrame。在该方法中,你可以设置Light组件的属性。然后为自定义剪辑创建PlayableAsset,代码如下:

[C#] 纯文本查看 复制代码
public class LightControlAsset : PlayableAsset
{
   public ExposedReference<Light> light;
   public Color color = Color.white;
   public float intensity = 1.0f;
  
   public override Playable CreatePlayable (PlayableGraph graph, GameObject owner)
   {
       var playable = ScriptPlayable<LightControlBehaviour>.Create(graph);
       var lightControlBehaviour = playable.GetBehaviour();
       lightControlBehaviour.light = light.Resolve(graph.GetResolver());
       lightControlBehaviour.color = color;
       lightControlBehaviour.intensity = intensity;
 
       return playable; 
   }
}

PlayableAsset有二个目的。首先它包含剪辑数据,因为它会在Timeline资源中序列化。其次它会构建PlayableBehaviour ,最后呈现为Playable视图。

第一行代码如下:

[C#] 纯文本查看 复制代码
var playable = ScriptPlayable<LightControlBehaviour>.Create(graph);

该代码会新建Playable,为它附加自定义行为LightControlBehaviour。然后可以在PlayableBehaviour设置光的属性。

那么代码中ExposedReference的作用是什么?由于PlayableAsset 是个资源,它不能直接引用场景中的对象。此时ExposedReference 会充当一个约定,表示在调用CreatePlayable 时,会解析一个对象。

现在可以在时间轴中添加Playable Track,并右键点击该新轨道添加自定义剪辑。将Light组件指定给该剪辑并查看结果。

https://v.qq.com/x/page/p07816563al.html

在这种情况下,内置Playable Track是个通用轨道,它能接收简单的Playable剪辑,例如:刚创建的剪辑。对于较复杂的情况,需要在专用轨道上托管剪辑。

示例2:自定义轨道
第一个示例的特点是每次添加自定义剪辑时,都要指定Light组件到每个剪辑上,如果剪辑数量较多,该过程非常麻烦。可以使用轨道的Bound(绑定)对象解决该问题。

<ignore_js_op> 
轨道结构图

轨道上可以绑定对象或组件,这意味着该轨道上的每个剪辑可以直接在绑定对象上操作。这是个很常见的行为,同时也是Animation、Activation和Cinemachine轨道的工作方式。

如果想要修改多个剪辑上的Light属性,可以创建自定义轨道,将Light组件作为绑定对象。创建自定义轨道需要扩展TrackAsset的脚本,代码如下:

[C#] 纯文本查看 复制代码
[TrackClipType(typeof(LightControlAsset))]
[TrackBindingType(typeof(Light))]
public class LightControlTrack : TrackAsset {}

这段代码有二个属性:
1、TrackClipType 指定该轨道将接受的PlayableAsset 类型。本例指定自定义LightControlAsset。
2、TrackBindingType 指定轨道要求绑定的类型,例如游戏对象、组件或资源。本例为Light组件。

现在还需要对PlayableAsset和PlayableBehaviour 稍作修改,从而使它们适用于轨道上。下面注释掉了不必要代码行,以供参考。

[C#] 纯文本查看 复制代码
public class LightControlBehaviour : PlayableBehaviour
{
   //public Light light = null;
   public Color color = Color.white;
   public float intensity = 1f;
 
   public override void ProcessFrame(Playable playable, FrameData info, object playerData)
   {
       Light light = playerData as Light;
       if (light != null)
       {
           light.color = color;
           light.intensity = intensity;
       }
   }
}

PlayableBehaviour 目前不需要Light变量。本示例中,ProcessFrame 方法直接提供轨道的绑定对象,只需将该对象转换为合适类型即可。

[C#] 纯文本查看 复制代码
public class LightControlAsset : PlayableAsset
{
   //public ExposedReference<Light> light;
   public Color color = Color.white;
   public float intensity = 1f;
 
   public override Playable CreatePlayable (PlayableGraph graph, GameObject owner)
   {
       var playable = ScriptPlayable<LightControlBehaviour>.Create(graph);
 
       var lightControlBehaviour = playable.GetBehaviour();
 
       //lightControlBehaviour.light = light.Resolve(graph.GetResolver());
       lightControlBehaviour.color = color;
       lightControlBehaviour.intensity = intensity;
 
       return playable;  
   }
}
 
PlayableAsset 不需要为Light组件保留ExposedReference 。该引用将由轨道托管,并直接交给PlayableBehaviour。
 
我们可以在Timeline中添加LightControl轨道,并给它绑定Light组件。现在给该轨道添加的每个剪辑都将根据该Light组件进行操作。
 
[url]https://v.qq.com/x/page/t0781rsxun3.html[/url]
 
如果使用Graph Visualizer展示该视图,效果如下:
 
[align=center] [attach]15184[/attach][/align]
 
上图中剪辑显示为右边名字相同的五个方块。可以把每个方块看作轨道。然后所有轨道都进入紫色方块表示的Timeline中。
 
请注意,Playable粉色方块实际上是Unity创建的Playable混合器。所以它和剪辑的颜色相同。混合器的概念将在示例3中说明。
 
[b]示例3:用混合器混合剪辑[/b]
Timeline支持重叠剪辑,从而在剪辑之间创建混合效果或交叉渐变效果。自定义剪辑也支持混合效果。为了启用混合功能,需要创建混合器,它能访问所有剪辑数据,然后混合数据。
 
混合器由PlayableBehaviour派生,就像前面使用的LightControlBehaviour。实际上混合器还使用ProcessFrame 函数。关键区别在于,该Playable会通过重写CreateTrackMixer函数在轨道脚本中声明为混合器。
 
LightControlTrack脚本代码如下:
[mw_shl_code=csharp,false]
[TrackClipType(typeof(LightControlAsset))]
[TrackBindingType(typeof(Light))]
public class LightControlTrack : TrackAsset
{
    public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) {
        return ScriptPlayable<LightControlMixerBehaviour>.Create(graph, inputCount);
    }
}

创建该轨道的Playable Graph后,它还会创建一个新行为即混合器,并将该行为连接到轨道上的所有剪辑。

你还要将逻辑从PlayableBehaviour 上移动到该混合器中。因此PlayableBehaviour中的代码较少。

[C#] 纯文本查看 复制代码
public class LightControlBehaviour : PlayableBehaviour
{
    public Color color = Color.white;
    public float intensity = 1f;
}

该代码只包含运行时中来自PlayableAsset 的数据。而混合器将拥有ProcessFrame 函数中的所有逻辑:

[C#] 纯文本查看 复制代码
public class LightControlMixerBehaviour : PlayableBehaviour
{
    // 注意:该函数会在运行时和编辑时调用。在设置属性数值时要注意这一点。
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
        Light trackBinding = playerData as Light;
        float finalIntensity = 0f;
        Color finalColor = Color.black;
 
        if (!trackBinding)
            return;
 
        int inputCount = playable.GetInputCount (); //get the number of all clips on this track
 
        for (int i = 0; i < inputCount; i++)
        {
            float inputWeight = playable.GetInputWeight(i);
            ScriptPlayable<LightControlBehaviour> inputPlayable = (ScriptPlayable<LightControlBehaviour>)playable.GetInput(i);
            LightControlBehaviour input = inputPlayable.GetBehaviour();
 
            finalIntensity += input.intensity * inputWeight;
            finalColor += input.color * inputWeight;
        }
 
        trackBinding.intensity = finalIntensity;
        trackBinding.color = finalColor;
    }
}

混合器可以访问轨道上的所有剪辑。本示例中,需要查看剪辑当前参与混合的所有亮度数值和颜色,所以要使用for循环在它们之中迭代。每次循环都要访问输入内容(GetInput(i)),并使用每个剪辑的权重(GetInputWeight(i))来构建最终数值,从而获得该剪辑对混合内容的贡献量。

假设要混合二个剪辑:一个剪辑贡献红色,另一个剪辑贡献白色。当混合内容为四分之一时,颜色由0.25 * Color.red + 0.75 * Color.white计算而来,这样会得到稍淡一点的红色。

在循环结束时,要将整体结果应用到绑定的Light组件。得到如下内容:

https://v.qq.com/x/page/l0781srhmr7.html

<ignore_js_op>

可以看出,红色方块表示你编写的混合器Playable,你可以完全控制它。这和示例2相反,示例2的混合器是由Unity默认提供的。

因为该视图处于混合中间,第二和第三个绿色方块都有白线连接着混合器,表示它们各自权重约有0.5。

请注意,不管何时在混合器实现混合,逻辑内容都取决于开发者。混合二个颜色很简单,但混合AI系统中二个代表不同AI状态的剪辑会产生什么结果呢?是否会在UI出现二行对话?如何在定格动画中混合二个静态姿势呢?

或许混合不是连续的,但它是以“阶梯式”进行的,所以姿势会以离散增量相互变形,例如:0, 0.25, 0.5, 0.75, 1。有了这个功能强大的系统,便可以得到各种有趣的情景。

示例4:为自定义剪辑设置动画
作为本指南最后一部分,我们首先来回顾一下前面的示例,然后使用“模板”来实现另一种移动数据的方式。该方法的优点是,它能让你设置模板属性的关键帧,从而可以在Timeline中为自定义剪辑创建动画。

在前面示例中有一个Light组件的引用,指向PlayableAsset和PlayableBehaviour上的颜色和亮度。该数据在PlayableAsset 的检视窗口上设置,然后在创建视图时,会在运行时复制到PlayableBehaviour 中。

该方法能有效处理数据,但会复制之后需要一直保持同步的数据。但复制这些数据很容易产生错误。或者你也可以通过在PlayableAsset创建引用,从而使用PlayableBehaviour 的“模板”概念。

因此首先如下重写LightControlAsset :

[C#] 纯文本查看 复制代码
public class LightControlAsset : PlayableAsset
{
    public LightControlBehaviour template;
 
    public override Playable CreatePlayable (PlayableGraph graph, GameObject owner) {
        var playable = ScriptPlayable<LightControlBehaviour>.Create(graph, template);
        return playable;
    }
}

LightControlAsset 现在只有对LightControlBehaviour的引用,而不是包含它的数据。代码量比之前更少。

LightControlBehaviour 保持原样即可:

[C#] 纯文本查看 复制代码
[System.Serializable]
public class LightControlBehaviour : PlayableBehaviour
{
    public Color color = Color.white;
    public float intensity = 1f;
}

对该模板的引用会在选中Timeline剪辑时,自动产生如下检视窗口:

<ignore_js_op>

完成该脚本后,就可以开始设置动画了。

请注意,如果创建新剪辑,你将在轨道标题栏看到红色的圆形按钮。它表示该剪辑现在不需要添加Animator就可以设置关键帧。你只需点击该红色按钮,选取剪辑,定位创建该关键帧的播放头位置,然后修改该属性数值即可。

你也可以点击白色的方形按钮,展开Curves视图来查看关键帧创建的曲线。

<ignore_js_op>

这样做还有的优点是你可以双击Timeline剪辑,让Unity打开Animation面板,然后将该剪辑链接到Timeline。你可以注意到,当下图的按钮出现时,这些剪辑将被链接。

<ignore_js_op>

这样在对Timeline和Animation窗口进行处理时,播放头将保持同步,从而完全控制关键帧。你可以在Animation窗口修改动画,在更便捷的环境中处理关键帧。

<ignore_js_op>

07.png (15.52 KB, 下载次数: 2)

下载附件

2018-9-10 23:49 上传

 

在该视图中,你可以充分使用动画曲线和摄影表(Dopesheet),来优化自定义剪辑的动画。

请注意,在通过这种方法设置动画时,会创建动画剪辑。可以在Timeline资源下找到这些剪辑。

<ignore_js_op>

结语
我们希望本文能作为实用向导指南,介绍通过Timeline可以实现的诸多功能,从而让你使用脚本将Timeline提升到新的水平。我们也希望大家使用Timeline创作出更多优秀的作品!

更多Unity最新最全面的功能介绍尽在Unity官方中文论坛(UnityChina.cn)!

Timeline扩展功能实践指南的更多相关文章

  1. Celery的实践指南

    http://www.cnblogs.com/ToDoToTry/p/5453149.html Celery的实践指南   Celery的实践指南 celery原理: celery实际上是实现了一个典 ...

  2. [CoreOS 转载] CoreOS实践指南(七):Docker容器管理服务

    转载:http://www.csdn.net/article/2015-02-11/2823925 摘要:当Docker还名不见经传的时候,CoreOS创始人Alex就预见了这个项目的价值,并将其做为 ...

  3. [CoreOS 转载] CoreOS实践指南(五):分布式数据存储Etcd(上)

    转载:http://www.csdn.net/article/2015-01-22/2823659 摘要:在“漫步云端:CoreOS实践指南”系列的前几篇,分别介绍了如何架设CoreOS集群,系统服务 ...

  4. [CoreOS 转载] CoreOS实践指南(四):集群的指挥所Fleet

    转载:http://www.csdn.net/article/2015-01-14/2823554/2 摘要:CoreOS是采用了高度精简的系统内核及外围定制的操作系统.ThoughtWorks的软件 ...

  5. OpenGL ES应用开发实践指南:iOS卷

    <OpenGL ES应用开发实践指南:iOS卷> 基本信息 原书名:Learning OpenGL ES for iOS:A Hands-On Guide to Modern 3D Gra ...

  6. 《赢在用户:Web人物角色创建和应用实践指南》阅读总结

           本书针对创建人物角色的每一个步骤,包括进行定性.定量的用户研究,生成人物角色分类,使人物角色真实可信等进行了十分详细的介绍.而且,在人物角色如何指导总体商业策略.确定信息架构.内容和设计 ...

  7. lua游戏开发实践指南学习笔记1

    本文是依据lua游戏开发实践指南做的一些学习笔记,仅用于继续自己学习的一些知识. Lua基础 1.  语言定义: 在lua语言中,标识符有非常大的灵活性(变量和函数名),只是用户不呢个以数字作为起始符 ...

  8. 《App架构实践指南》

    推荐书籍 <App 架构实践指南>

  9. Python 最佳实践指南 2018 学习笔记

    基础信息 版本 Python 2.7 Python 3.x Python2.7 版本在 2020 年后不再提供支持,建议新手使用 3.x 版本进行学习 实现 CPython:Python的标准实现: ...

随机推荐

  1. vim命令:编辑模式和命令模式

      vim:编辑模式 从一般模式进入编辑模式,只需你按一个键即可(i,I,a,A,o,O,r,R).当进入编辑模式时,会在屏幕的最下一行出现“INSERT或REPLACE”的字样.从编辑模式回到一般模 ...

  2. springmvc的前端控制器

    <servlet> <servlet-name>xxx</servlet-name> <servlet-class>org.springframewor ...

  3. table 里输入rules 验证

    HTML <el-form ref='from' :model="fromData"> <el-table ref="tableman" bo ...

  4. P1536 村村通

    原题链接 https://www.luogu.org/problemnew/show/P1536 昨天刚学的并查集,今天正好练习一下,于是就找到了这个题 看起来好像很简单,尤其是你明白了思路之后,完全 ...

  5. CF802C Heidi and Library (hard)

    题目描述 你有一个容量为k的空书架,现在共有n个请求,每个请求给定一本书ai,如果你的书架里没有这本书,你就必须以ci的价格购买这本书放入书架.当然,你可以在任何时候丢掉书架里的某本书.请求出完成这n ...

  6. 小白月赛13 小A与小B (双向BFS)

    链接:https://ac.nowcoder.com/acm/contest/549/G来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  7. 理解vue 修饰符sync

    也是在vux中看到了这个sync 现在我们来看看vue中的sync 我们先看下官方文档:vue .sync 修饰符,里面说vue .sync 修饰符以前存在于vue1.0版本里,但是在在 2.0 中移 ...

  8. MySQL 水平拆分与垂直拆分详解

    前言:说到优化mysql,总会有这么个回答:水平拆分,垂直拆分,那么我们就来说说什么是水平拆分,垂直拆分. 一.垂直拆分 说明:一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将 ...

  9. 关于vue-router 中参数传递的那些坑(params,query)

    1.query方式传参和接受参数 传参 this.$router.push({ path:'/xxx' query:{ idname:id } })接收的方式:this.$route.query.id ...

  10. linux 单用户密码修改

    1.启动系统,并在GRUB2启动屏显时,按下e键进入编辑模式. 2.在linux16/inux/linuxef所在参数行ro更改为init=/sysroot/bin/sh 3.按Crl+x启动到she ...