逐骨骼分层混合节点
最近更新时间:2023-10-07
概述
逐骨骼分层混合(Layered Blend Per Bone)能够在基础姿势上使用逐骨骼的掩码叠加多层姿势,常用于实现角色不同部位受不同动画驱动的这类需求。逐骨骼分层混合(Layered Blend Per Bone)允许将输入姿势定义为影响不同骨架区域的层,并对这些层进行加权混合,例如可用于角色在行走中播放不同的上半身动作。
节点使用
在动画树(Anim Tree)空白处右键打开节点选择面板,展开混合(Blends)分类,从中选取逐骨骼分层混合(Layered Blend Per Bone)。
添加完成:
点击节点上添加引脚(Add Pin)可以为其添加新的引脚。
添加引脚完成:
然后可以继续为节点配置合适的输入/输出。
属性
选中逐骨骼分层混合(Layered Blend Per Bone)节点后,可在属性窗口(Property Window)看到该节点的各项属性。
属性 | 说明 |
---|---|
混合权重列表(Blend Weight List) | 设置每层的权重。 |
网格空间旋转混合(Mesh Space Rotation Blend) | 是否在网格空间混合旋转。 |
曲线混合选项(Curve Blend Option) | 设置曲线混合行为来控制动画层的混合方式。
|
层设置(Layer Settings) | 层设置骨骼权重掩码,每层代表对应的混合姿势,有多少个混合姿势的输入就有多少层。每层可以配置多个分支,每个分支可以指定一根骨骼作为分支的根,一个深度值作为这个分支的过渡深度。当深度小于等于0时,该骨骼及其子骨骼不进行混合,应用基础姿势。当深度大于0时,该骨骼及其所有子骨骼与基础姿势进行混合,随着深度的增加逐渐增加混合姿势的混合权重。 |
示例
以下是使用逐骨骼分层混合(Layered Blend Per Bone)节点,让角色下半身移动时,上半身做攻击动作的简单示例。
本示例是在角色快速入门示例的基础上完成的。
添加动画
为原组合体(SK_Human.actor)添加攻击动画(default_Attack01.xskt)。
添加的攻击动画:
编辑动画资产
在组合体编辑器(Actor Editor)中双击添加的攻击动画(default_Attack01)打开动画编辑器(Animation Editor),进入到该动画资产的编辑界面。在资产细项(Asset Details)面板中,设置混入时间(Blend In Time)为0.2,混出时间(Blend Out Time)为0.3,使动作自然切换。
依次点击菜单栏文件(File) -> 保存(Save)保存修改。
配置动画树
在角色快速入门所创建的动画树中继续添加一个缓存姿势节点,修改其名称为Locomotion,并将状态机节点连接到该缓存姿势节点上。
在插槽管理器(Slot Manager)中添加一个UpperBody插槽,然后添加一个逐骨骼分层混合(Layered Blend Per Bone)节点、一个UpperBody插槽节点和两个使用缓存姿势“Locomotion”(Use Cache Pose "Locomotion")节点,并按照下图将各个节点连接起来。
注:有关插槽节点的使用可参考动画插槽。
选中逐骨骼分层混合(Layered Blend Per Bone)节点,在属性窗口(Property Window)中点击 按钮添加三层。
按照下图,为这三层分别填写骨骼名称(Bone Name)和深度(Depth),从而来指定该混合节点影响的骨骼。
需要混合的攻击动画,也就是上半身为攻击动作,下半身不进行混合。Bip01骨骼,深度为4(大于0)控制该骨骼及其子骨骼与基础姿势进行混合。
Bip01骨骼(黄色高亮):
下半身为行走动作,所以下半身不进行混合。找到控制下半身的骨骼Bip01 L Thigh(左大腿)和Bip01 R Thigh(右大腿),将这两个骨骼混合深度设置为-1使得下半身不进行混合,保持行走动画下半身的动作。
Bip01 L Thigh骨骼和Bip01 R Thigh骨骼(橙色框):
点击保存(Save)按钮。
输入设置
在角色快速入门输入设置中继续添加一个组合映射。点击组合映射列表(Combination Mapping List)旁的 按钮添加一个空的组合映射,然后设置其组合名称(Combination Name) 为Attack,键名(Key Name) 为Three。
编写脚本
继续编辑原脚本(controller.lua)。
在on_setup_input_component()
函数中为人物绑定攻击回调,如下所示:
input_comp:AddCombinationBinding("Attack", IE_Pressed, true, false, true, "Attack_Pressed")
nx_callback(owner, "Attack_Pressed", "on_attack_pressed")
在回调函数on_attack_pressed()
中混合攻击动画。
function on_attack_pressed(owner)
local actor = character_get_actor(owner)
if not nx_is_valid(actor) then
return
end
actor:BlendAction("default_Attack01", false, false, true, 1.0, true, "UpperBody", true)
end
controller.lua:
--动画资源类型
AT_UNKNOWN = 0
AT_SKELETON = 1
AT_SKELETON_AS_ANIMSEQUENCE = 2
AT_ANIMSEQUENCE = 3
AT_MONTAGE = 4
AT_BLENDSPACE = 5
AT_BLENDSPACE1D = 6
AT_AIMOFFSETBLENDSPACE = 7
AT_ANIMTREE = 8
local IE_Pressed = 0 --按键按下
local IE_Released = 1 --按键抬起
local IE_Repeat = 2
local IE_DoubleClick = 3
local IE_Axis = 4
local IE_MAX = 5
local CAMERA_DISTANCE_CHANGE_SPEED = 0.5
local CAMERA_DISTANCE_MIN = -1
local CAMERA_DISTANCE_MAX = 50
--用于初始化
function on_begin_play(component)
local owner = component.ActorOwner
if nx_is_valid(owner) then
local input_comp = owner.InputComponent
if nx_is_valid(input_comp) then
nx_bind_script(owner, nx_current())
--第一个参数对应输入设置中的Axis Name
--最后一个参数为自定义事件名
input_comp:AddAxisBinding("MoveForward", true, false, true, "InputAxisEvent_MoveForward")
input_comp:AddAxisBinding("MoveRight", true, false, true, "InputAxisEvent_MoveRight")
input_comp:AddAxisBinding("Turn", true, false, true, "InputAxisEvent_Turn")
input_comp:AddAxisBinding("LookUp", true, false, true, "InputAxisEvent_LookUp")
input_comp:AddAxisBinding("AdjustCameraDis", true, false, true, "InputAxisEvent_AdjustCameraDis")
input_comp:AddCombinationBinding("Attack", IE_Pressed, true, false, true, "Attack_Pressed")
--第二个参数为AddAxisBinding中的最后一个参数,最后一个参数表示脚本中的函数名
nx_callback(owner, "InputAxisEvent_MoveForward", "on_moveforward")
nx_callback(owner, "InputAxisEvent_MoveRight", "on_moveright")
nx_callback(owner, "InputAxisEvent_Turn", "on_turn")
nx_callback(owner, "InputAxisEvent_LookUp", "on_lookup")
nx_callback(owner, "InputAxisEvent_AdjustCameraDis", "on_adjust_camera_dis")
nx_callback(owner, "Attack_Pressed", "on_attack_pressed")
nx_callback(owner, "on_velocity_changed", "on_velocity_changed")
end
end
nx_callback(component, "on_tick", "tick")
end
--用于发布
function on_end_play(component)
end
--获取actor
function character_get_actor(character)
if nx_is_valid(character) then
local mesh_component = character.MeshComponent
if nx_is_valid(mesh_component) then
local vis_base = mesh_component.VisBase
if nx_is_valid(vis_base) and nx_is_kind(vis_base, "Actor") then
return vis_base
end
end
end
return nx_null()
end
--获取spring_arm
function character_get_spring_arm(character)
if nx_is_valid(character) then
local spring_arm_component = character:FindComponentByClassName("LSpringArmComponent")
return spring_arm_component
end
return nx_null()
end
--向前移动回调
function on_moveforward(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
--获得控制器Y轴角度
local controller = owner.Controller
if nx_is_valid(controller) then
local yaw = controller.AngleY
--获得向前方向
local x, y, z = nx_function("ext_angle_get_forward_vector", 0.0, yaw, 0.0)
--增加移动量
owner:AddMovementInput(x, y, z, axis_value)
end
return 1
end
--向右移动回调
function on_moveright(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
--获得控制器Y轴角度
local controller = owner.Controller
if nx_is_valid(controller) then
local yaw = controller.AngleY
--获得向右方向
local x, y, z = nx_function("ext_angle_get_right_vector", 0.0, yaw, 0.0)
--增加移动量
owner:AddMovementInput(x, y, z, axis_value)
end
return 1
end
--左右旋转(绕Y轴旋转)回调
function on_turn(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
owner:AddControllerYawInput(axis_value)
return 1
end
--上下旋转(绕X轴旋转)回调
function on_lookup(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
owner:AddControllerPitchInput(-axis_value)
return 1
end
--鼠标滚轮回调
function on_adjust_camera_dis(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
local spring_arm_component = character_get_spring_arm(owner)
if nx_is_valid(spring_arm_component) then
local delta = axis_value * CAMERA_DISTANCE_CHANGE_SPEED
local cur_value = spring_arm_component.TargetArmLength
local new_value = cur_value + delta
if delta > 0 then
if new_value > CAMERA_DISTANCE_MAX then
new_value = CAMERA_DISTANCE_MAX
end
spring_arm_component.TargetArmLength = new_value
else
if new_value < CAMERA_DISTANCE_MIN then
new_value = CAMERA_DISTANCE_MIN
end
spring_arm_component.TargetArmLength = new_value
end
end
return 1
end
--速度变化回调
function on_velocity_changed(owner, component_id, new_x, new_y, new_z, old_x, old_y, old_z)
local actor = character_get_actor(owner)
if nx_is_valid(actor) then
if actor:GetBlendActionCount() > 0 then
local action_name = actor:GetBlendActionName(0)
local action_type = actor:GetActionType(action_name)
local action_index = actor:GetActionIndex(action_name)
if action_type == AT_ANIMTREE then
local speed = math.sqrt(new_x * new_x + new_y * new_y + new_z * new_z)
actor:SetAnimTreeValue(action_index, "Speed", speed)
nx_set_custom(actor,"CurSpeed",speed);
end
end
end
return 1
end
function on_attack_pressed(owner)
local actor = character_get_actor(owner)
if not nx_is_valid(actor) then
return
end
actor:BlendAction("default_Attack01", false, false, true, 1.0, true, "UpperBody", true)
end
绑定脚本
从项目(Project)面板中将所创建的脚本拖拽到脚本组件(LScript)的脚本文件(Script File)中。
拖拽完成:
运行
点击运行按钮,WASD移动时按3键,角色会一边移动一边攻击。