Move to physics based climbing
This commit is contained in:
@ -56,7 +56,6 @@ func _physics_process(delta: float):
|
||||
# 1. Apply Mouse Rotation (Universal head look)
|
||||
_apply_mouse_rotation()
|
||||
|
||||
|
||||
if eva_suit_component:
|
||||
# Stabilization is handled within eva_suit_component.process_movement
|
||||
eva_suit_component.process_movement(delta, _move_input, _vertical_input, _roll_input, _r_click_input)
|
||||
|
||||
@ -75,9 +75,9 @@ func process_movement(delta: float, move_input: Vector2, roll_input: float, reac
|
||||
MovementState.REACHING:
|
||||
_process_reaching(delta)
|
||||
MovementState.GRIPPING:
|
||||
_process_gripping(delta, roll_input)
|
||||
_apply_grip_physics(delta, move_input, roll_input)
|
||||
MovementState.CLIMBING:
|
||||
_process_climbing(delta, move_input)
|
||||
_apply_climb_physics(delta, move_input)
|
||||
MovementState.CHARGING_LAUNCH:
|
||||
_handle_launch_charge(delta)
|
||||
|
||||
@ -158,87 +158,111 @@ func _process_reaching(_delta: float):
|
||||
# When close enough: state = MovementState.GRIPPING
|
||||
pass
|
||||
|
||||
func _process_gripping(delta: float, roll_input: float):
|
||||
if not is_instance_valid(pawn) or not is_instance_valid(current_grip): # Safety check
|
||||
_cancel_reach() # Transition out if grip or pawn is invalid
|
||||
return
|
||||
|
||||
# 1. Dampen Existing Motion
|
||||
pawn.velocity = pawn.velocity.lerp(Vector3.ZERO, delta * gripping_linear_damping)
|
||||
pawn.angular_velocity = pawn.angular_velocity.lerp(Vector3.ZERO, delta * gripping_angular_damping)
|
||||
|
||||
# Stop completely if very slow, prevents jitter
|
||||
if pawn.velocity.length_squared() < 0.001:
|
||||
pawn.velocity = Vector3.ZERO
|
||||
if pawn.angular_velocity.length_squared() < 0.001:
|
||||
pawn.angular_velocity = Vector3.ZERO
|
||||
|
||||
# 2. Move Pawn Towards Grip Position (lerp)
|
||||
var grip_base_transform = current_grip.get_grip_transform(pawn.global_position)
|
||||
|
||||
# --- Calculate Offset ---
|
||||
# Calculate the final target position with the offset
|
||||
var grip_in_direction = grip_base_transform.basis.z.normalized()
|
||||
|
||||
|
||||
var target_position = grip_base_transform.origin + grip_in_direction * _get_hold_distance()
|
||||
# --- End Offset Calculation ---
|
||||
|
||||
pawn.global_transform.origin = pawn.global_transform.origin.lerp(target_position, delta * reach_speed)
|
||||
func _apply_grip_physics(delta: float, move_input: Vector2, roll_input: float):
|
||||
if not is_instance_valid(pawn) or not is_instance_valid(current_grip):
|
||||
_release_current_grip(); return
|
||||
|
||||
# 3. Orient Pawn Towards Grip Orientation (slerp)
|
||||
# Make the pawn smoothly rotate to match the grip's desired orientation
|
||||
if not is_zero_approx(roll_input):
|
||||
pawn.rotate(current_grip.global_basis.z, -roll_input * gripping_orient_speed * delta)
|
||||
pass
|
||||
else:
|
||||
var chosen_basis = _choose_grip_orientation(current_grip.global_basis)
|
||||
pawn.global_transform.basis = pawn.global_transform.basis.slerp(chosen_basis, delta * gripping_orient_speed)
|
||||
|
||||
|
||||
# TODO: Later, replace step 2 and 3 with IK driving the hand bone to the target_transform.origin,
|
||||
# while the physics/orientation logic stops the main body's momentum.
|
||||
|
||||
# --- 1. Calculate Target Transform (Same as before) ---
|
||||
var grip_base_transform = current_grip.global_transform
|
||||
var target_direction = grip_base_transform.basis.z.normalized()
|
||||
var hold_distance = _get_hold_distance()
|
||||
var target_position = grip_base_transform.origin + target_direction * hold_distance
|
||||
|
||||
var grip_up_vector = grip_base_transform.basis.y.normalized()
|
||||
var grip_down_vector = -grip_base_transform.basis.y.normalized()
|
||||
var pawn_up_vector = pawn.global_transform.basis.y
|
||||
var dot_up = pawn_up_vector.dot(grip_up_vector)
|
||||
var dot_down = pawn_up_vector.dot(grip_down_vector)
|
||||
var chosen_orientation_up_vector = grip_up_vector if dot_up >= dot_down else grip_down_vector
|
||||
var target_basis = Basis.looking_at(-target_direction, chosen_orientation_up_vector).orthonormalized()
|
||||
|
||||
func _process_climbing(delta: float, move_input: Vector2):
|
||||
# --- 2. Apply Linear Force (PD Controller) ---
|
||||
var error_pos = target_position - pawn.global_position
|
||||
# Simple P-controller for velocity (acts as a spring)
|
||||
var target_velocity_pos = error_pos * gripping_linear_damping # 'damping' here acts as Kp
|
||||
# Simple D-controller (damping)
|
||||
target_velocity_pos -= pawn.velocity * gripping_angular_damping # 'angular_damping' here acts as Kd
|
||||
# Apply force via acceleration
|
||||
pawn.velocity = pawn.velocity.lerp(target_velocity_pos, delta * 10.0) # Smoothly apply correction
|
||||
|
||||
# --- 3. Apply Angular Force (PD Controller) ---
|
||||
if not is_zero_approx(roll_input):
|
||||
# Manual Roll Input (applies torque)
|
||||
var roll_torque_global = pawn.global_transform.basis.z * (-roll_input) * gripping_orient_speed # Use global Z
|
||||
pawn.add_torque(roll_torque_global, delta)
|
||||
else:
|
||||
# Auto-Orient (PD Controller)
|
||||
var current_quat = pawn.global_transform.basis.get_rotation_quaternion()
|
||||
var target_quat = target_basis.get_rotation_quaternion()
|
||||
var error_quat = target_quat * current_quat.inverse()
|
||||
var error_angle = error_quat.get_angle()
|
||||
var error_axis = error_quat.get_axis()
|
||||
|
||||
# Proportional torque (spring)
|
||||
var torque_proportional = error_axis.normalized() * error_angle * gripping_orient_speed # 'speed' acts as Kp
|
||||
# Derivative torque (damping)
|
||||
var torque_derivative = -pawn.angular_velocity * gripping_angular_damping # 'damping' acts as Kd
|
||||
|
||||
var total_torque_global = (torque_proportional + torque_derivative)
|
||||
pawn.add_torque(total_torque_global, delta)
|
||||
|
||||
func _apply_climb_physics(delta: float, move_input: Vector2):
|
||||
if not is_instance_valid(pawn) or not is_instance_valid(current_grip):
|
||||
_stop_climb(true) # Safety check
|
||||
return
|
||||
_stop_climb(true); return
|
||||
|
||||
var climb_direction = move_input.y * pawn.global_basis.y + move_input.x * pawn.global_basis.x
|
||||
climb_direction = climb_direction.normalized()
|
||||
# 1. Calculate Climb Direction (same as _start_climb)
|
||||
var pawn_forward = -pawn.global_transform.basis.z
|
||||
var pawn_right = pawn.global_transform.basis.x
|
||||
climb_direction_world = (pawn_forward * -move_input.y + pawn_right * move_input.x).normalized()
|
||||
|
||||
# 1. Accelerate towards climb speed in the climb direction
|
||||
var target_velocity = climb_direction * climb_speed
|
||||
pawn.velocity = pawn.velocity.lerp(target_velocity, delta * climb_acceleration)
|
||||
pawn.angular_velocity = pawn.angular_velocity.lerp(Vector3.ZERO, delta * gripping_angular_damping) # Dampen spin while climbing
|
||||
|
||||
# 2. Find the next potential grip in the climb direction
|
||||
next_grip_target = _find_best_grip(climb_direction) # Use world direction
|
||||
# 2. Find Next Grip
|
||||
next_grip_target = _find_best_grip(climb_direction_world, INF, climb_angle_threshold_deg)
|
||||
|
||||
# 3. Check for Handover
|
||||
var performed_handover = false
|
||||
if is_instance_valid(next_grip_target):
|
||||
performed_handover = _perform_grip_handover()
|
||||
var next_grip_pos = next_grip_target.global_position
|
||||
var dist_sq_to_next = pawn.global_position.distance_squared_to(next_grip_pos)
|
||||
if dist_sq_to_next < grip_handover_distance * grip_handover_distance:
|
||||
performed_handover = _perform_grip_handover()
|
||||
|
||||
# 4. Check for Release Past Grip (if no handover)
|
||||
if not performed_handover:
|
||||
var current_grip_pos = current_grip.global_position
|
||||
var vector_from_grip_to_pawn = pawn.global_position - current_grip_pos
|
||||
# Project this vector onto the climb direction
|
||||
var distance_along_climb_dir = vector_from_grip_to_pawn.dot(climb_direction_world)
|
||||
if distance_along_climb_dir > 0.2: # Release threshold
|
||||
_release_current_grip()
|
||||
return # State changed to IDLE
|
||||
|
||||
# If pawn is further along climb direction than the threshold past the grip's origin
|
||||
if distance_along_climb_dir > release_past_grip_threshold:
|
||||
print("Climbed past current grip without handover. Releasing.")
|
||||
_release_current_grip() # This sets state to IDLE
|
||||
# Apply slight brake on release
|
||||
pawn.velocity = pawn.velocity.lerp(Vector3.ZERO, 0.2)
|
||||
return # Exit early as state changed
|
||||
# 5. Apply Movement Force
|
||||
var target_velocity = climb_direction_world * climb_speed
|
||||
pawn.velocity = pawn.velocity.lerp(target_velocity, delta * climb_acceleration)
|
||||
|
||||
# 4. (Optional) Maintain Orientation relative to current grip or next target?
|
||||
# Only lerp origin slightly to allow movement, prioritize basis slerp
|
||||
# pawn.global_transform.origin = pawn.global_transform.origin.lerp(target_transform.origin + target_transform.basis.z * _get_hold_distance(), delta * reach_speed * 0.5) # Slower position lerp
|
||||
pawn.global_transform.basis = pawn.global_transform.basis.slerp(_choose_grip_orientation(current_grip.global_basis), delta * gripping_orient_speed)
|
||||
# 6. Apply Angular Force (Auto-Orient to current grip)
|
||||
var grip_base_transform = current_grip.global_transform
|
||||
var target_direction = grip_base_transform.basis.z.normalized()
|
||||
var grip_up_vector = grip_base_transform.basis.y.normalized()
|
||||
var grip_down_vector = -grip_base_transform.basis.y.normalized()
|
||||
var pawn_up_vector = pawn.global_transform.basis.y
|
||||
var dot_up = pawn_up_vector.dot(grip_up_vector)
|
||||
var dot_down = pawn_up_vector.dot(grip_down_vector)
|
||||
var chosen_orientation_up_vector = grip_up_vector if dot_up >= dot_down else grip_down_vector
|
||||
var target_basis = Basis.looking_at(-target_direction, chosen_orientation_up_vector).orthonormalized()
|
||||
|
||||
var current_quat = pawn.global_transform.basis.get_rotation_quaternion()
|
||||
var target_quat = target_basis.get_rotation_quaternion()
|
||||
var error_quat = target_quat * current_quat.inverse()
|
||||
var error_angle = error_quat.get_angle()
|
||||
var error_axis = error_quat.get_axis()
|
||||
|
||||
var torque_proportional = error_axis.normalized() * error_angle * gripping_orient_speed
|
||||
var torque_derivative = -pawn.angular_velocity * gripping_angular_damping
|
||||
var total_torque_global = (torque_proportional + torque_derivative)
|
||||
pawn.add_torque(total_torque_global, delta)
|
||||
|
||||
# --- Grip Helpers
|
||||
# Attempts to find and grab the best available grip within range
|
||||
|
||||
Reference in New Issue
Block a user