Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
使用 AnimationTree¶
前言¶
通过 AnimationPlayer,Godot 拥有你在所有游戏引擎中能找到的最灵活的动画系统之一。几乎可以在任何节点或资源中对任何属性进行动画处理,以及专门的变换、贝塞尔、函数调用、音频和子动画轨道,这样的能力相当独特。
然而, 通过 AnimationPlayer
混合这些动画的支持相对有限, 只能设置固定的交叉渐变过渡时间.
AnimationTree 是Godot 3.1中引入的一个新节点, 用于处理更高级的变换, 它取代了旧的 AnimationTreePlayer
, 同时增加了大量的功能和灵活性.
创建动画树¶
首先, 必须明确 AnimationTree
节点不包含它自己的动画. 相反, 它使用包含在 AnimationPlayer
节点中的动画. 通过这种形式, 您可以像往常一样编辑动画(或从3D场景导入动画), 然后使用这个额外节点来控制播放.
在3D场景中经常使用 AnimationTree
. 当从3D交换格式导入场景时, 它们通常自带动画(要么是多个, 要么是在导入时从一个大的动画中拆分出来). 最后, 导入的Godot场景在 AnimationPlayer
节点中包含动画.
很少在Godot中直接使用导入的场景(它们要么实例化, 要么来自继承), 您可以将 AnimationTree
节点放置在包含导入的新场景中. 然后, 将 AnimationTree
节点指向导入场景内创建的 AnimationPlayer
节点.
这是在 第三人称射击游戏演示, 中的设置, 参考下图:
A new scene was created for the player with a CharacterBody3D
as root. Inside this scene, the original .dae
(Collada) file was instantiated
and an AnimationTree
node was created.
创建树¶
可以在 AnimationTree
中使用三种主要节点类型:
Animation nodes, which reference an animation from the linked
AnimationPlayer
.动画根节点, 用于混合子节点.
动画混合节点,在
AnimationNodeBlendTree
中使用,通过多个输入端口进行单图混合。
在 AnimationTree
中设置根节点, 如下几种类型可供选择:
AnimationNodeAnimation
:从列表中选择一个动画并播放它. 这是最简单的根节点, 一般不直接用作根节点.AnimationNodeBlendTree
:包含许多混合类型的节点,如调配, 混合2, 混合3, 一对一等. 这是最常用的根节点之一.AnimationNodeStateMachine
:将多个根节点作为图中的子节点. 每个节点作为一个 状态 使用, 并提供多个函数在状态之间进行切换.AnimationNodeBlendSpace2D
:允许在二维混合空间中放置根节点. 在二维中控制混合位置以混合多个动画.AnimationNodeBlendSpace1D
:以上的简化版本(一维)。
混合树¶
AnimationNodeBlendTree
可包含用于混合的根节点和常规节点。节点从菜单添加到图中:
所有混合树默认都包含一个 Output
(输出)节点,为了让动画播放,必须有个东西与其相连。
测试此功能最简单的方法是直接连接一个 Animation
(动画)节点:
这会简单地回放动画. 确保 AnimationTree
节点对实际发生的事情是激活的.
以下是可用节点的简短描述:
混合2/混合3¶
这些节点将通过用户指定输入的两个或三个混合值之间进行混合:
对于更复杂的混合, 建议使用混合空间.
混合也可以使用过滤器, 也就是说, 您可以单独控制通过混合功能的轨道. 这对于动画的层叠非常有用.
OneShot¶
此节点将执行子动画, 并在完成后返回. 可以用于定制淡入淡出时间, 以及过滤器.
After setting the request and changing the animation playback, the one-shot node automatically clears the request on the next process frame by setting its request
value to AnimationNodeOneShot.ONE_SHOT_REQUEST_NONE
.
# Play child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE)
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_FIRE
# Abort child animation connected to "shot" port.
animation_tree.set("parameters/OneShot/request", AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT)
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/request"] = AnimationNodeOneShot.ONE_SHOT_REQUEST_ABORT
# Get current state (read-only).
animation_tree.get("parameters/OneShot/active"))
# Alternative syntax (same result as above).
animation_tree["parameters/OneShot/active"]
// Play child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Fire);
// Abort child animation connected to "shot" port.
animationTree.Set("parameters/OneShot/request", (int)AnimationNodeOneShot.OneShotRequest.Abort);
// Get current state (read-only).
animationTree.Get("parameters/OneShot/active");
TimeSeek¶
这个节点可以用来使寻找命令发生在动画图像的任何子代上。使用这个节点类型可以从 AnimationNodeBlendTree
中的开始或某个位置播放 Animation
。
After setting the time and changing the animation playback, the seek node automatically goes into sleep mode on the next process frame by setting its seek_request
value to -1.0
.
# Play child animation from the start.
animation_tree.set("parameters/TimeSeek/seek_request", 0.0)
# Alternative syntax (same result as above).
animation_tree["parameters/TimeSeek/seek_request"] = 0.0
# Play child animation from 12 second timestamp.
animation_tree.set("parameters/TimeSeek/seek_request", 12.0)
# Alternative syntax (same result as above).
animation_tree["parameters/TimeSeek/seek_request"] = 12.0
// Play child animation from the start.
animationTree.Set("parameters/TimeSeek/seek_request", 0.0);
// Play child animation from 12 second timestamp.
animationTree.Set("parameters/TimeSeek/seek_request", 12.0);
时间缩放¶
Allows scaling the speed of the animation (or reverse it) connected to the in input via the scale parameter. Setting the scale to 0 will pause the animation.
转换¶
Very simple state machine (when you don't want to cope with a StateMachine
node). Animations can be connected to the outputs and transition times can be specified.
After setting the request and changing the animation playback, the transition node automatically clears the request on the next process frame by setting its transition_request
value to an empty string (""
).
# Play child animation connected to "state_2" port.
animation_tree.set("parameters/Transition/transition_request", "state_2")
# Alternative syntax (same result as above).
animation_tree["parameters/Transition/transition_request"] = "state_2"
# Get current state name (read-only).
animation_tree.get("parameters/Transition/current_state")
# Alternative syntax (same result as above).
animation_tree["parameters/Transition/current_state"]
# Get current state index (read-only).
animation_tree.get("parameters/Transition/current_index"))
# Alternative syntax (same result as above).
animation_tree["parameters/Transition/current_index"]
// Play child animation connected to "state_2" port.
animationTree.Set("parameters/Transition/transition_request", "state_2");
// Get current state name (read-only).
animationTree.Get("parameters/Transition/current_state");
// Get current state index (read-only).
animationTree.Get("parameters/Transition/current_index");
二维混合空间¶
BlendSpace2D
是一个在二维空间进行高级混合的节点. 将点添加到一个二维空间, 然后可以控制位置来确定混合:
The ranges in X and Y can be controlled (and labeled for convenience). By default, points can be placed anywhere (right-click on the coordinate system or use the add point button) and triangles will be generated automatically using Delaunay.
也可以通过禁用 自动三角形 选项来手动绘制三角形, 虽然基本上没必要这么做:
最后, 可能会更改混合模式. 默认情况下, 混合是通过在最近的三角形内插点来实现的. 当处理二维动画(逐帧)时, 您可能希望切换到 离散 模式. 此外, 如果您想在离散动画之间切换时保持当前播放位置, 请使用 进位 模式. 此模式可在 混合 菜单中更改:
一维混合空间¶
这类似于二维混合空间, 但在一维空间中(所以不需要三角形).
状态机¶
这个节点是一个状态机,根节点都是状态。根节点可以创建并通过线路连接。状态通过转换连接,它们是具有特殊性质的连接。转换是单向的,但是可以用两个来达到双向连接。
有多种类型的转换:
Immediate(立即):将立即切换到下一个状态。当前状态将结束,并与新状态的开头相混合。
Sync(同步):立即切换到下一个状态,但会将新状态快进并到旧状态的播放位置。
At End(末尾):将等待当前状态播放结束,然后切换到下一个状态动画的开头。
过渡也有一些属性。单击任何过渡,它就会显示在“检查器”面板中:
Switch Mode(切换模式)为过渡类型(见上文),可以在此处创建后修改。
Auto Advance(自动前进)当达到此状态时将自动开启转换。最适合 At End 切换模式。
Advance Condition(前进条件)会在条件成立时打开自动前进。这是一个可以用变量名填充的自定义文本字段。可以从代码中修改变量(稍后将对此进行详细介绍)。
Xfade Time(叠化时间)是在这个状态和下一个状态之间交叉渐变的时间。
Priority(优先级)与代码中的
travel()
函数一起使用(后述)。当从一个状态到另一个状态时,会优先使用优先级较低的过渡。Disabled(禁用)允许禁用此转换(它不会在行程或自动前进期间使用)。
For better blending¶
In Godot 4.0+, in order for the blending results to be deterministic (reproducible and always consistent), the blended property values must have a specific initial value. For example, in the case of two animations to be blended, if one animation has a property track and the other does not, the blended animation is calculated as if the latter animation had a property track with the initial value.
When using Position/Rotation/Scale 3D tracks for Skeleton3D bones, the initial value is Bone Rest.
For other properties, the initial value is 0
and if the track is present in the RESET
animation,
the value of its first keyframe is used instead.
For example, the following AnimationPlayer has two animations, but one of them lacks a Property track for Position.
This means that the animation lacking that will treat those Positions as Vector2(0, 0)
.
This problem can be solved by adding a Property track for Position as an initial value to the RESET
animation.
备注
Be aware that the RESET
animation exists to define the default pose when loading an object originally.
It is assumed to have only one frame and is not expected to be played back using the timeline.
Also keep in mind that the Rotation 3D tracks and the Property tracks for 2D rotation with Interpolation Type set to Linear Angle or Cubic Angle will prevent rotation of more than 180 degrees from the initial value as blended animation.
This can be useful for Skeleton3Ds to prevent the bones penetrating the body when blending animations. Therefore, Skeleton3D's Bone Rest values should be as close to the midpoint of the movable range as possible. This means that for humanoid models, it is preferable to import them in a T-pose.
You can see that the shortest rotation path from Bone Rests is prioritized rather than the shortest rotation path between animations.
If you need to rotate Skeleton3D itself more than 180 degrees by blend animations for movement, you can use Root Motion.
根骨骼运动¶
处理 3D 动画时,一种流行的技术是动画师利用根骨骼为其余部分骨骼制作运动动画。这使得动画角色的脚步与下面的地板相匹配。并且允许在电影拍摄期间与物体进行精确的交互。
在 Godot 中回放动画时,可以选择这根骨骼作为根运动轨迹。这会在视觉上取消这根骨骼的变换(动画将保持原状)。
然后, 实际运动可以通过 AnimationTree API 作为转换:
# Get the motion delta.
animation_tree.get_root_motion_position()
animation_tree.get_root_motion_rotation()
animation_tree.get_root_motion_scale()
# Get the actual blended value of the animation.
animation_tree.get_root_motion_position_accumulator()
animation_tree.get_root_motion_rotation_accumulator()
animation_tree.get_root_motion_scale_accumulator()
// Get the motion delta.
animationTree.GetRootMotionPosition();
animationTree.GetRootMotionRotation();
animationTree.GetRootMotionScale();
// Get the actual blended value of the animation.
animationTree.GetRootMotionPositionAccumulator();
animationTree.GetRootMotionRotationAccumulator();
animationTree.GetRootMotionScaleAccumulator();
This can be fed to functions such as CharacterBody3D.move_and_slide to control the character movement.
还有一个名为 RootMotionView
的工具节点,可以放置在场景中,作为你的角色和动画的自定义地板(这个节点默认在游戏期间禁用)。
使用代码控制¶
创建树和预览之后,只剩下一个问题:“如何使用代码控制所有的节点?”。
要注意动画节点就是资源,因此他们会在所有使用他们的实例之间共享。直接修改节点中的值,将会影响到场景中所有使用这个 AnimationTree
的实例。通常是不希望这样的,不过也有一些不错的用法,比如你可以复制粘贴你的动画树的一部分,或者在不同的动画树中复用具有复杂布局的节点(例如状态机和混合树)。
实际的动画数据包含在 AnimationTree
节点中, 并通过属性访问. 检查 AnimationTree
节点的 "参数" 部分, 查看所有可以实时修改的参数:
这很方便, 因为它可以通过 AnimationPlayer
获得动画效果, 甚至是 AnimationTree
本身, 允许实现非常复杂的动画逻辑.
想要通过代码修改这些值, 必须获得该属性的路径. 这是很容易做到的, 把鼠标悬停在任何参数:
允许设置或读取它们:
animation_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Simpler alternative form:
animation_tree["parameters/eye_blend/blend_amount"] = 1.0
animationTree.Set("parameters/eye_blend/blend_amount", 1.0);
状态机行程¶
One of the nice features in Godot's StateMachine
implementation is the ability to travel. The graph can be instructed to go from the
current state to another one, while visiting all the intermediate ones. This is done via the A* algorithm.
If there is no path of transitions starting at the current state and finishing at the destination state, the graph teleports to the destination state.
要使用行程能力, 您应该首先从 AnimationTree
节点中检索 AnimationNodeStateMachinePlayback 对象(其被导出为一个属性).
var state_machine = animation_tree["parameters/playback"]
AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animationTree.Get("parameters/playback");
一旦检索到, 可以调用它提供的许多函数之一:
state_machine.travel("SomeState")
stateMachine.Travel("SomeState");
状态机必须正在运行才能使用行程能力。确保调用 start()
或选择一个节点以在加载时自动播放。