Movement And Input Examples¶
These scripts cover player-owned movement: air steering, jump height, wall jumps, speed tiers, rolling momentum, and charge release. They are adapted from several Test chronicle prototypes and cleaned up into reusable patterns.
Simple Air Steering¶
Attach this to a jump or aerial attack when you want the original controller to keep handling landing and collisions.
return function()
local player = game.this_player
if player == nil then
return true
end
-- Absolute left/right input is used for air steering because the player
-- expects left to move left even if the character is facing right.
local max_air_speed = 130
local acceleration = 7
if player.is_in_the_air then
if player.is_holding_right and player.impulse_x < max_air_speed then
player.impulse_x = player.impulse_x + acceleration
-- The previous-frame flag identifies the first held frame after a tap.
-- Snap to the starting speed once, then clear the old input state so
-- the snap is not applied again next frame.
if player.was_previously_holding_right then
player.impulse_x = max_air_speed
player.previous_held_buttons = 0
end
elseif player.is_holding_left and player.impulse_x > -max_air_speed then
player.impulse_x = player.impulse_x - acceleration
if player.was_previously_holding_left then
player.impulse_x = -max_air_speed
player.previous_held_buttons = 0
end
end
end
-- Only steering was changed. Let the jump controller keep running.
return true
end
Walk And Run Air-Control Profiles¶
Use tiny setup scripts on different jump-start animations, then share one air-control script across all aerial states.
Walk-jump setup:
return function()
local player = game.this_player
if player ~= nil then
-- Profile 1 means normal walk-jump steering.
player.custom_value_1 = 1
end
return true
end
Run-jump setup:
return function()
local player = game.this_player
if player ~= nil then
-- Profile 2 means faster run-jump steering.
player.custom_value_1 = 2
end
return true
end
Shared aerial script:
return function()
local player = game.this_player
if player == nil then
return true
end
local initial_velocity = 50
local max_velocity = 110
local acceleration = 16
if player.custom_value_1 == 2 then
initial_velocity = 100
max_velocity = 190
acceleration = 20
end
if player.is_in_the_air then
if player.is_holding_right and player.impulse_x < max_velocity then
player.impulse_x = player.impulse_x + acceleration
if player.was_previously_holding_right then
player.impulse_x = initial_velocity
player.previous_held_buttons = 0
end
elseif player.is_holding_left and player.impulse_x > -max_velocity then
player.impulse_x = player.impulse_x - acceleration
if player.was_previously_holding_left then
player.impulse_x = -initial_velocity
player.previous_held_buttons = 0
end
end
else
-- Grounded direction changes update facing and reset to normal profile.
if player.is_holding_right and not player.is_facing_right then
player.is_facing_right = true
player.custom_value_1 = 1
elseif player.is_holding_left and player.is_facing_right then
player.is_facing_right = false
player.custom_value_1 = 1
end
end
return true
end
Variable Jump Height¶
This script shortens upward momentum when the player releases jump early. It can be layered into an existing jump script that still returns true.
return function()
local player = game.this_player
if player == nil then
return true
end
local small_jump_force = 262
-- Negative impulse_y means the character is rising.
-- If the jump button is no longer held, clamp upward speed.
if player.impulse_y < -small_jump_force and not player.is_holding_button_c then
player.impulse_y = -small_jump_force
end
return true
end
Full Manual Air Physics¶
Use this when an aerial attack owns movement and landing. It handles hits, gravity, horizontal steering, wall stopping, and landing.
return function()
local player = game.this_player
if player == nil then
return true
end
local gravity = 14
local air_acceleration = 6
local top_speed = 393
-- Since this script replaces the controller, it must check interruptions.
if player.check_being_hit() then
return false
end
-- Let active hitboxes connect. If the move hits something, rebound both
-- horizontal and vertical impulses to produce a bounce-like feel.
if player.check_hitting_entities() then
player.impulse_x = -player.impulse_x
player.impulse_y = -player.impulse_y
end
player.impulse_y = player.impulse_y + gravity
if player.is_holding_left then
player.impulse_x = player.impulse_x - air_acceleration
elseif player.is_holding_right then
player.impulse_x = player.impulse_x + air_acceleration
end
if player.impulse_x > top_speed then
player.impulse_x = top_speed
elseif player.impulse_x < -top_speed then
player.impulse_x = -top_speed
end
player.velocity_x = player.impulse_x
player.position_offset_y = player.position_offset_y + player.impulse_y
if player.is_pressing_against_walls(true) then
player.impulse_x = 0
end
if player.impulse_y > 0 and player.position_offset_y >= 0 then
player.impulse_y = 0
player.position_offset_y = 0
player.set_animation(0)
player.object_mode = 2
end
return false
end
Wall Jump¶
This script freezes briefly on the wall, flips facing once, then lets the player launch away.
return function()
local player = game.this_player
if player == nil then
return true
end
local wall_freeze_frames = 8
if player.current_animation_frame < wall_freeze_frames then
if player.current_animation_frame == 0 then
-- Flip once at the start so forward now points away from the wall.
player.is_facing_right = not player.is_facing_right
end
-- Stop the normal controller while the character clings to the wall.
return false
end
local away_speed = 131
if player.is_holding_forward then
away_speed = 196
elseif player.is_holding_backward then
away_speed = 65
end
player.velocity_x = away_speed * player.direction_signal
player.impulse_y = player.impulse_y + 13
player.position_offset_y = player.position_offset_y + player.impulse_y
if player.pressed_button_c and player.is_pressing_against_walls(true) then
player.set_animation(56)
player.impulse_y = -307
player.play_sound("My Pack", "wall-jump", 2)
end
return false
end
Rolling Momentum Controller¶
This is a fuller player controller: acceleration, deceleration, depth movement, wall checks, jump, pickup, roll transition, and animation speed control.
local acceleration_x = 3
local deceleration_x = 32
local friction_x = 3
local top_speed_x = 393
local acceleration_z = 1
local deceleration_z = 17
local friction_z = 1
local top_speed_z = 196
local breaking_speed = 262
local rolling_minimum_speed = 32
local jump_force = 426
local function abs(value)
if value < 0 then
return -value
end
return value
end
local function stop_all_motion(player)
player.velocity_x = 0
player.velocity_y = 0
player.impulse_x = 0
player.impulse_z = 0
end
return function()
local player = game.this_player
if player == nil then
return true
end
if player.check_being_hit_or_grabbed() then
return false
end
local is_breaking = false
if player.is_holding_left then
if player.impulse_x > 0 then
is_breaking = true
player.impulse_x = player.impulse_x - deceleration_x
if player.impulse_x < 0 then
player.impulse_x = 0
end
elseif player.impulse_x > -top_speed_x then
player.impulse_x = player.impulse_x - acceleration_x
end
elseif player.is_holding_right then
if player.impulse_x < 0 then
is_breaking = true
player.impulse_x = player.impulse_x + deceleration_x
if player.impulse_x > 0 then
player.impulse_x = 0
end
elseif player.impulse_x < top_speed_x then
player.impulse_x = player.impulse_x + acceleration_x
end
else
if player.impulse_x > 0 then
player.impulse_x = player.impulse_x - friction_x
if player.impulse_x < 0 then
player.impulse_x = 0
end
elseif player.impulse_x < 0 then
player.impulse_x = player.impulse_x + friction_x
if player.impulse_x > 0 then
player.impulse_x = 0
end
end
end
if player.impulse_x > top_speed_x then
player.impulse_x = top_speed_x
elseif player.impulse_x < -top_speed_x then
player.impulse_x = -top_speed_x
end
-- Depth movement uses impulse_z and velocity_y.
if player.is_holding_up then
player.impulse_z = player.impulse_z - acceleration_z
elseif player.is_holding_down then
player.impulse_z = player.impulse_z + acceleration_z
else
if player.impulse_z > 0 then
player.impulse_z = player.impulse_z - friction_z
if player.impulse_z < 0 then
player.impulse_z = 0
end
elseif player.impulse_z < 0 then
player.impulse_z = player.impulse_z + friction_z
if player.impulse_z > 0 then
player.impulse_z = 0
end
end
end
if player.impulse_z > top_speed_z then
player.impulse_z = top_speed_z
elseif player.impulse_z < -top_speed_z then
player.impulse_z = -top_speed_z
end
player.velocity_x = player.impulse_x
player.velocity_y = player.impulse_z
local total_speed = abs(player.impulse_x) + abs(player.impulse_z)
if player.is_pressing_against_walls(true) then
if player.is_holding_left or player.is_holding_right then
player.set_animation(44)
else
player.impulse_x = 0
end
total_speed = 0
end
if player.pressed_button_b and total_speed < rolling_minimum_speed and player.check_pick_up_item() then
player.set_animation(14)
player.object_mode = 44
stop_all_motion(player)
return false
end
if player.pressed_button_c or player.pressed_button_a then
player.set_animation(2)
player.impulse_y = -jump_force
player.play_sound("My Pack", "jump", 2)
return false
end
if player.is_holding_button_b then
if total_speed < rolling_minimum_speed then
player.set_animation(46)
stop_all_motion(player)
else
player.set_animation(48)
player.play_sound("My Pack", "roll", 2)
end
return false
end
if player.impulse_x ~= 0 then
player.is_facing_right = player.impulse_x > 0
end
if is_breaking and total_speed > breaking_speed then
player.set_animation(49)
elseif total_speed >= top_speed_x then
player.set_animation(40)
elseif total_speed > 0 then
player.set_animation(1)
-- Slower frame advance at low speed. Animation frames normally advance
-- after scripts, so rewinding by one frame holds the frame longer.
local frame_duration = 9 - (total_speed / 65)
player.custom_value_1 = player.custom_value_1 + 1
if player.custom_value_1 >= frame_duration then
player.custom_value_1 = 0
else
player.current_animation_frame = player.current_animation_frame - 1
end
else
player.set_animation(0)
end
return false
end
Charge And Release Dash¶
Hold one button to charge, tap another button to increase charge, release to dash.
return function()
local player = game.this_player
if player == nil then
return true
end
local max_dash_speed = 786
if player.check_being_hit() then
return false
end
-- Gradually bleed charge so repeated taps matter.
player.custom_value_2 = player.custom_value_2 - player.custom_value_2 / 32
if player.pressed_button_c then
player.custom_value_2 = player.custom_value_2 + 2
local pitch_percentage = 100 + player.custom_value_2 * 2
if pitch_percentage > 170 then
pitch_percentage = 170
end
player.play_sound("My Pack", "charge-tick", 2, pitch_percentage)
player.jump_spam_timer = 0
player.special_spam_timer = 0
end
if not player.is_holding_button_b then
player.set_animation(48)
local dash_speed = 8 + player.custom_value_2 / 2
dash_speed = (dash_speed * 255 * 255) / 1000
if dash_speed > max_dash_speed then
dash_speed = max_dash_speed
end
player.impulse_x = dash_speed * player.direction_signal
player.custom_value_2 = 0
player.jump_spam_timer = 0
player.special_spam_timer = 0
player.play_sound("My Pack", "dash-release", 2)
end
return false
end