动画图编辑器是一个视觉化的基于节点的工具,使技术艺术家和动画师能够直接在Roblox Studio中构建复杂的动画逻辑。通过提供创建混合树等行为的简化界面,它消除了对角色运动手动脚本编写的传统依赖。
此系统与您现有的动画工作流程协同工作:
- 动画编辑器:继续将动画编辑器作为您创作单个剪辑和微调关键帧和曲线的主要工具。
- 动画图编辑器:使用这个新的工作区,将这些剪辑组织成一个逻辑树,以驱动复杂的游戏行为。
旨在增强协作性,视觉图表使开发者能够快速检查、调试和理解动画师创建的逻辑。当艺术家专注于完善交互运动时,开发者仍然可以以编程方式访问动画图节点,以便直接控制混合动画和状态。
##构建图
要开始构建可动角色的逻辑,请通过Studio功能区的头像选项卡访问动画图编辑器。以下步骤演示了如何初始化一个模型并使用默认的行走和挥手动画构建基本节点网络。
要深入了解实际应用,您可以探索动画图参考文件,其中包含基础和复杂的实现示例。
要创建您自己的动画图,类似于参考中提供的基本示例,请使用以下步骤:
在Studio中,通过导航到头像选项卡并选择角色 ⟩ 我的头像添加一个可动模型。

通过导航到头像选项卡中的图形编辑器打开动画图编辑器。

在3D视口中选择可动模型,然后选择创建图形。
在图形编辑器中,右键单击并选择剪辑。

在新剪辑节点中,设置动画ID。
选择动画ID下拉菜单。
要提交特定的动画资源ID,请单击导入。

在动画ID字段中,添加默认的行走动画:507777826。
选择导入。
通过重复步骤4-5使用默认挥手动画507770239添加另一个剪辑节点。

在图形编辑器中,右键单击并选择添加。

将剪辑节点连接到添加节点,通过将右上角的输出连接器拖到适当的端口:
- 将带有行走动画的剪辑节点连接到基本端口。
- 将带有挥手动画的剪辑节点连接到附加端口。

从添加节点,连接右上角的输出连接器到图形输出姿态端口。

**(可选)**为您的速度变量分配一个参数。
单击并拖动绿色速度端口到空白区域。一个新的参数节点会显示。

在图形编辑器的左上角,使用参数窗格快速修改节点中的参数。您还可以以编程方式访问。

通过单击播放按钮测试动画。

尝试测试不同的权重、速度、播放模式和其他动画。有关单独节点的更多信息,请参见节点参考。
API集成
创建和部署动画图遵循标准的Roblox动画管道。在动画图编辑器中选择一个模型后,会创建一个新的AnimationGraphDefinition资产。该资产作为您的节点、连接和参数的容器。完成逻辑后,您发布图形以获得标准资产ID。
在您的脚本中,您可以像操作传统动画那样,将这些图形加载到Animator上。要驱动图形的内部逻辑,请使用AnimationTrack:SetParameter()将实时值(例如运动速度或状态布尔值)直接传入图形的变量中。
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://123456789" -- 您发布的图形ID
local animationTrack = animator:LoadAnimation(animation)
animationTrack:Play()
-- 通过RunService动态参数更新
game:GetService("RunService").Stepped:Connect(function(_, dt)
local currentSpeed = humanoidRootPart.AssemblyLinearVelocity.Magnitude
animationTrack:SetParameter("humanoidSpeed", currentSpeed)
end)
节点参考
每个动画图节点充当逻辑门或数据源,处理在到达角色模型之前的动画数据。本节提供动画图编辑器内功能模块的技术细分。当前所有节点均输出动画姿态。
每个节点部分包括:
- 定义 – 节点的目的和在图中的角色摘要。
- 输入 – 进入节点的数据流。多个输入表示为Input1、Input2、...、InputN。
- 输入属性 – 直接与特定输入相关的设置(例如,将Idle输入分配给Blend1D节点的0.5位置)。
- 事件数据 – 由节点发出或消费的事件,以触发内部图逻辑或外部Luau脚本。此行为可能会在测试开发过程中发生变化。
全局事件规则
对于所有节点和过渡,默认情况下适用以下规则:
- 事件从源节点向上传播通过图形。每个事件带有一个权重,表示其源在最终混合中的影响。如果权重在任何时候达到零,则事件被静音。
- 没有自定义事件逻辑的节点将所有事件无变更地传递;进行混合或在输入之间选择的节点可以缩放权重或阻止次要输入的事件(请参见每节点事件部分)。
- 在图表顶部达到的标记事件可以通过AnimationTrack:GetMarkerReachedSignal()进行观察。
剪辑

对AnimationClip资产的引用。它作为图中的叶子节点,生成原始动画数据,以便为混合、选择或修改提供输入。
- 输入
- 无
- 属性
属性 类型 描述 剪辑 资产ID 要播放的动画资产(例如rbxassetid://12345)。 播放模式 Enum.AnimationNodePlayMode 定义剪辑在达到持续时间结束后行为。
- 循环(默认):一旦剪辑完成,将自动从头开始重新启动。
- 回弹:从开始到结束播放,然后立即从结束到开始反向播放。
- 一次并保持:播放一次,并在完成时保持最终姿态。
- 一次并重置:播放一次,并在完成时回到初始启动姿态。
反向 布尔值 控制播放方向。 速度 数字 播放速率的乘法因子。0.0暂停图形,1.0为正常速度,2.0为双倍速。 修剪 布尔值 切换剪辑的持续时间是否应被截断。 修剪开始 数字 播放应开始的绝对时间戳(以秒为单位)。 修剪结束 数字 播放应终止的绝对时间戳(以秒为单位)。 - 事件数据
- 事件处理: 无。这是一个没有子节点的叶子节点。
- 事件发射: 读取嵌入在动画片段中的自定义标记(例如“脚步声”、“武器挥动”)并在它们出现的准确帧发射为命名事件。如果剪辑被修剪,则仅发射在定义的[TrimStart, TrimEnd]区间内(包括)内的标记。
选择

通过选择属性选择任意数量的输入。每当当前选择更改时,它会触发新的过渡。
- 输入
- 输入1...输入N
- 属性
属性 类型 描述 选择 字符串 要选择的输入的唯一ID,匹配输入连接的名称(例如“行走”)。 - 事件数据
- 事件处理: 来自当前选择输入的事件通过其权重不变地传递。
- 事件发射: 仅通过来自当前选择输入的所有事件。在过渡期间,事件发射遵循全局事件规则。
优先选择

从上到下评估连接输入的列表,并播放条件评估为真的第一个输入。这允许基于特定逻辑进行层次动画选择。每当当前选择更改时,它会触发新的过渡。
- 输入
- 输入1...输入N
- 触发(布尔值):必须为真的逻辑条件才能激活此输入。在当前版本中,连接到布尔参数。
- 可中断(Enum.AnimationNodeInterruptible):定义何时可以被更高优先级输入中断的规则。
- 始终(默认):该输入可以随时被更高优先级条件中断。
- 完成:当前动画必须完成播放后,才能由更高优先级的输入接管。
- 触发:该输入仅在InterruptibleTrigger设置为真时可以被中断。
- InterruptibleTrigger(布尔值):仅在可中断设置为触发时可用。当此特定表达式为真时,输入可以被中断。
- 属性
属性 类型 描述 默认可中断 Enum.AnimationNodeInterruptible 应用于所有输入的基线中断规则。每个输入可以使用自己的可中断输入属性覆盖此规则。 默认中断触发 布尔值 当输入解决的可中断模式为触发时使用的基线触发值。每个输入可以使用自己的InterruptibleTrigger输入属性覆盖此规则。 - 事件数据
- 事件处理: 来自当前选择输入的事件通过其权重不变地传递。
- 事件发射: 仅通过来自当前选择输入的所有事件。在过渡期间,事件发射遵循全局事件规则。
顺序

根据定义的等待条件以特定顺序激活连接输入。每当当前选择更改时,它会触发新的过渡。
- 输入
- 输入1...输入N
- 等待(Enum.AnimationNodeWaitFor):指定必须满足的条件,以便顺序推进到下一个输入。
- 完成(默认):在当前输入完成一个循环后,向下推进到下一个输入。推进行为取决于连接的输入:
- 播放一次的剪辑:当剪辑结束时推进。
- 循环剪辑:当完成一个完整循环后推进。
- 具有无限循环的序列:在所有输入之间完成一个完整周期后推进。
- 具有无限循环的序列:在所有循环完成后推进。
- 触发:当自定义逻辑表达式评估为真时激活下一个输入。
- 等待触发(布尔值):仅在WaitFor设置为触发时可用。
- 属性
属性 类型 描述 循环次数 数字 循环穿越整个序列的次数(默认值为1)。值为0表示无限循环。一旦达到计数,节点将尊重最终输入的循环或保持设置。 默认等待 Enum.AnimationNodeWaitFor 应用于所有输入的基线等待条件。每个输入可以使用自己的等待输入属性覆盖此规则。 - 事件数据
- 事件处理: 来自当前活动输入的事件通过其权重不变地传递。
- 事件发射: 仅通过来自序列中当前活动输入的所有事件。在过渡期间,事件发射遵循全局事件规则。
随机序列

随机选择并播放其连接的输入之一。当当前选择的动画完成时,该节点会随机选择另一个输入进行播放。为每个输入分配特定的权重以影响被选择的概率。每当当前选择更改时,它会触发新的过渡。
- 输入
- 输入1...输入N
- 权重(数字):确定此输入被选择的概率;较高的权重增加选择的机会。
- 属性
属性 类型 描述 播放次数 数字 节点在停止之前将播放的输入次数。一旦达到,节点将尊重最后一个输入的循环或保持设置。默认值为0,表示无限播放。 种子 数字 一个用于初始化随机数生成器(RNG)的值,确保序列在不同客户端之间保持一致。默认值为-1,表示随机种子。 - 事件数据
- 事件处理: 来自当前活动输入的事件通过其权重不变地传递。
- 事件发射: 仅通过来自序列中当前活动输入的所有事件。在过渡期间,事件发射遵循全局事件规则。
叠加

将叠加姿态叠加到基本姿态之上。当与蒙版节点结合使用时,叠加姿态中被蒙版掉的关节完全暴露出基本姿态,从而创建透明叠加效果。
- 输入
- 基础:背景或底层,通常是全身动画,例如运动或静止状态。
- 叠加:要应用到基本上的前景或顶层,例如手势或工具使用动画。
- 属性
属性 类型 描述 权重 数字 用于衰减叠加姿态的混合权重。默认值为1.0(完全覆盖),且不受限制。 - 事件数据
- 事件处理:
- 节点监听来自基础和叠加输入的事件。
- 事件发射:
- 基础事件:来自基础输入的所有事件都不中断地传递。
- 叠加事件:来自叠加输入的事件按权重属性进行缩放。
- 在权重为0.5时,叠加事件以半权重传播
- 在权重为0时,它们被静音。
添加

将附加姿态添加到基本姿态,按特定权重衰减(不受限制)。
- 输入
- 基础:主要动画姿态。
- 附加:将叠加到基础上的姿态。
- 属性
属性 类型 描述 权重 数字 确定施加在基础上的附加姿态的强度。 - 事件数据
- 事件处理: 节点监听来自基础和附加输入的事件。
- 事件发射: 来自基础和附加输入的所有事件都不中断地传递。
减去

通过从目标姿态中减去相对基础姿态将动画转换为附加姿态()。结果按特定权重衰减(不受限制)。
- 输入
- A:目标动画姿态。
- B:被减去的相对基础姿态。
- 属性
属性 类型 描述 权重 数字 确定施加于结果的减法效果强度。 - 事件数据
- 事件处理: 节点监听来自输入A和输入B的事件。
- 事件发射: 来自输入A和输入B的所有事件都不中断地传递。
混合1D

在单一轴上线性插值,介于两个距离当前输入位置最近的动画姿态之间。
- 输入
- 输入1...输入N
- 位置(数字):每个后续输入在混合轴上的特定坐标。
- 属性
属性 类型 描述 位置 数字 用于采样动画的混合轴上的当前激活值。如果位置超出了定义的输入位置范围,节点会使用两个最近的输入进行外推。 相位同步 Enum.AnimationNodePhaseSync 配置子输入的时序是否应同步。
- 同步(默认):归一化同步。节点根据活动输入的加权平均值计算“虚拟持续时间”。每个输入节点的时间步长会被调整,以便所有子节点收敛到同一相位,从而同步不同长度的动画。
- 未同步:标准混合,剪辑独立以各自的播放速率向前推进。
- 事件数据
- 事件处理: 节点监听来自所有当前激活的子节点的事件。
- 事件发射: 仅来自最高权重的活动输入的事件传播,按其混合权重缩放。来自次要输入的事件被静音。
混合2D

根据二维坐标空间内的两个输入参数混合多个动画姿态。这使混合1D节点能够处理更复杂的场景,例如同时根据运动方向和速度混合。
- 输入
- 输入1...输入N
- X(数字):每个后续输入的X坐标。
- Y(数字):每个后续输入的Y坐标。
- 属性
属性 类型 描述 输入模式 Enum.AnimationNodeBlend2DInputMode 定义用于评估混合空间的坐标系统:
- 笛卡尔(默认):使用标准二维网格坐标。X和Y表示在混合空间内的当前位置信息。
- 极坐标:使用角度和大小值。X表示弧度方向,而Y表示运动的大小或强度。方向的权重比大小更重,因此处于相似角度但大小不同的输入比处于不同角度的输入混合得更平滑。
X 数字 当前X坐标(笛卡尔)或方向(弧度)(极坐标)。 Y 数字 当前Y坐标(笛卡尔)或大小(极坐标)。 相位同步 Enum.AnimationNodePhaseSync 配置子输入的时序是否应同步。
- 同步(默认):归一化同步。节点根据活动输入的加权平均值计算“虚拟持续时间”。每个输入节点的时间步长会被调整,以便所有子节点收敛到同一相位,从而同步不同长度的动画。
- 未同步:标准混合,剪辑独立以各自的播放速率向前推进。
- 事件数据
- 事件处理: 节点监听来自所有当前激活的子节点的事件。
- 事件发射: 仅来自最高权重的活动输入的事件传播,按其混合权重缩放。来自次要输入的事件被静音。
蒙版

应用预定义的蒙版到输入姿态。蒙版由Rig层次结构中每个对象(例如关节)的权重定义,使动画的精确控制或“羽化”成为可能。
- 输入
- 姿势:要进行蒙版的动画姿态。
- 属性
属性 类型 描述 蒙版 ObjectValue 一个ObjectValue(作为蒙版AnimationNodeDefinition的直接子项创建)定义了蒙版权重。它可以以两种方式运行:
- 直接:ObjectValue本身包含将Rig对象名称映射到特定权重值的属性。
创建蒙版时,您可以选择一个Rig模式(HumanoidRigDescription或Picker)来填充层次结构。
- HumanoidRigDescription:对人形角色进行标准化蒙版。
- Picker:允许用户在工作区中选择任何特定的Rig以填充蒙版层次结构。
反向 布尔值 当为真时,将权重值应用为1 - 权重。这允许反向重复使用蒙版,而无需创建新资产。 - 事件数据
- 事件处理: 节点监听来自输入姿势的所有事件。
- 事件发射: 输入姿势的所有事件都不中断地传递。
速度

修改传入动画姿态的播放速率。
- 输入
- 姿态:将修改播放速度的动画姿态或子图。
- 属性
属性 类型 描述 速度 数字 施加于时间增量()的乘法因子。0.0暂停图形,1.0为正常速度,2.0为双倍速度。 - 事件数据
- 事件处理: 节点监听来自输入姿势的事件。
- 事件发射: 输入姿势的所有事件都不中断地传递。请注意,虽然视觉播放速度已更改,但发射事件的时机(例如标记)将根据已修改的播放速率相应缩放。
图形输出

表示图形的最终评估姿态。该节点在动画图编辑器中自动包含在所有新图中。它的存在确保图形始终有效并始终生成动画姿态。
- 输入
- 姿态:应用于Rig的最终处理动画数据。
- 属性
- 无
- 事件数据
- 事件处理: 节点监听所有通过最终连接输入传递的事件。
- 事件发射: 该节点作为动画管道的出口,不会向图中发射信号。
过渡
动画图中的多个节点(如选择、优先选择、顺序和随机顺序)管理在切换活动输入时动画如何混合。为避免在节点参考中冗余,这些行为由标准化的过渡属性组定义。
默认过渡

应用于节点的基础混合行为,每当其切换到新的活动输入时。
- DefaultTransitionDuration(数字):完全混合新姿势所需的时间(以秒为单位)。
- DefaultTransitionCurve(Enum.PoseEasingStyle):在混合期间应用的缓和函数。目前仅支持Enum.PoseEasingStyle.Linear和Enum.PoseEasingStyle.CubicV2。
过渡覆盖
特定输入的链接属性,覆盖默认过渡。这些属性在节点切换到特定输入时应用。
- TransitionOverrideDuration(数字):覆盖默认过渡持续时间。
- TransitionOverrideCurve(Enum.PoseEasingStyle):覆盖默认过渡曲线。目前仅支持Enum.PoseEasingStyle.Linear和Enum.PoseEasingStyle.CubicV2。
复制
动画图参数是每个AnimationTrack本地的,并不会自动复制。这在未来的更新中可能会改变,但目前建议的方法使用:
- 客户端 → 服务器:使用RemoteEvent将参数从拥有的客户端发送到服务器,固定的更新频率。
- 服务器 → 其他客户端:实例属性,它们自动复制到所有客户端。
玩家角色
客户端脚本驱动玩家角色。由于只有拥有的客户端知道当前的游戏状态(运动输入、人形状态等),参数必须从拥有的客户端流向服务器,然后再流向所有其他客户端。
设置
- 一个LocalScript用于客户端动画和参数驱动。
- 一个Script用于服务器端复制。
- 一个RemoteEvent用于客户端到服务器的通信。
客户端脚本
拥有的客户端从游戏事件驱动参数,并定期将其发送到服务器。其他客户端则从服务器脚本中读取复制的属性。
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:FindFirstChildOfClass("Animator") or humanoid
-- 加载动画图
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://YOUR_GRAPH_ID" -- 替换为您发布的图形ID
local track = animator:LoadAnimation(animation)
track:Play()
local isLocalCharacter = Players:GetPlayerFromCharacter(character) == Players.LocalPlayer
if not isLocalCharacter then
-- 从服务器脚本读取复制的参数
local animateServer = script.Parent:WaitForChild("AnimateServer")
for attribute, value in animateServer:GetAttributes() do
track:SetParameter(attribute, value)
end
animateServer.AttributeChanged:Connect(function(attribute)
track:SetParameter(attribute, animateServer:GetAttribute(attribute))
end)
return
end
-- 本地玩家:从游戏事件驱动参数
local params = {}
local function setParam(key, value)
track:SetParameter(key, value)
params[key] = value
end
-- 示例:从人形状态驱动参数
humanoid.Running:Connect(function(speed)
setParam("Speed", speed)
setParam("State", if speed < 0.1 then "Idle" else "Moving")
end)
-- 以固定的更新率将参数发送到服务器
local replicateEvent = script.Parent:WaitForChild("ReplicateEvent")
local TICK_RATE = 1 / 20
local lastSendTime = 0
RunService.Stepped:Connect(function(gameTime)
if gameTime - lastSendTime > TICK_RATE then
lastSendTime = gameTime
replicateEvent:FireServer(params)
end
end)
服务器脚本
接收来自拥有客户端的参数,驱动服务器端动画图,并传播属性值以便复制到其他客户端。
local Players = game:GetService("Players")
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:FindFirstChildOfClass("Animator") or humanoid
-- 在服务器加载相同的动画图
local animation = Instance.new("Animation")
animation.AnimationId = "rbxassetid://YOUR_GRAPH_ID" -- 与客户端相同的图形ID
animation.Parent = script
local track = animator:LoadAnimation(animation)
track:Play()
-- 从拥有的客户端接收参数并通过属性进行复制
local replicateEvent = script.Parent:WaitForChild("ReplicateEvent")
replicateEvent.OnServerEvent:Connect(function(player, params)
if Players:GetPlayerFromCharacter(character) ~= player then
return
end
for key, value in params do
track:SetParameter(key, value)
script:SetAttribute(key, value)
end
end)
NPC
由于服务器拥有NPC动画,因此仅需要从服务器到客户端的复制。驱动服务器端的动画图,将参数写为服务器脚本上的属性,并使用相同的Instance.AttributeChanged模式在客户端读取它们。
延迟
其他客户端的动画将带有小的延迟,等于更新间隔(最多50毫秒,20赫兹)加上网络延迟。您可以通过增加频率(例如1/30)来减少更新间隔,但会增加网络流量。20赫兹是一个合理的默认值;动画混合是宽容的,因此小的延迟难以察觉。