209 lines
8.7 KiB
GDScript
209 lines
8.7 KiB
GDScript
class_name RootMovementHandler
|
|
extends Node
|
|
|
|
@export_category("Nodes")
|
|
@export var character : Character3D
|
|
@export var player_camera : PlayerCamera3D
|
|
@export var slice_ring_spawner : SliceRingSpawner
|
|
|
|
@export_category("Input Settings")
|
|
@export var _walk_reaction_speed : float = 1.0
|
|
@export var _walk_turn_speed : float = 10.0
|
|
|
|
@export_category("Input Resources")
|
|
@export var _player_movement_standard : InputResource
|
|
@export var _player_movement_slice_time : InputResource
|
|
|
|
@export_category("Physics Constants")
|
|
@export_range(0.0, 10.0, 0.01) var _gravity_multiplier : float = 1.0
|
|
@export_range(0.0, 100.0, 0.1) var _terminal_velocity : float = 35.0
|
|
@export_range(0.0, 10.0, 0.05) var _air_acceleration : float = 6.0
|
|
@export_range(0.0, 20.0, 0.05) var _max_air_velocity : float = 6.0
|
|
@export_range(0.0, 10.0, 0.05) var _drag : float = 1.0
|
|
|
|
@export_range(0.0, 50.0, 0.05) var _slice_time_aim_blend_speed = 30.0
|
|
|
|
@onready var _state_chart : StateChart = $StandardMovementChart
|
|
|
|
var _target_movement_vector : Vector2 = Vector2.ZERO
|
|
var _current_movement_speed : float = 0.0
|
|
var _lateral_motion_accumulator : Vector3 = Vector3.ZERO
|
|
var _gravity : float = 0.0
|
|
|
|
var _slice_time_aim_blend : float = 0.0
|
|
var _slice_time_tilt : float = 0.5
|
|
var _slice_time_angle : float = 0.0
|
|
|
|
signal state_changed(StringName)
|
|
|
|
|
|
func _ready() -> void:
|
|
InputHandler.add_input_resource(self, _player_movement_standard)
|
|
|
|
character.character_skeleton.animation_tree.advance_expression_base_node = self.get_path()
|
|
|
|
|
|
func _on_movement_2d(_delta:float, _vector:Vector2) -> void:
|
|
_target_movement_vector = _vector
|
|
func _on_slice_time_rotate_2d(_delta:float, _vector:Vector2) -> void:
|
|
_target_movement_vector = _vector
|
|
|
|
func _on_jump_pressed() -> void:
|
|
pass
|
|
|
|
func _on_slice_time_pressed() -> void:
|
|
_state_chart.send_event("slice_time")
|
|
func _on_slice_time_released() -> void:
|
|
_state_chart.send_event("no_more_slice_time")
|
|
|
|
|
|
func _on_walking_state_entered() -> void:
|
|
_lateral_motion_accumulator = Vector3.ZERO
|
|
_gravity = 0.0
|
|
emit_signal("state_changed", "walk")
|
|
func _on_coyote_time_state_entered() -> void:
|
|
_on_walking_state_entered()
|
|
|
|
func _on_walking_state_processing(_delta:float) -> void:
|
|
_root_motion_walk_process(_delta)
|
|
_camera_relative_heading_process(_delta)
|
|
func _on_coyote_time_state_processing(_delta: float) -> void:
|
|
_on_walking_state_processing(_delta)
|
|
|
|
func _on_walking_state_physics_processing(_delta:float) -> void:
|
|
_lateral_movement_physics_process(_delta)
|
|
#_gravity_physics_process(_delta)
|
|
_move_character()
|
|
|
|
if !character.is_on_floor():
|
|
_state_chart.send_event("fall")
|
|
_gravity = 0.0
|
|
func _on_coyote_time_state_physics_processing(_delta: float) -> void:
|
|
_on_walking_state_physics_processing(_delta)
|
|
|
|
|
|
func _on_falling_state_entered() -> void:
|
|
_gravity = 0.0
|
|
_lateral_motion_accumulator = character.velocity
|
|
|
|
func _on_falling_state_processing(_delta:float) -> void:
|
|
_camera_relative_heading_process(_delta)
|
|
_physics_movement_accumulation_process(_delta)
|
|
|
|
func _on_falling_state_physics_processing(_delta:float) -> void:
|
|
_physics_movement_physics_process(_delta)
|
|
_gravity_physics_process(_delta)
|
|
_move_character()
|
|
|
|
if character.is_on_floor():
|
|
_state_chart.send_event("land")
|
|
|
|
|
|
func _on_landed_state_entered() -> void:
|
|
emit_signal("state_changed", "landed")
|
|
|
|
func _on_landed_state_physics_processing(_delta:float) -> void:
|
|
pass
|
|
|
|
|
|
func _on_slice_time_state_entered() -> void:
|
|
InputHandler.remove_input_resource(self, _player_movement_standard)
|
|
InputHandler.add_input_resource(self, _player_movement_slice_time)
|
|
_reset_all_movement()
|
|
character.character_skeleton.animation_tree["parameters/playback"].travel("slice_time")
|
|
_slice_time_tilt = 0.5
|
|
player_camera.state_chart.send_event("slice_time")
|
|
slice_ring_spawner.spawn(self, true);
|
|
func _on_slice_time_state_exited() -> void:
|
|
_reset_all_movement()
|
|
character.character_skeleton.animation_tree["parameters/playback"].travel("walk")
|
|
slice_ring_spawner.despawn()
|
|
InputHandler.remove_input_resource(self, _player_movement_slice_time)
|
|
InputHandler.add_input_resource(self, _player_movement_standard)
|
|
player_camera.state_chart.send_event("no_more_slice_time")
|
|
|
|
func _on_slice_time_state_processing(_delta:float) -> void:
|
|
_root_motion_accumulation_process(_delta)
|
|
character.character_skeleton.animation_tree["parameters/slice_time/slice_time_idle/slice_time_pitch/seek_request"] = _slice_time_tilt
|
|
#character.character_skeleton.animation_tree["parameters/slice_time_active/aim_angle/seek_request"] = 0.0
|
|
|
|
func _on_slice_time_state_physics_processing(_delta:float) -> void:
|
|
_lateral_movement_physics_process(_delta)
|
|
_move_character()
|
|
|
|
func _on_slice_time_aim_state_entered() -> void:
|
|
var _slice_time_input : InputAction = _player_movement_standard.find_input_action("slice_time")
|
|
if not _slice_time_input or not _slice_time_input.is_active():
|
|
_state_chart.send_event("no_more_slice_time")
|
|
|
|
func _on_slice_time_aim_state_processing(_delta:float) -> void:
|
|
_slice_time_tilt = clampf(_slice_time_tilt + (_target_movement_vector.y * _delta), 0.0, 1.0)
|
|
|
|
var old_blend_time : float = character.character_skeleton.animation_tree["parameters/slice_time/slice_time_idle/slice_time_angle_blend/blend_amount"] as float
|
|
character.character_skeleton.animation_tree["parameters/slice_time/slice_time_idle/slice_time_angle_blend/blend_amount"] = lerpf(old_blend_time, _slice_time_aim_blend, _slice_time_aim_blend_speed * _delta)
|
|
|
|
character.character_skeleton.animation_tree["parameters/slice_time/slice_time_idle/slice_time_angle/seek_request"] = _slice_time_angle
|
|
|
|
var old_slice_tilt : float = character.character_skeleton.animation_tree["parameters/slice_time/slice_time_idle/slice_time_pitch/seek_request"]
|
|
character.character_skeleton.animation_tree["parameters/slice_time/slice_time_idle/slice_time_pitch/seek_request"] = lerpf(old_slice_tilt, _slice_time_tilt, _slice_time_aim_blend_speed * _delta)
|
|
|
|
func _on_slice_time_aim_state_physics_processing(_delta:float) -> void:
|
|
_slice_time_rotation_physics_process(_delta)
|
|
|
|
func _on_slice_time_slice_state_entered() -> void:
|
|
pass
|
|
|
|
|
|
func _move_character():
|
|
character.move_and_slide()
|
|
|
|
func _root_motion_walk_process(_delta:float) -> void:
|
|
_current_movement_speed = lerpf(_current_movement_speed, _target_movement_vector.length_squared(), clampf(_delta * _walk_reaction_speed, 0.0, 1.0))
|
|
character.character_skeleton.animation_tree["parameters/walk/blend_position"] = _current_movement_speed
|
|
_root_motion_accumulation_process(_delta)
|
|
|
|
func _root_motion_accumulation_process(_delta:float) -> void:
|
|
var root_motion_speed : Vector3 = character.quaternion * character.character_skeleton.animation_tree.get_root_motion_position()
|
|
if root_motion_speed.length_squared() >= 0.00000000:
|
|
_lateral_motion_accumulator += root_motion_speed
|
|
|
|
func _camera_relative_heading_process(_delta:float):
|
|
var _target_movement_length_squared : float = _target_movement_vector.length_squared()
|
|
if _target_movement_length_squared >= 0.0001:
|
|
var _camera_relative_rotation : Quaternion = \
|
|
Quaternion(Vector3(_target_movement_vector.x, 0.0, _target_movement_vector.y).normalized(), \
|
|
Vector3(-player_camera.camera.global_basis.z.x, 0.0, -player_camera.camera.global_basis.z.z).normalized())
|
|
character.quaternion = character.quaternion.slerp(_camera_relative_rotation, clampf(_delta * _walk_turn_speed, 0.0, 1.0))
|
|
|
|
func _lateral_movement_physics_process(_delta:float) -> void:
|
|
character.velocity = _lateral_motion_accumulator / _delta
|
|
_lateral_motion_accumulator = Vector3.ZERO
|
|
|
|
func _physics_movement_accumulation_process(_delta:float) -> void:
|
|
_lateral_motion_accumulator = _lateral_motion_accumulator.lerp(character.basis.z * _max_air_velocity, _target_movement_vector.length_squared() * _delta * _air_acceleration)
|
|
|
|
func _physics_movement_physics_process(_delta:float) -> void:
|
|
_lateral_motion_accumulator = _lateral_motion_accumulator.lerp(Vector3.ZERO, _drag * _delta * (1.0 - _target_movement_vector.length_squared()))
|
|
character.velocity = _lateral_motion_accumulator
|
|
|
|
func _slice_time_rotation_physics_process(_delta:float) -> void:
|
|
character.rotate_y(-_target_movement_vector.x * _delta * 1.5)
|
|
|
|
func _on_slice_aim_update(_is_aiming:bool, _angle:float) -> void:
|
|
_slice_time_angle = 1.0 - (absf(_angle - PI) / TAU)
|
|
_slice_time_aim_blend = 1.0 if _is_aiming else 0.0
|
|
func _on_slice_executed() -> void:
|
|
_state_chart.send_event("slice")
|
|
|
|
func _gravity_physics_process(_delta:float) -> void:
|
|
_gravity = clamp(_gravity + (ProjectSettings.get_setting("physics/3d/default_gravity") * _gravity_multiplier * _delta), 0.0, _terminal_velocity)
|
|
character.velocity -= character.global_basis.y * _gravity
|
|
|
|
func _reset_all_movement() -> void:
|
|
_target_movement_vector = Vector2.ZERO
|
|
_current_movement_speed = 0.0
|
|
_lateral_motion_accumulator = Vector3.ZERO
|
|
_gravity = 0.0
|
|
|
|
character.character_skeleton.animation_tree["parameters/walk/blend_position"] = _current_movement_speed
|