使用动画树AnimationTree

简介

通过 AnimationPlayer,Godot拥有你在所有游戏引擎中能找到的最灵活的动画系统之一. 可以在任何节点或资源中对任何属性进行动画处理, 以及专门的变换, 贝塞尔, 函数调用, 音频和子动画轨道, 这样的能力相当独特.

然而, 通过 AnimationPlayer 混合这些动画的支持相对有限, 只能设置固定的交叉渐变过渡时间.

AnimationTree 是Godot 3.1中引入的一个新节点, 用于处理更高级的变换, 它取代了旧的 AnimationTreePlayer , 同时增加了大量的功能和灵活性.

创建动画树

首先, 必须明确 AnimationTree 节点不包含它自己的动画. 相反, 它使用包含在 AnimationPlayer 节点中的动画. 通过这种形式, 您可以像往常一样编辑动画(或从3D场景导入动画), 然后使用这个额外节点来控制播放.

在3D场景中经常使用 AnimationTree . 当从3D交换格式导入场景时, 它们通常自带动画(要么是多个, 要么是在导入时从一个大的动画中拆分出来). 最后, 导入的Godot场景在 AnimationPlayer 节点中包含动画.

很少在Godot中直接使用导入的场景(它们要么实例化, 要么来自继承), 您可以将 AnimationTree 节点放置在包含导入的新场景中. 然后, 将 AnimationTree 节点指向导入场景内创建的 AnimationPlayer 节点.

这是在 第三人称射击游戏演示, 中的设置, 参考下图:

../../_images/animtree1.png

为玩家创建了一个以 KinematicBody 为根节点的新场景. 在这个场景中, 已实例化原来的 .dae (Collada)文件, 并创建 AnimationTree 节点.

创建树

可以在 AnimationTree 中使用三种主要节点类型:

  1. 动画节点, 从链接的 AnimationTree 中引用动画.

  2. 动画根节点, 用于混合子节点.

  3. 动画混合节点, 它们在 "动画节点混合树" 中使用, 通过多个输入端口作为单图混合.

AnimationTree 中设置根节点, 如下几种类型可供选择:

../../_images/animtree2.png
  • "动画节点动画": 从列表中选择一个动画并播放它. 这是最简单的根节点, 一般不直接用作根节点.

  • "动画节点混合树": 包含许多 混合 类型的节点, 如调配, 混合2, 混合3, 一对一等. 这是最常用的根节点之一.

  • "动画节点状态机": 将多个根节点作为图中的子节点. 每个节点作为一个 状态 使用, 并提供多个函数在状态之间进行切换.

  • "动画节点二维混合空间": 允许在二维混合空间中放置根节点. 在二维中控制混合位置以混合多个动画.

  • "动画节点一维混合空间": 以上的简化版本(一维).

混合树

一个 "动画节点混合树" 可包含用于混合的根节点和常规节点. 节点从菜单添加到图中:

../../_images/animtree3.png

所有混合树默认包含一个 "输出" 节点, 为了让动画播放, 必须有个东西与其相连.

测试此功能最简单的方法是直接连接一个 "动画" 节点:

../../_images/animtree4.png

这会简单地回放动画. 确保 AnimationTree 节点对实际发生的事情是激活的.

以下是可用节点的简短描述:

混合2/混合3

这些节点将通过用户指定输入的两个或三个混合值之间进行混合:

../../_images/animtree5.gif

对于更复杂的混合, 建议使用混合空间.

混合也可以使用过滤器, 也就是说, 您可以单独控制通过混合功能的轨道. 这对于动画的层叠非常有用.

../../_images/animtree6.png

OneShot

此节点将执行子动画, 并在完成后返回. 可以用于定制淡入淡出时间, 以及过滤器.

../../_images/animtree6b.gif

查找

这个节点可以用来使寻找命令发生在动画图像的任何子代上。使用这个节点类型可以从 AnimationNodeBlendTree 中的开始或某个位置播放 Animation

在设置时间和改变动画播放后,寻找节点通过设置其 seek_position 值为 -1.0 ,在下一个进程帧自动进入睡眠模式。

# Play child animation from the start.
anim_tree.set("parameters/Seek/seek_position", 0.0)
# Alternative syntax (same result as above).
anim_tree["parameters/Seek/seek_position"] = 0.0

# Play child animation from 12 second timestamp.
anim_tree.set("parameters/Seek/seek_position", 12.0)
# Alternative syntax (same result as above).
anim_tree["parameters/Seek/seek_position"] = 12.0
// Play child animation from the start.
animTree.Set("parameters/Seek/seek_position", 0.0);

// Play child animation from 12 second timestamp.
animTree.Set("parameters/Seek/seek_position", 12.0);

时间缩放

允许缩放任何子节点的动画速度(或将其反转). 设置为0会暂停动画.

转换

非常简单的状态机(当您不想使用 "状态机" 节点时). 动画可以连接到输出, 并且可以指定转换时间.

二维混合空间

BlendSpace2D 是一个在二维空间进行高级混合的节点. 将点添加到一个二维空间, 然后可以控制位置来确定混合:

../../_images/animtree7.gif

可以控制X和Y的范围(为方便起见, 还可以标记它们). 默认情况下, 可以在任何位置放置点(只需右键单击坐标系统或使用 添加点 按钮)将自动生成德洛内三角形.

../../_images/animtree8.gif

也可以通过禁用 自动三角形 选项来手动绘制三角形, 虽然基本上没必要这么做:

../../_images/animtree9.png

最后, 可能会更改混合模式. 默认情况下, 混合是通过在最近的三角形内插点来实现的. 当处理二维动画(逐帧)时, 您可能希望切换到 离散 模式. 此外, 如果您想在离散动画之间切换时保持当前播放位置, 请使用 进位 模式. 此模式可在 混合 菜单中更改:

../../_images/animtree10.png

一维混合空间

这类似于二维混合空间, 但在一维空间中(所以不需要三角形).

状态机

这个节点是个相对简单的状态机. 根节点可以创建并通过线路连接. 状态通过 转换 连接, 它们是具有特殊性质的连接. 转换是单向的, 但是可以用两种方式连接.

../../_images/animtree11.gif

有多种类型的转换:

../../_images/animtree12.png
  • Immediate: 将立即切换到下一个状态. 当前状态将结束, 并融合到新状态的开始.

  • 同步: 立即切换到下一个状态, 但会将新状态搜索并放置到旧状态的回放位置.

  • 末尾: 将等待当前状态回放结束, 然后切换到下一个状态的开始动画.

转换也有一些属性. 点击任何转换, 它会显示在属性面板中:

../../_images/animtree13.png
  • 切换模式 为过渡类型(见上文), 可以在此处创建后修改.

  • 自动前进 当达到此状态时将自动开启转换. 最适合 "末尾" 切换模式.

  • 前进条件 设置此条件后, 将打开自动前进. 这是一个可以用变量名填充的自定义文本字段. 可以从代码中修改变量(稍后将对此进行详细介绍).

  • X消隐时间 是在这个状态和下一个状态之间交替渐变的时间.

  • 优先级 与代码中的 travel() 函数一起使用(稍后再谈). 当从一个状态到另一个状态时, 当给这个节点更多的优先权.

  • 禁用 允许禁用此转换(它不会在行程或自动前进期间使用).

根骨骼运动

当处理3D动画时, 一种流行的技术是动画师利用根骨骼为其余部分骨骼制作运动动画. 这使得动画角色的脚步与下面的地板相匹配. 并且允许在电影拍摄期间与物体进行精确的交互.

在Godot中回放动画时, 可以选择这根单根骨作为 根运动轨迹. 这会在视觉上取消单根骨的转换(动画将保持原状).

../../_images/animtree14.png

然后, 实际运动可以通过 :ref:`AnimationTree <class_AnimationTree>`API作为转换:

anim_tree.get_root_motion_transform()
animTree.GetRootMotionTransform();

可以提供给诸如: 参考:动力学物体. 移动和滑动 <类_动力学物体_方法_移动和滑动> 这样的函数来控制角色移动.

还有一个名为 "根骨骼运动视图" 的工具节点, 可以放置在场景中, 并作为你的角色和动画的自定义地板(此节点通常在游戏期间禁用).

../../_images/animtree15.gif

使用代码控制

创建树和预览之后, 只剩下一个问题:"如何使用代码控制所有的节点?".

要注意动画节点就是资源, 并且因此他们在所有实例之间共享. 直接修改节点中的值, 将会影响到场景中所有使用这个 AnimationTree 的实例. 不过这也有一些不错的用法, 比如你可以复制粘贴你的动画树的一部分, 或者在不同的动画树中复用具有复杂布局的节点(例如状态机和混合树).

实际的动画数据包含在 AnimationTree 节点中, 并通过属性访问. 检查 AnimationTree 节点的 "参数" 部分, 查看所有可以实时修改的参数:

../../_images/animtree16.png

这很方便, 因为它可以通过 AnimationPlayer 获得动画效果, 甚至是 AnimationTree 本身, 允许实现非常复杂的动画逻辑.

想要通过代码修改这些值, 必须获得该属性的路径. 这是很容易做到的, 把鼠标悬停在任何参数:

../../_images/animtree17.png

允许设置或读取它们:

anim_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Simpler alternative form:
anim_tree["parameters/eye_blend/blend_amount"] = 1.0
animTree.Set("parameters/eye_blend/blend_amount", 1.0);

状态机行程

执行 "状态机" 的行程能力, 是Godot引擎的优秀特性之一. 可以指示图表从当前状态转到另一个状态, 我们可以访问其所有的中间状态. 这是通过A*算法实现的.

要使用行程能力, 您应该首先从 AnimationTree 节点中检索 AnimationNodeStateMachinePlayback 对象(其被导出为一个属性).

var state_machine = anim_tree["parameters/playback"]
AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/playback");

一旦检索到, 可以调用它提供的许多函数之一:

state_machine.travel("SomeState")
stateMachine.Travel("SomeState");

状态机必须正在运行才能使用行程能力. 确保调用 start() 或选择一个节点以 在加载时自动播放 .

../../_images/animtree18.png