Skip to main content

骨骼朝向

概述

骨骼朝向(Look At)功能可以实现角色自然地看向一个点。

骨骼朝向(Look At) IK功能以动画后处理节点(Animation Post-processing Nodes)的方式整合在动画树(Animation Tree)中,每个节点控制一根骨骼,可以选择在骨骼链上的一根或多根骨骼上应用该动画节点获得期望的角色姿态。

骨骼朝向节点

动画树(Animation Tree)中的骨骼朝向(Look At)节点可控制骨骼始终朝向某个目标对象。

属性

下面为骨骼朝向(Look At)节点的属性。

image-20230519133344376

属性说明
修改骨骼(Modify Bone)该参数指定当前节点控制的骨骼名称。
向前方向参数名(Forward Dir Param Name)向前方向是指朝向目标的方向。该参数指定用于获取向前方向数据的动画树参数名。
向前方向在局部空间(Forward Dir In Local)该参数指定向前方向是否在局部空间。
使用向上方向(Using Up Dir)向上方向是指旋转的轴向。该参数指定是否使用向上方向。
锁定向上方向(Up Dir Locked)该参数指定是否锁定向上方向。锁定向上方向将严格将向上方向作为旋转轴,而不锁定则将其视为一个大致的轴向。
向上方向参数名(Up Dir Param Name)该参数指定用于获取向上方向数据的动画树参数名。
向上方向在局部空间(Up Dir In Local)该参数指定向上方向是否在局部空间。
目标参数名(Target Param Name)该参数指定用于获取目标位置数据的动画树参数名。
目标位置在局部空间(Target In Local)该参数指定目标位置是否在局部空间。
插值(Interpolated)该参数指定是否插值。
插值速度(Interpolation Speed)该参数指定向目标朝向逼近的速率。
约束角度(Constraint Angle)该参数用于限制被控制骨骼的旋转角度。
混合权重(Alpha)该参数控制该节点对被修改骨骼施加影响的程度。
开启调试绘制(Debug Draw Enabled)是否开启调试绘制。

脚本调用

相关脚本调用接口:

/// method: void GetAnimTreeValue(constIVarList&args, IVarList& result)
/// \brief Get action tree instance parameter value
/// \paramaction_index Action Index
/// \paramparameter_name Parameter Name
DECLARE_METHOD_N(Actor, GetAnimTreeValue);
/// method: void SetAnimTreeValue(constIVarList&args, IVarList& result)
/// \brief Set action tree instance parameter value
/// \paramaction_index Action Index
/// \paramparameter_name Parameter Name
/// \param value Parameter Value
DECLARE_METHOD_N(Actor, SetAnimTreeValue);

示例

下面为展示骨骼朝向(Look At)功能的一个示例。

创建组合体

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

image-20230525165025964

配置动画树

在该组合体编辑器( Actor Editor)动作列表(Action List)中双击一个骨架资产打开动画编辑器(Animation Editor)

image-20230525165134960

为角色创建一个骨骼朝向(Look At)资产,在动画编辑器中创建一个动画树。依次点击菜单栏文件(File)-> 创建(Create),点击弹出窗口中的下拉框,选择动画树(AnimTree),为其命名并选择创建的路径。

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

动画树编辑器(Anim Tree Editor)中为角色动画添加骨骼朝向(Look At)控制,需先将最初的动画转为网格空间(Mesh Space),因为骨骼朝向(Look At)节点在网格空间工作。从播放“default_stand”节点引出一个局部空间转网格空间(Convert Local To Mesh Space)节点。

添加骨骼朝向(Look At)节点来控制指定骨骼。从局部空间转网格空间(Convert Local To Mesh Space)节点引出一个骨骼朝向(Look At)节点。

添加节点完成后,设置该骨骼朝向(Look At)节点的属性

image-20230519141703719

属性
修改骨骼(Modify Bone)Bip01 Spine
向前方向参数名(Forward Dir Param Name)NeckForwardDir
向前方向在局部空间(Forward Dir In Local)启用
使用向上方向(Using Up Dir)启用
锁定向上方向(Up Dir Locked)启用
向上方向参数名(Up Dir Param Name)UpDirLS
向上方向在局部空间(Up Dir In Local)启用
目标参数名(Target Param Name)TargetPos
目标位置在局部空间(Target In Local)不启用
插值(Interpolated)启用
插值速度(Interpolation Speed)0.3
约束角度(Constraint Angle)45
混合权重(Alpha)1
开启调试绘制(Debug Draw Enabled)启用

动画参数(Animation Parameter)窗口中增加骨骼朝向(Look At)节点需要用到的参数,如何创建参数详见动画参数。创建四个字符串类型的参数NeckForwardDir、UpDirLS、UpDirWS和TargetPos,并为其设置合适的数值。其中TargetPos参数设置为角色所要朝向的目标位置。

骨骼朝向(Look At)节点设置完成后,从骨骼朝向(Look At)节点引出一个网格空间转局部空间(Convert Mesh To Local Space)节点。

网格空间转局部空间(Convert Mesh To Local Space)的节点连接到最终动画姿势(Final Animation Pose)节点上,然后点击应用(Apply)保存(Save)按钮。

预览(Preview)窗口中查看效果,此时可以看到角色的腰部已经扭向了目标方向。其中一条白线指向的是目标方向,另一条白线指向的是Forward Dir调整后的真实方向,灰线指向的是Forward Dir调整前的方向,黑线指向的是期望方向。经过骨骼朝向(Look At)节点对骨骼的控制,可以看到代表期望方向的黑线与代表真实方向的白线已经高度重合了。

可以继续添加新的骨骼朝向(Look At)节点来控制其他骨骼,从而使角色身体的不同部位朝向目标方向,以形成更加自然的跟随目标动画。将新创建的骨骼朝向(Look At)节点串联在之前的动画树(Animation Tree)中,在第一个骨骼朝向(Look At)节点后再添加两个骨骼朝向(Look At)节点。

新增的两个骨骼朝向(Look At)节点分别用来控制Spine2和Head骨骼,为这两个节点分别设置合适的属性值。

image-20230522112536019

image-20230526135112484

为保证角色最终的朝向目标姿势自然不夸张,将控制Spine骨骼的骨骼朝向(Look At)节点的混合权重值改为0.2。

image-20230522113426376

编辑完成后,点击动画树编辑器(Anim Tree Editor)中的应用(Apply)保存(Save)。在预览窗口中可以看到角色身体的不同部位已经以不同程度朝向了目标方向。

若要取消节点调试线条的绘制,则选中节点,在属性窗口(Property Window)面板中取消勾选开启调试绘制(Debug Draw Enabled),然后点击应用(Apply)保存(Save)按钮,在预览窗口中可看到取消后的效果。

image-20230526132312222

配置组合体

在组件编辑器的项目(Project)面板中双击组合体打开组合体编辑器(Actor Editor),在资产细项(Asset Details)面板中将默认动作(Default Action)改为骨骼朝向(Look At)动画树资产。

image-20230526133130488

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

image-20230526133254903

创建角色

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

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

image-20230627151717403

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

image-20230522165633220

为角色绑定脚本

在观察器面板中选中LCharacter(Instance),然后点击添加组件(Add Component)按钮,选择脚本组件(Script Component)

image-20230627154118904

image-20230627154107714

为该角色绑定脚本。编写一个脚本并将其保存在项目资源中的脚本文件夹中。在大纲(Hierarchy)面板中选中LCharacter,然后在观察器(Inspector)面板中选中LScript,从资源预览窗口中将脚本拖入到脚本文件(Script File)插槽中。

image-20230523095518133

拖入完成:

image-20230523101941116

脚本

lookat.lua

require("public_utils")

function on_begin_play(component)
local self_object = get_mesh_object(component)

if not nx_is_valid(self_object) then
return
end

nx_callback(component, "on_tick", "tick")
end

function tick(component)
local self_object = get_mesh_object(component)

if not nx_is_valid(self_object) then
return
end

local target_object = get_target_object()

if not nx_is_valid(target_object) then
return
end

local x = target_object.PositionX
local y = target_object.PositionY
local z = target_object.PositionZ

local target_pos = nx_string(x) .. "," .. nx_string(y) .. "," .. nx_string(z)

local action_idnex = self_object:GetActionIndex("lookat")
local return_value = self_object:SetAnimTreeValue(action_idnex, "TargetPos", target_pos)
end

function get_mesh_object(component)
local owner = component.GameObjectOwner

if not nx_is_valid(owner) then
return nx_null()
end

local mesh_com = owner.MeshComponent

if not nx_is_valid(mesh_com) then
return nx_null()
end

return mesh_com.VisBase
end

function get_target_object()
local player_controller = get_player_control()

if not nx_is_valid(player_controller) then
return nx_null()
end

local pawn = player_controller.Pawn
if not nx_is_valid(pawn) then
return nx_null()
end
--[[
local actors = pawn:GetComponentList()
local mesh_com = nx_null()
for i = 1, table.getn(actors) do
if nx_is_kind(actors[i], "fx_component.LModelComponent") then
mesh_com = actors[i]
break
end
end

if not nx_is_valid(mesh_com) then
return nx_null()
end

return mesh_com.VisBase
--]]
return pawn
end

public_utils.lua

function get_player_control()
local lworld = nx_value("lworld")

if nx_is_valid(lworld) then
local lscene = lworld.PlayScene

if nx_is_valid(lscene) then
local local_player = lworld:GetLocalPlayerFromControllerId(lscene, 0)

if nx_is_valid(local_player) then
return local_player.PlayerController
end
end
end

return nx_null()
end

创建默认Pawn

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

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

运行

点击运行(Play)即可查看效果,角色始终跟随视角朝向。

image-20230526141849176