关卡序列提供了大量脚本API,能让你对关卡序列进行自动创建和修改。本文中,我们将探索关卡序列编辑器的自动化流程,创建一些能够扩展Sequencer功能的工具,从而满足项目的各种需求。
本指南还将为大家介绍各类API,剖析关卡序列的结构,同时展示一些的常见流程,帮助大家了解基础知识,学会构建自己的脚本。
蓝图与Python
在本文中,所有示例都将以蓝图图表的形式提供。我们在网页*上提供了Python版本的示例。Python API是通过蓝图API创建的,因此这些示例可以轻松改写成Python脚本。我们还在Sequencer脚本编辑插件的内容文件夹中提供了很多示例,详情参见
Engine\Plugins\MovieScene\SequencerScripting\Content\Python
https://docs.unrealengine.com/5.2/zh-CN/python-scripting-in-sequencer-in-unreal-engine/
UI重构以及关卡序列刷新事宜
从5.1版本开始,我们对Sequencer的用户界面进行了重构,现在不需要手动刷新就可以显示更新后的内容。在旧版本中,你可以调用“刷新当前的关卡序列”,手动刷新序列。
编辑器与运行时脚本
这些示例主要用于在编辑器工具控件(Editor Utility Widget)中运行,并且展示了一些仅用于编辑器的功能,目的在于介绍API的功能。其中有些函数还可以在运行时在打包项目中被调用,有些则不行。例如,在打包版本中不能访问通道和创建资产。
在打包项目中,有些函数可以在运行时状态下修改关卡序列。默认情况下,为了避免降低性能,在打包项目中,关卡序列资产不会响应运行时变化,不过你可以针对资产进行单独设置。方法是,将资产标记为易变。可以通过“高级(Advanced)”中的操作菜单来完成。
关卡序列结构
在使用脚本API时,熟悉一个序列的内部结构有助于确定在创建或更改现有序列时需要与哪些元素交互。
任务和对象绑定
在序列中,轨道或对象绑定,是用来组织和添加元素的根元素。轨道要么独立运行(比如Camera Cut轨道),要么作为对象绑定的一部分,代表在序列中被赋予了动画的Actor。对象绑定将多个轨道绑定到一个特定对象上,或者绑定到某个已经存在于关卡中的对象上(可持有对象)或由序列生成的对象(可生成对象)上。对象绑定上的轨道包括骨骼动画轨道、属性轨道和变换轨道等。
轨道对应UI树形大纲视图中的一行,以及该行在时间轴中的全部内容。轨道相当于分段(section)的容器,可能包含一个或多个分段。分段定义了动画评估(evaluation)的时间跨度;因此仅仅存在一个轨道并不足以定义动画。
分段
我们知道,分段(section)定义了轨道的时间跨度;其中包含动画评估的细节信息,例如关键帧、对动画数据的引用情况(比如骨骼动画、其他对象或其他序列)。它们可以拥有无限范围的起始和/或结束,有些还支持分段之间的混合/重叠。分段还有不同的混合类型,以便调整数值的计算方式,例如绝对(Absolute)和相对(Relative)。绝对型分段会直接设置数值,而相对型分段会考虑之前的动画数值,并将关键帧值添加在预动画的值上。
通过脚本添加分段时,必须确保分段有适当的起始点和结束点,因为通过用户界面添加的默认值在使用脚本API时无法使用。它们的默认范围是0帧。
通道
对于支持关键帧的分段来说,关键帧将被储存在分段的一个或多个通道中。例如变换轨道有9个通道,对应位置、旋转和缩放的不同组成部分(X、Y和Z)。
关键帧
存储在通道上的关键帧数据由时间和值,还有类型(三次、线性、常数等)和切线等插值信息组成。
结构剖析
序列结构剖析
1.轨道(独立):与关卡中对象无关。图中的Camera Cut轨道独立存在,不与某个Actor相关联(每个Camera Cut分段都可以引用不同的摄像机Actor)。
2.对象绑定:轨道的集合;包含对一个或多个Actor的引用。图中的摄像机Actor是一个可生成对象,包含一系列用于创建动画效果的轨道。
3.分段:控制轨道的时间数据。在这个例子中,Camera Cut分段控制着轨道使用的摄像机以及相应时长。
4.通道:为分段添加关键帧的舞台。通道控制一个特定值。图中,变换分段包含变换的各个组成部分,每个组成部分都有自己的通道来保存关键帧。这个通道控制着摄像机位置的X值。
5.关键帧:在特定时间为通道赋予特定值,它还有相关的插值方法。在这个例子中,变换的旋转处于折叠状态,表示该关键帧位置上,旋转的三个通道上都有一个单独的关键帧。
总的来说,对象绑定将对象/Actor关联到轨道上;轨道中包含由通道组成的分段,而通道中保存着关键帧。轨道和分段共同控制动画。如果值需要随时间变化,可能需要包含关键帧的通道。例如摄像机的焦距或变换等属性轨道。它们还可以将序列与其他系统同步,比如骨骼动画轨道,使用序列的播放位置来评估动画,并将数据发送到动画蓝图中。还可以和通道结合,例如骨骼动画轨道有一个权重值可以制作成动画,用来控制被评估动画的权重。
创建关卡序列资产
脚本API可以在现有的序列上使用,但有时候最好创建一个序列资产,例如为关卡序列创建默认状态,以填充项目中所有序列的公共轨道,比如Camera Cut和摄像机。
要创建新的关卡序列资产,可以使用资产工具和关卡序列工厂新(Level Sequence Factory New)对象。工厂对象可以用从类构造对象(Construct Object From Class)节点来创建。然后调用获取资产工具(Get Asset Tools)和创建资产(Create Asset)。目前只支持在编辑器中创建新资产。
点击文末“阅读原文”,查看完整图表
添加绑定
对象绑定可以将序列和使用序列创建动画的Actor隔离开来。这可以让我们实现在运行时动态绑定,重复使用序列,和在播放期间优雅地处理某些绑定Actor无法使用的情况。
绑定可以通过引用关卡中的现有对象(可持有对象)添加,或者由序列拥有和生成(可生成对象)。要想进一步了解可生成对象与可持有对象的差别,请参阅本文档。虚幻引擎5.3新增了一项功能,即允许动态选择使用该绑定创建动画的对象。这样就可以基于自定义蓝图脚本,创建行为类似于两者的绑定。
需要注意的是,可生成对象仅在评估序列时生成。如果没有生成,脚本工作流程可能无法引用与它们关联的Actor。在将其组件添加到序列中时,也要注意这一点。稍后我们会详细介绍。
在脚本中,绑定以影片场景绑定代理结构(绑定代理)的形式表示。这些代理包含识别序列中的绑定所需的信息,通常由GUID(又称“绑定ID”)及其所在的序列组成。在大多数情况下,脚本API只关心代理本身,但有时也会使用绑定ID。
可持有对象
要在关卡序列中添加一个可持有对象,关卡中必须已经存在一个对象。要添加现有对象,可以调用“添加可拥有项(Add Possessable)”,引用该对象和添加该对象的目标序列。
所有组件,无论它们的父组件是可生成对象还是可持有对象,都将作为可持有对象添加到序列中,因为序列不直接控制它们从该绑定中生成。
可生成对象
可生成对象可以基于关卡中现有的对象或基于类添加到序列中。
基于现有对象创建可生成对象不会影响它所基于的对象,也不会使该对象产生动画,但要确保关卡中存在一个可用于其他目的的对象,例如添加组件。要基于现有对象添加一个可生成对象,可以调用“从实例添加可生成项(Add Spawnable from Instance)”,引用对象和序列,类似于添加可持有对象。
基于类创建可生成对象意味着对象不需要已经存在,具有更大的灵活性,还可以减少清理工作。要基于类添加一个可生成对象,可以调用“从类添加可生成项(Add Spawnable from Class)”,引用该类和序列。
点击文末“阅读原文”,查看完整图表
在这个例子中,Actor和它的组件需要在序列中添加轨道,从而在关卡中生成一个对象,然后从该实例添加可生成对象。之后,生成的Actor会被销毁,一旦不需要,就不会留在关卡中。
向现有绑定添加组件
如上所述,所有组件都将作为可持有对象添加,因为它们的父对象将处理如何生成它们而不是序列,即便这个序列在一开始生成了它们的父对象。
要添加组件,可以像前面一样调用“添加可拥有项(Add Possessable)”,引用序列和组件,然后调用“设置父项(Set Parent)”,组件的绑定代理为“在绑定中(In Binding)”,父对象的绑定代理为“在父绑定中(In Parent Binding)”。如果未调用“设置父项”,则该组件将独立存在于序列中,而不是嵌套在它的父对象中。
点击文末“阅读原文”,查看完整图表
添加轨道
在向绑定添加独立运行轨道和轨道时,调用“添加轨道(Add Track)”,区别在于目标对象。对于独立运行轨道来说,要在序列上调用,对于绑定来说,要在绑定上调用。
独立运行
独立运行轨道的一个常见示例是Camera Cut轨道。下面这个例子展示的是如何设置独立运行轨道,内容截取自上文提到的添加可生成对象示例。
这个示例中的轨道类型是影片场景摄像机剪切轨道(MovieSceneCameraCutTrack)。
向绑定添加轨道
添加到绑定中的大多数轨道都是属性轨道。下面这个例子展示的是如何为摄像机焦距添加一个浮点轨道,内容截取自初始化摄像机绑定示例。
这个例子中的轨道类型是影片场景浮点轨道(MovieSceneFloatTrack)。这是由属性的类型决定的,例如,任何可以由过场动画控制的浮点属性都可以通过影片场景浮点轨道添加到序列中。
设置属性名称和路径
如果不将属性轨道分配给要在对象上创建动画的属性,上面的例子就不完整。这是通过设置属性名称和路径(调用设置属性名称和路径)来完成的。属性名称就是在蓝图或C++类中定义的需要创建动画的属性。属性路径用于支持结构体中的嵌套属性,即使该属性不是结构体的一部分,也必需有这个路径。在焦距示例中,名称和路径相同。
例如,设置摄像机焦距的脚本需要提供包含对焦设置结构体的属性路径。在这种情况下,属性路径就是“FocusSettings.ManualFocusDistance”。
属性路径的基本格式是[结构体名称].[属性名称],然后根据需要为嵌套在结构体中的结构体添加额外的结构体名称。属性名称和属性路径的名称都是内部名称,不是显示名称。
内部名称可以通过右键菜单从Actor的细节面板中复制。在从Sequencer编辑器复制时,属性名称和路径也包含在轨道的文本表示中。此外,我们还可以通过脚本,在通过用户界面添加的任意影片场景属性轨道(MovieScenePropertyTrack)上调用“获取属性名称(Get Property Name)”和“获取属性路径(Get Property Path)”,获取属性名称和属性路径。
添加分段
分段定义动画的时间范围,保存与整个时间段相关的属性。例如,骨骼动画分段将定义动画范围,以及将播放动画效果的特定动画资产。要添加分段,可以在轨道上调用“添加分段(Add Section)”。通过脚本添加分段时,它的范围是0帧,因此必须调用“设置结束帧(Set End Frame)”来设置最后一帧,这样就可以定义一段有意义的持续时间。也可以调用“设置范围(Set Range)”或“设置起始帧(Set Start Frame)”和“设置结束帧(Set End Frame)”。除了以帧为单位,这些函数还有以秒为单位计时的其他版本。
添加关键帧
可添加关键帧的分段可能包含一个或多个通道。对于要在分段上创建动画的属性来说,每个通道通常只控制该属性的其中一个组成部分,该组成部分属于“普通旧数据”类型,例如整数、浮点和布尔值。像变换和颜色这样的复杂类型往往包含多个通道,与相关属性绑定的结构体的每个属性有它自己的轨道。只有在开发版本中才能访问分段的通道,在发布版本中无法添加和读取关键帧。
随着向大型世界坐标系迁移,变换轨道现在基于Double通道,而不是浮点通道。对于在蓝图中编辑脚本来说,浮点和Double的区别不像在C++中那么明显,但是脚本API与底层数据交互,类型差异在这里十分明显。在为大多数浮点属性创建动画时,通常使用浮点通道和浮点关键帧,但是对变换来说,通常使用Double通道和Double关键帧。下图展示了它们的区别,内容截取自上文将组件添加到现有绑定的示例。
与序列编辑器交互
许多工作流程都会与序列编辑器交互,包括关卡序列资产本身。虚幻引擎通过“关卡序列编辑器”蓝图库提供了这个API的一部分。在蓝图操作菜单中搜索关卡序列编辑器将会显示它提供的所有功能。下面列举了一些常见示例,可以帮助你了解可用的资源。
我们还提供了关卡序列编辑器子系统,可用于实现与序列编辑器的进一步交互。这个子系统可以用于烘焙变换、在可生成对象和可持有对象之间转换、复制和粘贴序列元素等等。
获取当前序列
如果脚本要用于通用关卡序列,那么在当前打开的关卡序列上操作通常是有用的,特别是在创建编辑器工具控件,协助完成序列编辑人员可能参与的任务时。下图展示了如何搜索带特定标记的绑定,并在当前序列中选中它们。
点击文末“阅读原文”,查看完整图表
打开序列
要更改序列或打开新建的序列,可以使用“打开关卡序列(Open Level Sequence)”节点。在这个例子中,我们打开了之前在这个编辑器工具控件中创建的序列,并在Camera Cut轨道视图中播放序列。
点击文末“阅读原文”,查看完整图表
修改序列的开始、结束和帧率
要修改序列的开始和结束,可以设置播放范围。脚本API具有以帧和以秒为单位设置播放开始和结束的功能:
-
设置播放开始
-
设置播放结束
-
设置播放开始秒数
-
设置播放结束秒数
要修改序列的帧率,可以设置显示率。显示率以分子分母形式保存,可以避免浮点精度错误,也可以显示用小数无法清晰表示的帧率,比如33⅓。要在设置显示率(Set Display Rate)中为输入“显示率(Display Rate)”提供帧率结构体,可以使用创建帧率(Make Frame Rate)节点。
点击文末“阅读原文”,查看完整图表
在可持有对象和可生成对象之间转换
通过使用关卡序列编辑器子系统,绑定可以在可生成对象和可持有对象之间来回转换。这个例子通过检查选择的所有绑定是否存在于当前序列的可持有对象数组中来确定它们当前的类型,然后将它们转换为另一种类型。
点击文末“阅读原文”,查看完整图表
通过FBX导入和导出Sequencer
导出FBX
FBX可以通过导出关卡序列FBX(Export Level Sequence FBX)节点从关卡序列中导出。该节点需要一个SequenceExportFBXParams(序列导出FBX参数)对象,该对象保存着函数调用所需的所有参数。这种惯例是为了避免在每次更新功能和需要新输入时反复弃用该函数。因为这是一个结构体,我们可以使用“创建”节点。但是这个结构体的重载选项(Override Options)要用从类构建对象(Construct Object from Class)节点来创建,因为它是一个UObject,而不是UStruct。
导出关卡序列FBX节点会创建一个序列播放器,评估输入SequenceExportFBXParams “序列”参数的关卡序列的第一帧。如果这是一个子序列,它会评估整个序列到子序列开始的地方,如果是根序列,它会评估第一帧。这和基于当前已播放帧导出的用户界面行为不同。
点击文末“阅读原文”,查看完整图表
导入FBX
导入过程与导出过程相似,只不过没有轨道和根序列选项。导入FBX设置类是影片场景用户导入FBX设置。“在绑定中(In Bindings)”中将输入一个空的数组,以导入所有绑定。
点击文末“阅读原文”,查看完整图表
有关控制绑定的注意事项:
FBX导入和导出管线是指在外部DCC工具(比如Maya)和虚幻引擎之间传输文件。要使用控制绑定,可以使用另一个脚本API。
获取绑定对象
绑定对象可以使用关卡序列编辑器脚本库的“获取绑定对象(Get Bound objects)”函数从打开的序列中获取,该函数需要一个影片场景绑定ID。如果未打开序列,可以使用Sequencer工具函数库的“获取绑定对象”,该函数需要一个世界、关卡序列、绑定代理数组和一个范围。这个函数会在指定的关卡中创建一个关卡序列播放器,在指定范围内评估序列,然后收集与输入的绑定代理绑定的对象。
点击文末“阅读原文”,查看完整图表
延伸阅读
1.编辑器的脚本与自动化:
https://docs.unrealengine.com/5.0/en-US/scripting-and-automating-the-unreal-editor/
2.Editor Utility Tasks:
https://dev.epicgames.com/community/learning/tutorials/0lxq/unreal-engine-editor-utility-tasks