extends CharacterBody3D const WALK_SPEED = 6.0 const CHARGE_SPEED = 18.0 const WALK_ACCEL = 14.0 const CHARGE_ACCEL = 40.0 # snaps to charge speed quickly — burst feel const DECELERATION = 12.0 const CHARGE_DECEL = 3.5 # momentum lingers after releasing shift const WALK_TURN_FAST = 8.0 # rad/s at rest const WALK_TURN_SLOW = 3.5 # rad/s at full walk speed const CHARGE_TURN = 0.55 # rad/s — ~31 deg/s, very wide committed arc const JUMP_VELOCITY = 4.5 @export var visual_turn_speed: float = 12.0 @onready var camera_pivot: Node3D = $SpringArmPivot @onready var cube_guy: Node3D = $bull_v10 var current_speed: float = 0.0 var facing_angle: float = 0.0 func _physics_process(delta: float) -> void: if not is_on_floor(): velocity += get_gravity() * delta if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = JUMP_VELOCITY var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back") var cam_basis := camera_pivot.global_transform.basis var flat_forward := -Vector3(cam_basis.z.x, 0.0, cam_basis.z.z).normalized() var flat_right := Vector3(cam_basis.x.x, 0.0, cam_basis.x.z).normalized() var direction := (flat_forward * -input_dir.y + flat_right * input_dir.x).normalized() # Logic for mimicking 4 legged animal movement if direction: var target_angle := atan2(direction.x, direction.z) # Snap on first movement so there is no startup arc from 0 if current_speed < 0.01: facing_angle = target_angle var charging := Input.is_key_pressed(KEY_SHIFT) var speed_t: float = clampf(current_speed / WALK_SPEED, 0.0, 1.0) var turn_rate: float = CHARGE_TURN if charging else lerpf(WALK_TURN_FAST, WALK_TURN_SLOW, speed_t) var diff := wrapf(target_angle - facing_angle, -PI, PI) facing_angle += clampf(diff, -turn_rate * delta, turn_rate * delta) var top_speed: float = CHARGE_SPEED if charging else WALK_SPEED var accel: float = CHARGE_ACCEL if charging else WALK_ACCEL current_speed = move_toward(current_speed, top_speed, accel * delta) else: var charging := Input.is_key_pressed(KEY_SHIFT) var decel: float = CHARGE_DECEL if charging else DECELERATION current_speed = move_toward(current_speed, 0.0, decel * delta) velocity.x = sin(facing_angle) * current_speed velocity.z = cos(facing_angle) * current_speed cube_guy.rotation.y = lerp_angle(cube_guy.rotation.y, facing_angle, delta * visual_turn_speed) move_and_slide()