Motion Warping
Overview
Motion Warping allows the animation's Root Motion to be procedurally adjusted to fit different start positions and rotations to reuse the Root Motion animation assets in different scenes.
Automatically corrects the movement and rotation values of the animation based on the position and orientation of the target object. For example, allow the player to jump over barriers with a jumping motion at different positions.
![]() | ![]() | ![]() |
Animation Sequences
By creating Motion Warping State Notifies in the animation sequence to specify the Motion Warping area, and adjusting the Root Bone displacement as needed, you can create a variety of animation effects even with limited animation assets.
Creating State Notifies
Open the needed animation sequence asset in the Animation Editor. Right-click the Notify track and select Add Notify -> Motion Warping.
After adding a Motion Warping State Notify, you can drag the Notify tab left or right on the track to adjust its start and end time to specify the warping area.
Properties
After selecting a Motion Warping State Notify on the track, you can edit its related properties in the Details Panel.
Property | Description |
---|---|
Type | Specify the type of the applied algorithm. |
Target Name | Specify the tag name of the target in the scene. |
Warp Translation | Specify whether to enable the adjustment of the displacement. |
Warp Rotation | Specify whether to eanble the adjustment of the rotation. |
Rotation Warping Type | Specify the method used to adjust the rotation.
|
Animation Trees
Animations can be controlled and managed by adding nodes in the Animation Tree, and animations that will use Motion Warping can be placed into slots in the Animation Tree.
Example
The following is an example of the character using the Motion Warping feature to jump over a target barrier.
Assembling Actors
Refer to Assembling Actors to create an Actor containing skinned mesh and skeleton assets.
Setting Animation Sequences
Convert the jumping skeleton asset to an animation sequence asset. Select the skeleton asset in the Asset Browser window of the Animation Editor, then right-click and select Convert Skeleton To AnimSeq.
Name the animation sequence asset in the pop-up window, and click the OK button to finish the conversion.
Enable Root Motion. Check Enable Root Motion in the Asset Details panel of the animation sequence asset.
Add a Motion Warping State Notify to the track in the editing panel of the asset. Right-click the track and then click Add Notify -> Motion Warping.
Enter the name of the State Notify in the pop-up New Notify window, and click the OK button to finish adding.
Adjust the position and duration of the Motion Warping State Notify according to the animations to be warped. For basic operations on State Notify, please refer to Animation Notify.
Select the Motion Warping State Notify and modify its properties in the Details Panel. Enter the name VaultPoint in Target Name to mark the warping.
Continue adding State Notifies as described above, and specify Target Names for these warpings.
Check End Trigger in the Asset Details panel to trigger a Notify when the animation finishes playing.
Configuring Animation Trees
Character animations can be managed by adding nodes in Animation Trees.
In the Animation Editor, click File (Menu Bar) -> Create, then click the drop-down box in the pop-up window, select AnimTree, name it and select the path to create it.
Right-click in the created Animation Tree to open the Node Selection panel, and then click State Machine -> Add New State Machine to add a State Machine node.
Double-click the State Machine node to enter the state graph editing interface, where you can create states and organize structures. Create the transition between the two states idle and run by referring to Character Quick Start -> Adding State Machine Nodes.
Create a slot FullBody in the Slot Manager window of the Anim Tree Editor. First, right-click Slot Name in Slot Manager, and then click Add Default Group And Slot in the pop-up Shortcut Menu.
Right-click DefaultSlot to open the Shortcut Menu, select Add Slot to add a new one, enter the slot name in the pop-up window and press the Enter key to finish adding.
Drag from the State Machine node pin to open the Node Selection panel, expand the Montage category and select Slot"FullBody".
Connect the Slot "Fullbody" node to the Final Animation Pose node.
Last, click File -> Save in the Anim Tree Editor.
Configuring Actors
Double-click the created Actor file in the Resource Preview window to open it, then click Asset Details in the Actor Editor, set Default Action to the Animation Tree you just created, and set Root Motion Mode to From Everything.
After modification, click Save in the Actor Editor.
Creating Characters
Create a Level in the Component Editor. Add a Character in the Level and set its properties. The Skeletal Mesh Config File for the character is the Actor you just created. Refer to Character Quick Start -> Character Settings for details.
Input Settings
Refer to Character Quick Start -> Editing Input Settings for details.
Creating Targets
Drag three models from the Resource Preview window into the Level and adjust their sizes. Use the three static meshes as targets to be avoided by the character warping (The orientation of the three static meshes needs to be consistent with the character).
In the Hierarchy panel, right-click the static mesh object to open the Shortcut Menu, and click Edit -> Rename to rename it.
After renaming:
Select the static mesh you just created in the Hierarchy panel and enter the name in the String Tag property under the Inspector panel. The name here should be consistent with the Target Name of the Motion Warping State Notify in the animation sequence.
Set String Tags as FlyEndPoint and LandingPoint for the remaining two static meshes as described above.
Binding Scripts to Characters
Bind a script to the character. Select LCharacter in the Hierarchy panel, select LCharacter(Instance) in the Inspector panel, then click the Add Component button and select the Script Component.
Configure the script file in the Property panel of the Script Component. Drag Script from the Resource Preview window into the Script File slot of the character script component.
After dragging:
Script One
Reference script:
motion_warping_character.lua
--Script template
require("public_attr")
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
--------------------------
--Local utility function--
--------------------------
---------------------
--Default callbacks--
---------------------
--Use this for initialization
function on_begin_play(component)
local owner = component.GameObjectOwner
if nx_is_valid(owner) then
local input_comp = owner.InputComponent
if nx_is_valid(input_comp) then
nx_bind_script(owner, nx_current())
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("Jump", IE_Pressed, true, false, true, "InputActionEvent_Jump_Pressed")
input_comp:AddCombinationBinding("Jump", IE_Released, true, false, true, "InputActionEvent_Jump_Released")
input_comp:AddCombinationBinding("Dash", IE_Pressed, true, false, true, "InputActionEvent_Dash_Pressed")
input_comp:AddCombinationBinding("EnableLookAt", IE_Pressed, true, false, true, "InputActionEvent_EnableLookAt_Pressed")
input_comp:AddCombinationBinding("LeftClick", IE_Pressed, true, false, true, "InputActionEvent_LeftClick_Pressed")
input_comp:AddCombinationKeyBinding("R", false, false, false, false, 0, false, false, true, "r_key_pressed")
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, "InputActionEvent_Jump_Pressed", "on_jump_pressed")
nx_callback(owner, "InputActionEvent_Jump_Released", "on_jump_released")
nx_callback(owner, "InputActionEvent_LeftClick_Pressed", "on_leftclick_pressed")
nx_callback(owner, "InputActionEvent_Dash_Pressed", "on_dash_pressed")
nx_callback(owner, "InputActionEvent_EnableLookAt_Pressed", "on_enable_look_at_pressed")
nx_callback(owner, "on_velocity_changed", "on_velocity_changed")
nx_callback(owner, "r_key_pressed", "toggle_ragdoll")
end
-- Query MotionWarping TargetPoint
local mw_target_name1 = "VaultPoint"
local mw_target_name2 = "FlyEndPoint"
local mw_target_name3 = "LandingPoint"
local scene = owner.Scene
local actor = character_get_actor(owner)
local vault_point_objs = scene:QueryObjectListByTag(mw_target_name1, "fx_component.LGameObject")
if #vault_point_objs > 0 then
local vault_point_actor = vault_point_objs[1]
actor:AddOrUpdateMotionWarpingTarget(mw_target_name1, vault_point_actor.PositionX, vault_point_actor.PositionY, vault_point_actor.PositionZ, vault_point_actor.AngleX, vault_point_actor.AngleY, vault_point_actor.AngleZ)
end
local fly_end_point_objs = scene:QueryObjectListByTag(mw_target_name2, "fx_component.LGameObject")
if #fly_end_point_objs > 0 then
local fly_end_point_actor = fly_end_point_objs[1]
actor:AddOrUpdateMotionWarpingTarget(mw_target_name2, fly_end_point_actor.PositionX, fly_end_point_actor.PositionY, fly_end_point_actor.PositionZ, fly_end_point_actor.AngleX, fly_end_point_actor.AngleY, fly_end_point_actor.AngleZ)
end
local landing_point_objs = scene:QueryObjectListByTag(mw_target_name3, "fx_component.LGameObject")
if #landing_point_objs > 0 then
local landing_point_actor = landing_point_objs[1]
actor:AddOrUpdateMotionWarpingTarget(mw_target_name3, landing_point_actor.PositionX, landing_point_actor.PositionY, landing_point_actor.PositionZ, landing_point_actor.AngleX, landing_point_actor.AngleY, landing_point_actor.AngleZ)
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
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
owner:AddMovementInput(x, y, z, axis_value)
end
return 1
end
--Rotate left and right (around 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 (around 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
--Press jump key callback
function on_jump_pressed(owner)
if nx_find_custom(owner, "JumpAgainLock") and owner.JumpAgainLock then
return 1
end
--Character jumping
owner:Jump()
return 1
end
--Dash
function on_dash_pressed(owner)
local actor = character_get_actor(owner)
if not nx_is_valid(actor) then
return
end
local movement_comp = owner.CharacterMovement
--local mode = movement_comp.MovementMode
local new_mode = nx_enum("MOVEMENT_MODE::Flying")
movement_comp:SetMovementModeWithCustom(new_mode, 0)
actor:BlendAction("Super_Jumping_Over_sq", false, false, true, 1.0, true, "FullBody", true)
end
--lookat
function on_enable_look_at_pressed(owner)
local actor = character_get_actor(owner)
if not nx_is_valid(actor) then
return
end
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
if actor:GetAnimTreeValue(action_index, "LookAtIndex") == 0 then
actor:SetAnimTreeValue(action_index, "LookAtIndex", 1)
else
actor:SetAnimTreeValue(action_index, "LookAtIndex", 0)
end
end
end
--Release jump key callback
function on_jump_released(owner)
--Character stops jumping
owner:StopJumping()
return 1
end
function on_leftclick_pressed(owner)
local actor = character_get_actor(owner)
if not nx_is_valid(actor) then
return
end
--actor:BlendAction("PrimaryMelee_D_Slow_sq", false, false, true, 1.0, true, "myslot", true)
--actor:BlendAction("PrimaryMelee_C_Slow2_MSA_Anim", false, false, true, 1.0, true, "myslot", true)
actor:BlendAction("PrimaryMelee_C_Slow_MSA_sq", false, false, true, 1.0, true, "myslot", true)
--actor:SetBlendActionPause("PrimaryMelee_C_Slow_MSA_sq", true)
--[[
local cur_percent = 0
while cur_percent < 100 do
actor:SetCurrentPercent("PrimaryMelee_C_Slow_MSA_sq", cur_percent)
nx_pause(0.016)
cur_percent = cur_percent + 0.016
end]]
--actor:SetBlendActionPause("PrimaryMelee_C_Slow_MSA_sq", true)
end
--Velocity changes 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
function on_rhand_ik_open(self, bOpen)
nx_set_custom(self, "rhand_ik", bOpen)
--[[local actor = self.Actor
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
actor:SetAnimTreeValue(action_index, "bRhand", bOpen)
end
end
end--]]
end
function set_tree_state(actor, state_name, enable)
if not nx_is_valid(actor) then
return
end
-- Set state machine variables
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
actor:SetAnimTreeValue(action_index, state_name, enable)
end
end
end
-- Toggle ragdoll
function toggle_ragdoll(owner, key_name)
local actor = character_get_actor(owner)
if not nx_is_valid(actor) then
return
end
nx_set_value("default_actor", actor)
local mesh_component = owner.MeshComponent
if not nx_is_valid(mesh_component) then
return
end
-- Record mesh_component
actor.mesh_component = mesh_component
local enable_ragdoll = not mesh_component.SimulatePhysics
if (enable_ragdoll) then
mesh_component.SimulatePhysics = enable_ragdoll
set_tree_state(actor, "IsDead", true)
else
local capsule = owner.CapsuleComponent
if nx_is_valid(capsule) then
-- Record capsule
actor.capsule = capsule
end
-- Save snapshot
actor:SavePoseSnapshot("finalpose")
mesh_component.SimulatePhysics = enable_ragdoll
-- Notify blend snapshot
set_tree_state(actor, "IsStopped", true)
-- Play get up action
actor:BlendAction("GetUp_Back_montage", false, false, true, 1.0)
set_tree_state(actor, "IsDead", false)
-- Pause two frames
nx_pause(0)
nx_pause(0)
-- Notify stop blend snapshot
set_tree_state(actor, "IsStopped", false)
end
end
function tick(component, delta_time)
update_capsule_location()
end
-- Update capsule location in the ragdoll state
function update_capsule_location()
local actor = nx_value("default_actor")
if not nx_is_valid(actor) then
return
end
if not nx_find_custom(actor, "mesh_component") then
return
end
local mesh_component = actor.mesh_component
if not nx_is_valid(mesh_component) then
return
end
if mesh_component.SimulatePhysics then
if not nx_find_custom(actor, "capsule") then
return
end
local capsule = actor.capsule
if not nx_is_valid(capsule) then
return
end
local x, y, z = actor:GetNodeWorldPosition("pelvis")
capsule:SetPosition(x, capsule.PositionY, z)
end
end
public_attr.lua
--Explorer: global variables
MODEL_PATH = "mdl\\"
MODEL_PATH_DX9 = ""
ACTOR_PATH = "ini\\actor\\"
ACTOR_PATH_DX9 = "ini\\actor\\"
LIGHT_PATH = "ini\\light\\"
LIGHT_PATH_DX9 = "ini\\light\\"
EFFECT_PATH = "ini\\effect\\"
PARTICLE_PATH = "ini\\particle\\"
SOUND_PATH = "snd\\"
REVERB_PATH = "reverb\\"
TRIGGER_PATH = "ini\\trigger\\"
PROBE_PATH = "ini\\light_probe\\"
VOLUME_FOG_PATH = "ini\\volume_fog\\"
MATERIAL_PATH = ""
--Resource type definitions
TYPE_MODEL = "model"
TYPE_ACTOR = "actor"
TYPE_LIGHT = "light"
TYPE_EFFECT = "effect"
TYPE_PARTICLE = "particle"
TYPE_SOUND = "sound"
TYPE_REVERB = "reverb"
TYPE_TRIGGER = "trigger"
TYPE_PROBE = "light_probe"
TYPE_VOLUME_FOG = "volume_fog"
TYPE_DECAL = "decal"
TYPE_GROUP = "group"
TYPE_RIPPLE = "ripple"
TYPE_SNOW = "snow"
TYPE_RAIN = "rainlayer"
TYPE_UI3D = "ui3d"
--Animation resource types
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
TYPE_LIST = {
"model",
"actor",
"light",
"effect",
"particle",
"decal",
"sound",
"reverb",
"trigger",
"light_probe",
"volume_fog",
"ripple",
"snow",
"rainlayer",
}
FORM_TREE_BROWSER = "common_form\\form_tree_browser"
SEARCH_PATH = "ini\\common_form\\form_tree_browser\\"
SEARCH_CONFIG = SEARCH_PATH .. "form_put_visual.ini"
SEARCH_CONFIG_TEMP = "cache\\common_form\\form_tree_browser\\form_put_visual_temp.ini"
GROUP_CONFIG = "ter\\visual_group.ini"
--Get visual_put file
function get_search_file()
local ini = nx_create("IniDocument")
ini.FileName = nx_resource_path() .. SEARCH_CONFIG
if not ini:LoadFromFile() then
nx_destroy(ini)
return 0
end
local file = ini:ReadString("SEARCH_CONFIG", "search_file", "form_put_visual.ini")
nx_destroy(ini)
return nx_resource_path() .. SEARCH_PATH .. file, file
end
Bind another Motion Warping Animation Script for the character as described above.
Script Two
Reference script:
motion_warping_character_animation.lua
--Script template
--------------------------
--Local utility function--
--------------------------
require("public_attr")
require("scene_utils")
local function get_terrain(pawn)
if nx_is_valid(pawn) then
local lscene = pawn.Scene
if nx_is_valid(lscene) then
return scene_get_engine_terrain(lscene)
end
end
return nx_null()
end
local function get_movement_component(pawn)
if nx_is_valid(pawn) then
return pawn:GetMovementComponentID()
end
return nx_null()
end
local function is_moving(pawn)
if nx_is_valid(pawn) then
local movement = get_movement_component(pawn)
if nx_is_valid(movement) then
local speed = nx_function("ext_vector_length", pawn.VelocityX, pawn.VelocityY, pawn.VelocityZ)
if speed > 0.0 and movement:IsMovingOnGround() then
return true
end
end
end
return false
end
local function get_speed(pawn)
if nx_is_valid(pawn) then
local movement = get_movement_component(pawn)
if nx_is_valid(movement) then
local speed = nx_function("ext_vector_length", pawn.VelocityX, pawn.VelocityY, pawn.VelocityZ)
return speed
end
end
return 0.0
end
local function get_current_jump_count(pawn)
if nx_is_valid(pawn) then
return pawn.JumpCurrentCount
end
return 0
end
local function is_jumping(pawn)
if nx_is_valid(pawn) then
local movement = get_movement_component(pawn)
if nx_is_valid(movement) then
if movement:IsFalling() then
return true
end
end
end
return false
end
local function is_accelerating(pawn)
if nx_is_valid(pawn) then
local movement = get_movement_component(pawn)
if nx_is_valid(movement) then
if math.abs(movement.CurrentAccelerationX) > 0.0
or math.abs(movement.CurrentAccelerationY) > 0.0
or math.abs(movement.CurrentAccelerationZ) > 0.0 then
return true
end
end
end
return false
end
local function get_anim_tree_index(role)
if role:GetBlendActionCount() > 0 then
local action_name = role:GetBlendActionName(0)
local action_type = role:GetActionType(action_name)
if action_type == AT_ANIMTREE then
return role:GetActionIndex(action_name)
end
end
return -1
end
local function get_foot_ik_weight(role)
local foot_ik_weight = -1.0
local foot_ik_id = role:GetCurveIDByNameInSkeleton("foot_ik")
if foot_ik_id >= 0 then
foot_ik_weight = role:GetCurveValue(foot_ik_id)
end
return foot_ik_weight
end
function transition_callback_IdleToJogStart(pawn, role, action_index, machine_index, prev_state, next_state)
if is_accelerating(pawn) and not is_jumping(pawn) and is_moving(pawn) then
return true
else
return false
end
end
function transition_callback_JogStartToJogStop(pawn, role, action_index, machine_index, prev_state, next_state)
if get_speed(pawn) < 1.0 then
return true
else
return false
end
end
function transition_callback_IdleToRun(pawn, role, action_index, machine_index, prev_state, next_state)
if get_speed(pawn) > 50.0 then
return true
else
return false
end
end
function transition_callback_RunToJogStop(pawn, role, action_index, machine_index, prev_state, next_state)
if not is_accelerating(pawn) then
return true
else
return false
end
end
function transition_callback_JogStopToIdle(pawn, role, action_index, machine_index, prev_state, next_state)
if is_accelerating(pawn) then
return true
else
return false
end
end
function transition_callback_JogStopToJogStart(pawn, role, action_index, machine_index, prev_state, next_state)
if is_accelerating(pawn) then
return true
else
return false
end
end
function transition_callback_IdleOrJogsToJumps(pawn, role, action_index, machine_index, prev_state, next_state)
if is_jumping(pawn) then
return true
else
return false
end
end
function transition_callback_JumpsToJumpLand(pawn, role, action_index, machine_index, prev_state, next_state)
if not is_jumping(pawn) then
return true
else
return false
end
end
function transition_callback_JumpsToIdleOrJogs(pawn, role, action_index, machine_index, prev_state, next_state)
if is_jumping(pawn) then
return true
else
return false
end
end
function transition_callback_JumpToSecondJump(pawn, role, action_index, machine_index, prev_state, next_state)
if is_jumping(pawn) and get_current_jump_count(pawn) > 1 then
return true
else
return false
end
end
function transition_callback_SecondJumpToThirdJump(pawn, role, action_index, machine_index, prev_state, next_state)
if is_jumping(pawn) and get_current_jump_count(pawn) > 2 then
return true
else
return false
end
end
function transition_callback_IdleToMultiDiectionMove(pawn, role, action_index, machine_index, prev_state, next_state)
if is_moving(pawn) then
return true
else
return false
end
end
function transition_callback_MultiDiectionMoveToIdle(pawn, role, action_index, machine_index, prev_state, next_state)
if not is_moving(pawn) then
return true
else
return false
end
end
transition_event_callback =
{
["IdleToJogStart"] = transition_callback_IdleToJogStart,
["JogStartToJogStop"] = transition_callback_JogStartToJogStop,
["IdleToRun"] = transition_callback_IdleToRun,
["RunToJogStop"] = transition_callback_RunToJogStop,
["JogStopToIdle"] = transition_callback_JogStopToIdle,
["JogStopToJogStart"] = transition_callback_JogStopToJogStart,
["IdleOrJogsToJumps"] = transition_callback_IdleOrJogsToJumps,
["JumpsToJumpLand"] = transition_callback_JumpsToJumpLand,
["JumpsToIdleOrJogs"] = transition_callback_JumpsToIdleOrJogs,
["JumpToSecondJump"] = transition_callback_JumpToSecondJump,
["SecondJumpToThirdJump"] = transition_callback_SecondJumpToThirdJump,
["IdleToMultiDiectionMove"] = transition_callback_IdleToMultiDiectionMove,
["MultiDiectionMoveToIdle"] = transition_callback_MultiDiectionMoveToIdle,
}
function on_transition_event(mesh_comp, action_index, machine_index, prev_state, next_state, callback_name)
local pawn = mesh_comp.GameObjectOwner
local role = mesh_comp.VisBase
if not nx_is_valid(pawn) or not nx_is_valid(role) then
return false
end
local callback = transition_event_callback[callback_name]
if callback then
return callback(pawn, role, action_index, machine_index, prev_state, next_state)
end
return false
end
function state_machine_state_callback_EnterState(pawn, role, action_index, machine_index, state_index)
if nx_is_valid(role) then
nx_log("state_machine_state_callback_EnterState event, role: " .. nx_string(role) .. " action_name: " .. role:GetActionName(action_index) .. ", machine index: " .. role:GetAnimTreeStateMachineName(action_index, machine_index) .. ", state name: " .. role:GetAnimTreeStateMachineStateName(action_index, machine_index, state_index))
end
end
function state_machine_state_callback_LeaveState(pawn, role, action_index, machine_index, state_index)
if nx_is_valid(role) then
nx_log("state_machine_state_callback_LeaveState event, role: " .. nx_string(role) .. " action_name: " .. role:GetActionName(action_index) .. ", machine index: " .. role:GetAnimTreeStateMachineName(action_index, machine_index) .. ", state name: " .. role:GetAnimTreeStateMachineStateName(action_index, machine_index, state_index))
end
end
function state_machine_state_callback_FullyBlendState(pawn, role, action_index, machine_index, state_index)
if nx_is_valid(role) then
nx_log("state_machine_state_callback_FullyBlendState event, role: " .. nx_string(role) .. " action_name: " .. role:GetActionName(action_index) .. ", machine index: " .. role:GetAnimTreeStateMachineName(action_index, machine_index) .. ", state name: " .. role:GetAnimTreeStateMachineStateName(action_index, machine_index, state_index))
end
end
state_machine_state_event_callback =
{
["start_state"] = state_machine_state_callback_EnterState,
["end_state"] = state_machine_state_callback_LeaveState,
["fully_blend_state"] = state_machine_state_callback_FullyBlendState,
}
function on_state_machine_state_event(mesh_comp, action_index, machine_index, state_index, callback_name)
local pawn = mesh_comp.GameObjectOwner
local role = mesh_comp.VisBase
if not nx_is_valid(pawn) or not nx_is_valid(role) then
return false
end
local callback = state_machine_state_event_callback[callback_name]
if callback then
return callback(pawn, role, action_index, machine_index, state_index)
end
return
end
function has_fullbody_action(pawn)
return false
end
function animtree_callback_ShouldUseLayerBlend(pawn, role)
if not has_fullbody_action(pawn) and is_moving(pawn) then
return true
end
return false
end
function animtree_callback_CalcLeanAngle(pawn, role)
return 0.0
end
function animtree_callback_CalcSlopeAngle(pawn, role)
return 0.0
end
function animtree_callback_ShouldUseRHand(pawn, role)
if nx_is_valid(pawn) then
return nx_custom(pawn, "rhand_ik")
end
return false
end
function animtree_callback_GetPlayerWeaponState(pawn, role)
return 0
end
function animtree_callback_GetPlayerFightState(pawn, role)
return 0
end
function animtree_callback_IsPlayerMoving(pawn, role)
if is_moving(pawn) then
return true
end
return false
end
--Animation tree right foot IK node callback
function animtree_callback_RRootIK(pawn, role)
local terrain = get_terrain(pawn)
if not nx_is_valid(terrain) then
return false
end
local foot_ik_weight = get_foot_ik_weight(role)
if foot_ik_weight < 0.0 then
--Foot IK weight less than 0.0, close Foot IK
return false
end
--Right foot node position in model space
local obj_pos_x, obj_pos_y, obj_pos_z = role:GetNodeObjectPosition("foot_r")
local foot_rel_height = obj_pos_y
--Right foot node position in world space
local position_x = 0.0
local position_y = 0.0
local position_z = 0.0
position_x, position_y, position_z = role:GetNodeWorldPosition("foot_r")
--Ground height in world space
local groundheight = terrain:GetGroundHeight(position_x,position_z)
--Right foot node adjust to the ground in world space
local strnewpos = nx_string(position_x) .. "," .. nx_string(groundheight+foot_rel_height) .. "," .. nx_string(position_z)
if (position_y-foot_rel_height) < groundheight or ((position_y-foot_rel_height)-groundheight) > 0.05 then
--Right foot node lower than the ground or 0.05 meter above the ground, set right foot IK parameters
local action_index = get_anim_tree_index(role)
if action_index >= 0 then
role:SetAnimTreeValue(action_index, "footrpos", strnewpos)
role:SetAnimTreeValue(action_index, "foot_ik_weight", foot_ik_weight)
return true
end
return false
else
return false
end
end
function animtree_callback_LRootIK(pawn, role)
local terrain = get_terrain(pawn)
if not nx_is_valid(terrain) then
return false
end
local foot_ik_weight = get_foot_ik_weight(role)
if foot_ik_weight < 0.0 then
return false
end
local obj_pos_x, obj_pos_y, obj_pos_z = role:GetNodeObjectPosition("foot_l")
local foot_rel_height = obj_pos_y
local position_x = 0.0
local position_y = 0.0
local position_z = 0.0
position_x, position_y, position_z = role:GetNodeWorldPosition("foot_l")
local groundheight = terrain:GetGroundHeight(position_x,position_z)
local strnewpos = nx_string(position_x) .. "," .. nx_string(groundheight+foot_rel_height) .. "," .. nx_string(position_z)
if (position_y-foot_rel_height) < groundheight or ((position_y-foot_rel_height)-groundheight) > 0.05 then
local action_index = get_anim_tree_index(role)
if action_index >= 0 then
role:SetAnimTreeValue(action_index, "footlpos", strnewpos)
role:SetAnimTreeValue(action_index, "foot_ik_weight", foot_ik_weight)
return true
end
return false
else
return false
end
end
animtree_event_callback =
{
["ShouldUseLayerBlend"] = animtree_callback_ShouldUseLayerBlend,
["CalcLeanAngle"] = animtree_callback_CalcLeanAngle,
["CalcSlopeAngle"] = animtree_callback_CalcSlopeAngle,
["ShouldUseRHand"] = animtree_callback_ShouldUseRHand,
["RRootIK"] = animtree_callback_RRootIK,
["LRootIK"] = animtree_callback_LRootIK,
["GetPlayerWeaponState"] = animtree_callback_GetPlayerWeaponState,
["GetPlayerFightState"] = animtree_callback_GetPlayerFightState,
["IsPlayerMovingHorizontal"] = animtree_callback_IsPlayerMoving,
}
function on_animtree_event(mesh_comp, action_index, callback_name)
local callback = animtree_event_callback[callback_name]
local pawn = mesh_comp.GameObjectOwner
local role = mesh_comp.VisBase
if not nx_is_valid(pawn) or not nx_is_valid(role) then
return false
end
local callback = transition_event_callback[callback_name]
if callback then
return callback(pawn, role)
end
return false
end
local function get_current_time_scale(role)
local curve_id = role:GetCurveIDByNameInSkeleton("slowmo_time_scale")
--local curve_id = nx_custom(role, "slowmo_time_scale")
if curve_id < 0 then
--nx_log("invalid test_curve_id: " .. curve_id)
return
end
local curve_value = role:GetCurveValue(curve_id)
return curve_value
end
local function begin_bullut_time(pawn)
if nx_is_valid(pawn) then
local time_scale = 0.05
local world = nx_value("world")
world.GlobalTimeSpeed = time_scale
pawn.camera_rotated = true
end
end
local function end_bullut_time(pawn)
if nx_is_valid(pawn) then
local world = nx_value("world")
world.GlobalTimeSpeed = 1
pawn.camera_rotated = false
end
end
function on_action_event(mesh_comp, action_name, event_name, frame, info)
local pawn = mesh_comp.GameObjectOwner
local role = mesh_comp.VisBase
if not nx_is_valid(pawn) or not nx_is_valid(role) then
return false
end
if event_name == "JumpOver" then
pawn.JumpAgainLock = true
elseif event_name == "JumpLand" then
pawn.JumpAgainLock = false
elseif event_name == "AttackHit" then
local terrain = nx_value("terrain")
local npc_story = nx_value("npc_story")
if nx_is_valid(terrain) then
local visual_table = terrain:GetRangeVisualList(role.PositionX, role.PositionZ, 2.0)
for k,v in pairs(visual_table) do
if nx_string(npc_story) == nx_string(v) then
npc_story.be_hurted = true
end
end
end
elseif event_name == "Melee_Start" then
begin_bullut_time(pawn)
elseif event_name == "Melee_End" then
end_bullut_time(pawn)
else
--nx_log("event_name = " .. event_name)
end
if event_name == "TRIGGER_ACTION_END" then
local movement_comp = pawn.CharacterMovement
--if movement_comp.MovementMode == nx_enum("MOVEMENT_MODE::Flying") then
local movement_mode = nx_enum("MOVEMENT_MODE::Walking")
movement_comp:SetMovementModeWithCustom(movement_mode, 0)
--end
end
end
function anim_callee_init(mesh_comp)
nx_callback(mesh_comp, "on_transition_event", "on_transition_event")
nx_callback(mesh_comp, "on_animtree_event", "on_animtree_event")
nx_callback(mesh_comp, "on_action_event", "on_action_event")
nx_callback(mesh_comp, "on_state_machine_state_event", "on_state_machine_state_event")
end
---------------------
--Default callbacks--
---------------------
--Use this for initialization
function on_begin_play(component)
local owner = component.GameObjectOwner
if nx_is_valid(owner) then
local mesh_comp = owner.MeshComponent
if nx_is_valid(mesh_comp) then
mesh_comp.ActorCalleeType = "CComponentActorCalleeScript"
nx_bind_script(mesh_comp, nx_current(), "anim_callee_init")
end
end
end
--Use this for release
function on_end_play(component)
end
scene_utils.lua
--scene utils script
function scene_get_engine_terrain(lscene)
local level = lscene.PersistentLevel
if nx_is_valid(level) then
local lterrain = level:GetGameObjectByType("fx_component_terrain.LTerrain")
if nx_is_valid(lterrain) then
return lterrain.EngineTerrain
end
end
return nx_null()
end
PIE
Click the Play In Editor button to view the effect.