extends CharacterBody3D const TARGET_FPS: float = 60.0 const BASE_SPEED: float = 5.0 const BOOST_FORCE: float = 20.0 const JUMP_FORCE: float = 8.0 const FRICTION: float = 0.3 const AIR_DRAG: float = 0.03 const INPUT_SENSITIVITY: float = 0.7 const TURN_SENSITIVITY: float = 0.04 # Get the gravity from the project settings to be synced with RigidBody nodes. var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") @onready var camera_root: ThirdPersonCamera = $CameraRoot @onready var mesh: Node3D = $Mesh @onready var animation_tree: AnimationTree = $Mesh/Mech/AnimationTree @onready var animation_player: AnimationPlayer = $AnimationPlayer @onready var _crosshair: Crosshair = get_tree().get_first_node_in_group("CrosshairGroup") @onready var _overlay: OverlayEffects = get_tree().get_first_node_in_group("OverlayEffectsGroup") func is_boosting() -> bool: return animation_tree["parameters/boost/active"] func _physics_process(delta: float) -> void: var delta_factor: float = delta * TARGET_FPS if is_boosting(): _crosshair.jostle(Vector2(randf_range(-3, 3), randf_range(-3, 3))) else: # Add the gravity. if not is_on_floor(): velocity.y -= gravity * delta animation_tree["parameters/jump_state/transition_request"] = "in_air" animation_tree["parameters/anim_state/transition_request"] = "air" elif animation_tree["parameters/anim_state/current_state"] == "air": # If not on floor but still in jump state, transition back to landing animation_tree["parameters/jump_state/transition_request"] = "end" # Handle jump. if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = JUMP_FORCE if is_boosting(): # Jump cancels boost animation_tree["parameters/boost/request"] = 2 # Abort # Get the input direction and handle the movement/deceleration. var input_dir: Vector2 = Input.get_vector("left", "right", "forward", "backward") if input_dir: # Transform movement based on camera angle var movement: Vector3 = ( ( camera_root.global_transform.basis * Vector3(input_dir.x, 0.0, input_dir.y) * Vector3(-1.0, 0.0, -1.0) ) . normalized() ) if not is_boosting(): velocity.x = lerpf( velocity.x, movement.x * BASE_SPEED, delta_factor * INPUT_SENSITIVITY ) velocity.z = lerpf( velocity.z, movement.z * BASE_SPEED, delta_factor * INPUT_SENSITIVITY ) if Input.is_action_just_pressed("boost"): velocity.x += movement.x * BOOST_FORCE velocity.z += movement.z * BOOST_FORCE velocity.y = 0.0 mesh.rotation.y = atan2(velocity.x, velocity.z) animation_tree["parameters/boost/request"] = 1 _overlay.play_boost() animation_player.play("boost_fov") animation_player.seek(0) # Slowly turn mesh towards camera vector when moving on ground if is_on_floor(): mesh.rotation.y = lerp_angle( mesh.rotation.y, camera_root.rotation.y, delta_factor * TURN_SENSITIVITY ) animation_tree["parameters/walk_space/blend_position"] = lerp( animation_tree["parameters/walk_space/blend_position"], input_dir, delta_factor * TURN_SENSITIVITY ) var drag: float = FRICTION if (is_on_floor() and not is_boosting()) else AIR_DRAG velocity.x = lerpf(velocity.x, 0.0, delta_factor * drag) velocity.z = lerpf(velocity.z, 0.0, delta_factor * drag) move_and_slide() func _on_mech_stomp() -> void: _crosshair.jostle(Vector2(randf_range(-1, 1), randf_range(-1, 5))) # camera_root.jostle(randf_range(-2, 2))