Character Quick Start
Overview
This documentation will introduce how to create Anim Trees and State Machines, transit the state of State Machines, bind inputs, and finally write scripts to control the character's actions in the Level.
Creating a New Project
Create a new empty project, please see Project for details
Importing Art Assets
Import skeleton meshes and animations. Please see Model Resources in the Resources Workflow for details.
Assembling Actors
Please see Assembling Actors for details.
Adding State Machine
Create an Anim Tree tp_tree in the Animation Editor.
Add a State Machine Node to the Anim Tree and connect it to Final Animation Pose.
Double-click the State Machine Node to enter its editing interface. Add two state nodes, idle and run, and connect them to form a cycle.
Double-click the idle state node and add a stand pose to it. And then add a Sequence Playback Node and connect it to Final Animation Pose.
Similarly, add a run action to the run state.
Add a Speed Parameter of the Float type.
Set the state transition condition of idle → run as Speed is greater than 0.1.
Set the state transition condition of run → idle as Speed is less than 0.1.
Click Save.
Editing Input Settings
Click Config (Menu Bar) -> Project Settings to open the Project Settings window in the Component Editor.
In the Axis Mapping List of Input Settings, add 7 keys similar to the ones in the picture below.
Character Settings
Creating a Character
Click Create Game Object -> Character to create a player character in the new level.
Property Settings
Modify the property Skeletal Mesh Config File of the CharacterMesh0(Inherited) Component to the previously created .actor file.
Then modify the property Default Action Override to the previously created tp_tree as well.
In the Hierarchy panel, select CollisionCylinder(Inherited) and adjust the position of the capsule. Then select CharacterMesh0(Inherited) and adjust the position of the character mesh, so that the feet of the character skin is close to the ground in the premise that the capsule wraps the character as much as possible.
Add a Spring Arm Component under the CollisionCylinder(Inherited) Component.
Then add a Camera Component under the Spring Arm Component.
Adjust the Camera Component to the appropriate position.
Select the LCharacter and set its property Auto Possess Player to Player0. At this time, PIE will automatically use the camera of the character.
Set (Uncheck) the property Use Controller Angle Yaw of the character object to false.
Set (Check) the properties of the Spring Arm Component including Use Pawn Control Rotation to true, Do Collision Test to false (Uncheck), and Enable Camera Lag to true respectively.
Set (Check) the property Orient Rotation To Movement of the CharMoveComp(Inherited) to true.
Scripting Character Control
Add a controller.lua file in the script folder of the project, and scripting as follows.
--Animation Resource Type
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 --Press the Key
local IE_Released = 1 --Release the Key
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())
--The first parameter corresponds to the Axis Name in the input settings
--The last parameter is the custom Event 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")
--The second parameter is the last parameter in AddAxisBinding, which represents the function name in the script
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
--Get 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
--Get 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
--Move forward callback
function on_moveforward(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
--Get controller Y-axis angle
local controller = owner.Controller
if nx_is_valid(controller) then
local yaw = controller.AngleY
--Get forward direction
local x, y, z = nx_function("ext_angle_get_forward_vector", 0.0, yaw, 0.0)
--Add Movement value
owner:AddMovementInput(x, y, z, axis_value)
end
return 1
end
--Move right callback
function on_moveright(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
--Get controller Y-axis angle
local controller = owner.Controller
if nx_is_valid(controller) then
local yaw = controller.AngleY
--Get right direction
local x, y, z = nx_function("ext_angle_get_right_vector", 0.0, yaw, 0.0)
--Add Movement value
owner:AddMovementInput(x, y, z, axis_value)
end
return 1
end
--Rotate left and right (along Y-axis) callback
function on_turn(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
owner:AddControllerYawInput(axis_value)
return 1
end
--Rotate up and down (along X-axis) callback
function on_lookup(owner, axis_value)
if not nx_is_valid(owner) then
return 0
end
owner:AddControllerPitchInput(-axis_value)
return 1
end
--Mouse wheel callback
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
--Velocity change callback
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
Binding Character Control Script
Add a Script Component to the character object.
Set the property Script File of the Script Component to the controller.lua as written previously.
Click Save Current Level (Ctrl+S).
PIE
Playing in the Editor as shown below.