Skip to main content

根运动

概述

根运动(Root Motion)是动画编辑器中的一项功能,该功能可以提取动画序列中的根运动数据,运用该数据驱动角色胶囊体移动。

根运动支持提取多个动作融合后的根骨骼动画的增量变换(Delta Transform),并抹除动画中的根骨骼动画。逻辑层基于该特性,可以制作动画驱动的位移表现。

启用根运动

在不启用根运动的时候,骨架网格体和胶囊体是分离的,直接穿过静态网格体并在动画结束后回到最初的位置,角色的动作原点始终在原地。

在启用根运动后,胶囊体保持同步,物理碰撞仍然可以使用。提取动画序列中的根运动数据来驱动角色胶囊体的移动,使角色与静态网格体产生交互,动作坐标原点始终跟随角色移动。角色碰撞到静态网格体后停止位移,此时,动作坐标原点变为角色移动后的位置处。


不启用根运动

启用根运动

根骨骼

角色动画由其骨架网格体的骨骼驱动,在所有骨骼中,根骨骼是整个骨架的基础骨骼。动画中没有动画数据的根骨骼是保持静止不动的,动画在继续播放中,角色本身不在世界坐标中有任何位移。

还有一些动画,其根骨骼是静止的,其他的运动在根骨骼上分配动作,根骨骼进行移动,但是角色本身并不跟随移动,若要角色有位移动作,则需开启根运动。开启根运动后,先锁住根骨骼动画数据,然后将它提取出来应用到角色上。

使用根运动

前置条件

使用根运动功能需要一个包含根骨骼的骨架(组合体(Actor)),同时,该组合体中需要包含一个动画序列,为根骨骼添加动画。

动画序列

使用根运动功能需将每个动画序列的根运动(Root Motion)开启。在动画序列的资产细项(Asset Details)面板中勾选启用根运动(Enable Root Motion),可根据需求修改根运动相关属性。

image-20230816172314620

属性说明
启动根运动(Enable Root Motion)是否开启根运动。
根运动根锁定(Root Motion Root Lock)根运动提取后,根骨骼的锁定方式。
  • 参考姿势(Ref Pose):使用参考姿势。
  • 动画第一帧(Anim First Frame):使用第一帧。
  • 0:使用规范化的Transform。
强制锁定根骨骼(Force Root Lock)强制锁定根骨骼,无论是否提取过根运动。
使用标准化根运动缩放(Use Normalized Root Motion Scale)使用归一化的根运动缩放(避免非归一化的缩放对根运动结果的影响)。
位移(Translate)旧的根运动相关提取设置。可选择清除Z轴(Clear Z-Axis)和清除X、Z轴(Clear X,Z Axes)。

动画树

将动画序列中的根运动启用后,还需在动画树编辑器中使用节点来控制管理动画。可将要用到根运动的动画放入动画树中的插槽中。

根运动模式

在将要使用根运动功能的组合体(Actor)中,可以选择不同的根运动模式(Root Motion Mode)

名称说明
不抽取(No Extraction)不从动画中提取根骨骼运动数据,将其保留在动画姿势中。
忽略根运动(Ignore Root Motion)提取动画中的根运动,并锁定动画姿势的根变换。但不会缓存提取的根运动数据给上层逻辑使用。
来自任何动画(From Everything)从任何开启根运动的动画中提取根运动数据,并锁定动画姿势的根变换。会根据来源于动画的各自权重混合并缓存结果供上层逻辑使用。
仅来自于蒙太奇(From Montage Only)仅从蒙太奇中提取根运动数据,并锁定动画姿势的根变换。会缓存数据供上层逻辑使用。

脚本调用播放

actor:BlendAction(“选择播放的动画序列”, false, false, true, 1.0, true, “选择播放插槽”, true),最后一个true表示停止播放同插槽组的其他蒙太奇。

示例

下面为开启根运动后,角色奔跑中遇到墙壁的示例。

创建组合体

参照Actor组装,创建一个包含蒙皮和骨架资产的组合体。

image-20230526143146390

编辑动画资产

在动画编辑器中选中sprint骨架资产后右击,在弹出的快捷菜单中选择将骨架资产转换为动画序列资产(Convert Skeleton To AnimSeq),将该骨架资产转换为一个动画序列资产,然后在弹出的窗口中输入名称后点击确定(OK)按钮完成转换。

image-20230523151438382

在该动画序列的资产细项(Asset Details)面板中,勾选启用根运动(Enable Root Motion)

image-20230523152947818

编辑完成后,依次点击菜单栏文件(File) -> 保存所有(Save All)进行保存。

image-20230523153058498

配置动画树

在动画编辑器中点击菜单栏文件(File) -> 创建(Create),在弹出的窗口中的下拉框中选择动画树(AnimTree),然后输入名称并选择路径,点击确定(OK)按钮完成创建并进入到动画树编辑器中。

添加一个序列播放节点。在动画树空白处右击打开节点选择面板,展开动画(Animation)选项,选择播放“default_stand”(Play “default_stand”)节点。

在动画编辑器中的插槽管理器(Slot Manager)创建一个默认插槽(DefaultSlot),具体创建步骤可参照动画插槽

从该动画播放节点引脚处连接一个插槽节点,在弹出的节点选择面板中选择蒙太奇(Montage)选项中刚才所创建的插槽节点。

将该插槽节点连接到最终动画姿势(Final Animation Pose)节点上。

编辑完成后,在动画编辑器中依次点击菜单栏文件(File) -> 保存(Save)

配置组合体

项目(Project)面板中,双击组合体(Actor)文件打开组合体编辑器。

在打开的组合体编辑器中,点击资产细项(Asset Details),然后将默认动作(Default Action)设置为所创建的根运动动画树资产,将根运动模式(Root Motion Mode)设置为仅来自于蒙太奇(From Montage Only)

image-20230523144308272

编辑完成后,在组合体编辑器中依次点击菜单栏文件(File) -> 保存(Save)

创建角色

在组件编辑器中创建一个关卡,依次点击创建游戏对象(Create Game Object) -> 角色(Character)

大纲(Hierarchy)面板中选中该角色对象,然后在观察器(Inspector)面板中选中CharacterMesh0(Inherited),将所创建的组合体(Actor)拖入到骨架网格体配置文件(Skeletal Mesh Config File)的插槽中。

image-20230523131150125

拖入完成:

image-20230524173119575

观察器(Inspector)面板中,选中CollisionCylinder(Inherited)和CharacterMesh0(Inherited)分别调整该角色胶囊体和网格体的位置,使胶囊体尽可能在包裹角色的前提下,让角色蒙皮的脚部贴地,胶囊体也需在地面上方。

image-20230428140907772

调整角色角度,在观察器(Inspector)面板中勾选该角色的自动控制AI(Auto Control AI)属性。

image-20230524175546934

创建墙壁

在关卡中添加一个墙壁(静态网格体)。从项目(Project)面板中的资源预览窗口中拖拽一个.xmod文件到关卡(Level)中。

根据需要调整该静态网格体的位置和大小。

创建触发器

在关卡中添加一个对象作为触发器。依次点击创建游戏对象(Create Game Object) -> 游戏对象(Game Object),并将该对象重名为RootMotionTrigger。

在大纲面板中选中RootMotionTrigger对象,然后在观察器面板中点击添加组件(Add Component)按钮,选择添加一个胶囊体(Capsule)组件。

在其形状(Shape)选项中可调整该胶囊体的一些相关属性,胶囊体经线数量(Capsule Sides)胶囊体半球纬线数量(Capsule Half Sphere Rings)胶囊体半径(Capsule Radius)圆柱体半高(Cylinder Half Height)。在关卡中调整该胶囊体的位置,使其与角色胶囊体不重叠。

image-20230606151913833

为触发器绑定脚本

为RootMotionTrigger再添加一个脚本(Script)组件。在大纲(Hierarchy)面板中选中该对象,然后在观察器(Inspector)面板中点击添加组件(Add Component)按钮,选择脚本组件(Script Component)

为该触发器对象绑定一个脚本。编写一个脚本并将其保存在项目资源中的脚本文件夹中。从资源预览窗口中将脚本拖入到该对象脚本组件的脚本文件(Script File)插槽中。

image-20230523154604532

拖入完成:

image-20230523155845647

脚本

local target_actor_name = "LCharacter"

function on_begin_play(component)
local owner = component.GameObjectOwner

nx_bind_script(owner, nx_current())
nx_callback(owner, "on_begin_overlap", "on_begin_overlap")

return 1
end

function on_end_play(component)
end

function on_begin_overlap(pawn, other_actor)
local lscene = nx_value("lscene")
local level = lscene.PersistentLevel
local target_actor = nx_function("ext_find_object", nx_null(), level, target_actor_name)

if not nx_is_valid(target_actor) then
return 0
end

local target_character_mesh_component

for i = 1, target_actor.ComponentCount do
local component = target_actor:GetComponentByIndex(i - 1)

if nx_name(component) == "LSkeletalMeshComponent" then
target_character_mesh_component = component
break
end
end

if not nx_is_valid(target_character_mesh_component) then
return 0
end

local actor = target_character_mesh_component.VisBase

if not nx_is_valid(actor) then
return 0
end

actor:BlendAction("root_motion_sprint_sq", false, false, true, 1.0, true, "DefaultSlot", true)

return 1
end

创建默认Pawn

依次点击创建游戏对象(Create Game Object) -> 默认Pawn(Default Pawn),调整其朝向并将其移动到合适的位置上。

image-20230525162849158

在大纲面板中选中LDefaultPawn,然后在观察器面板中选中LDefaultPawn(Instance),将自动持有玩家(Auto Possess Player)设置为Player 0。

运行

点击运行(Play)按钮即可查看效果。