Skip to main content

角色快速入门

概述

本文档将介绍动画树和状态机的创建,状态机状态的过渡,输入的绑定,最后编写脚本在关卡中可以控制角色行动。

创建项目

新建一个空项目,详见项目

导入美术资源

导入骨架网格体和动画。详见资源工作流程

Actor组装

详见Actor组装

添加状态机

在动画编辑器中新建一个动画树tp_tree。

image-20220902154842336

image-20220902154854384

在动画树中添加一个状态机节点,并连接到最终动画姿势。

image-20230131134903709

双击该状态机节点,进入状态机编辑界面,添加idle和run两个状态节点并连接这两个状态,使这两个状态间可形成一个循环。

双击idle状态节点,在该状态中添加站立动作。添加一个序列播放节点,并连接到最终动画姿势。

同样的,在run状态中添加跑步动作。

增加一个浮点类型的Speed参数。

image-20220909152055538

设置idle→run这个状态迁移的条件为:Speed大于0.1。

image-20230131135338115

设置run→idle这个状态迁移的条件为:Speed小于0.1。

image-20230131135522048

保存。

image-20230131135726500

输入设定编辑

在组件编辑器中依次点击菜单栏配置(Config) -> 项目设置(Project Settings)打开项目设置窗口。

image-20230131140704274

输入设置(Input Settings)的AxisMappingList中,增加类似下图中的7个按键。

image-20230131141632259

image-20230131142034455

设置角色

创建角色

在新关卡中,依次点击工具栏创建游戏对象(Create Game Object) -> 角色(Character)来创建一个玩家角色。

image-20230131142228340

属性设置

修改CharacterMesh0(Inherited)组件的骨骼网格配置文件(Skeletal Mesh Config File)属性为之前创建的.actor文件。

image-20230131142815953

继续修改默认行为覆盖(Default Action Override)属性为之前创建的tp_tree。

image-20230131143124082

大纲(Hierarchy)面板中选中CollisionCylinder(Inherited)后调整胶囊体的位置,选中CharacterMesh0(Inherited)后调整角色网格体的位置,使胶囊体尽可能在包裹角色的前提下,让角色蒙皮的脚部贴地。

image-20230330104903739

在CollisionCylinder(Inherited)组件下添加一个摄像机摇臂组件(Spring Arm Component)

image-20230131144335391

image-20230131144529512

在摄像机摇臂组件下添加一个摄像机组件(Camera Component)

image-20230131144624048

image-20230131144708289

调整摄像机组件至合适位置。

image-20230131144910107

选择角色对象,将其自动持有玩家(Auto Possess Player)属性设置为Player0,这个时候PIE会自动使用角色身上的摄像机。

image-20230131145132977

设置角色对象的使用控制器偏航角(Use Controller Angle Yaw)属性为false。

image-20230131145223727

设置摄像机摇臂组件的使用Pawn操控旋转(Use Pawn Control Rotation)属性为true,做碰撞测试(Do Collision Test)属性为false和启用摄像机延迟(Enable Camera Lag)属性为true。

设置CharMoveComp(Inherited)的为运动确定旋转方向(Orient Rotation To Movement)属性为true。

image-20230131145535527

编写角色控制脚本

在项目的script文件夹中,增加一个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


--use this for initialization
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")

--第二个参数为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, "on_velocity_changed", "on_velocity_changed")

end
end

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

--use this for release
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

绑定角色控制脚本

在角色对象中添加一个脚本组件(Script Component)

image-20230131145704041

image-20230131145804905

将脚本组件的脚本文件(Script File)属性设置为之前所写的controller.lua。

image-20220909153047489

保存。

image-20230131151930154

运行

运行后可查看效果。

image-20230131150149755