|
|
|
|
@ -3,14 +3,14 @@ extends CharacterBody3D
|
|
|
|
|
class_name ZeroGPawn
|
|
|
|
|
|
|
|
|
|
# --- Movement Parameters ---
|
|
|
|
|
@export var move_speed: float = 5.0
|
|
|
|
|
@export var move_speed: float = 2.0
|
|
|
|
|
@export var roll_speed: float = 1.0
|
|
|
|
|
@export var grab_check_distance: float = 1.0 # How close to a surface to attempt grabbing
|
|
|
|
|
@export var launch_charge_rate: float = 20.0
|
|
|
|
|
@export var max_launch_speed: float = 15.0
|
|
|
|
|
|
|
|
|
|
# --- Orientation Parameters ---
|
|
|
|
|
@export var orientation_speed: float = 5.0 # How quickly the body turns to face the camera direction
|
|
|
|
|
@export var orientation_speed: float = 2.0 # How quickly the body turns to face the camera direction
|
|
|
|
|
@export var target_body_pitch_degrees: float = 70.0 # Desired pitch angle when moving
|
|
|
|
|
|
|
|
|
|
# --- State ---
|
|
|
|
|
@ -26,29 +26,37 @@ var _move_input: Vector2 = Vector2.ZERO # (Strafe, Forward/Backward)
|
|
|
|
|
var _roll_input: float = 0.0
|
|
|
|
|
var _interact_pressed: bool = false
|
|
|
|
|
var _interact_released: bool = false
|
|
|
|
|
var _r_pressed: bool = false
|
|
|
|
|
var _r_held: bool = false
|
|
|
|
|
var _r_released: bool = false
|
|
|
|
|
|
|
|
|
|
# --- Rotation Input Buffer ---
|
|
|
|
|
var _yaw_input: float = 0.0
|
|
|
|
|
var _pitch_input: float = 0.0
|
|
|
|
|
|
|
|
|
|
const MIN_PITCH = -PI / 2.0 + 0.1 # Limit looking straight up/down
|
|
|
|
|
const MAX_PITCH = PI / 2.0 - 0.1
|
|
|
|
|
const MIN_PITCH = rad_to_deg(-PI / 2.0) + 15 # Limit looking straight up/down
|
|
|
|
|
const MAX_PITCH = rad_to_deg( PI / 2.0) - 30
|
|
|
|
|
|
|
|
|
|
@onready var grab_ray: RayCast3D = $GrabRay # We'll need to add this node
|
|
|
|
|
@onready var camera_pivot: Node3D = $CameraPivot # Add this @onready var
|
|
|
|
|
@onready var camera: Camera3D = $CameraPivot/SpringArm/Camera3D
|
|
|
|
|
|
|
|
|
|
func _physics_process(delta: float):
|
|
|
|
|
# --- Apply buffered rotations FIRST ---
|
|
|
|
|
if _yaw_input != 0.0:
|
|
|
|
|
camera_pivot.rotate_y(_yaw_input) # Rotate the pivot locally for head yaw
|
|
|
|
|
_yaw_input = 0.0
|
|
|
|
|
camera_pivot.rotate_y(_yaw_input) # Rotate the pivot locally for head yaw
|
|
|
|
|
#camera_pivot.rotation.y = clamp(camera_pivot.rotation.y, deg_to_rad(-90), deg_to_rad(90))
|
|
|
|
|
_yaw_input = 0.0
|
|
|
|
|
|
|
|
|
|
if _pitch_input != 0.0:
|
|
|
|
|
camera_pivot.rotate_x(_pitch_input) # Rotate ONLY the pivot around its X (right) axis
|
|
|
|
|
# Clamp the pitch to prevent flipping
|
|
|
|
|
camera_pivot.rotation.x = clamp(camera_pivot.rotation.x, MIN_PITCH, MAX_PITCH)
|
|
|
|
|
# Apply pitch rotation to the PIVOT AROUND the pivots local right axis
|
|
|
|
|
camera_pivot.rotate_object_local(Vector3.RIGHT, _pitch_input)
|
|
|
|
|
|
|
|
|
|
#Clamp the pitch to prevent flipping
|
|
|
|
|
camera_pivot.rotation.x = clamp(camera_pivot.rotation.x, deg_to_rad(MIN_PITCH), deg_to_rad(MAX_PITCH))
|
|
|
|
|
_pitch_input = 0.0
|
|
|
|
|
|
|
|
|
|
camera_pivot.rotation.z = 0.0
|
|
|
|
|
# --- Update State ---
|
|
|
|
|
_update_state()
|
|
|
|
|
|
|
|
|
|
@ -56,8 +64,11 @@ func _physics_process(delta: float):
|
|
|
|
|
match current_state:
|
|
|
|
|
State.FLOATING:
|
|
|
|
|
_apply_floating_movement(delta)
|
|
|
|
|
if _move_input != Vector2.ZERO:
|
|
|
|
|
_orient_pawn(delta) # Auto-orient the body only when WASD is pressed
|
|
|
|
|
if _r_held:
|
|
|
|
|
_orient_pawn(delta) # Auto-orient the body only when moving forward
|
|
|
|
|
|
|
|
|
|
#if _move_input != Vector2.ZERO:
|
|
|
|
|
#_orient_pawn(delta) # Auto-orient the body only when WASD is pressed
|
|
|
|
|
State.GRABBING:
|
|
|
|
|
_handle_grabbed_state(delta)
|
|
|
|
|
State.CHARGING_LAUNCH:
|
|
|
|
|
@ -73,6 +84,8 @@ func _physics_process(delta: float):
|
|
|
|
|
_roll_input = 0.0
|
|
|
|
|
_interact_pressed = false
|
|
|
|
|
_interact_released = false
|
|
|
|
|
_r_pressed = false
|
|
|
|
|
_r_released = false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _update_state():
|
|
|
|
|
@ -106,11 +119,12 @@ func _update_state():
|
|
|
|
|
|
|
|
|
|
# --- Modified Movement uses CAMERA direction ---
|
|
|
|
|
func _apply_floating_movement(delta: float):
|
|
|
|
|
# Get the forward direction *from the camera pivot's perspective*
|
|
|
|
|
var camera_forward = -camera_pivot.global_transform.basis.z
|
|
|
|
|
var camera_right = camera_pivot.global_transform.basis.x
|
|
|
|
|
|
|
|
|
|
var move_dir = (camera_forward * _move_input.y + camera_right * _move_input.x)
|
|
|
|
|
var body_forward = -self.global_transform.basis.z
|
|
|
|
|
var body_right = self.global_transform.basis.x
|
|
|
|
|
|
|
|
|
|
var forward_motion = body_forward * _move_input.y if _move_input.y >= 0.1 else body_forward * _move_input.y
|
|
|
|
|
|
|
|
|
|
var move_dir = (forward_motion + body_right * _move_input.x)
|
|
|
|
|
|
|
|
|
|
if move_dir != Vector3.ZERO:
|
|
|
|
|
velocity += move_dir.normalized() * move_speed * delta
|
|
|
|
|
@ -122,22 +136,32 @@ func _apply_floating_movement(delta: float):
|
|
|
|
|
# --- Auto-Orientation Logic ---
|
|
|
|
|
func _orient_pawn(delta: float):
|
|
|
|
|
# 1. Determine Target Orientation Basis
|
|
|
|
|
var initial_cam_basis = camera_pivot.global_transform.basis
|
|
|
|
|
var target_forward = -camera_pivot.global_transform.basis.z # Look where camera looks
|
|
|
|
|
var target_up = Vector3.UP # Default up initially
|
|
|
|
|
|
|
|
|
|
# Feet trailing logic (simple version):
|
|
|
|
|
# If moving significantly, try to align 'up' opposite to velocity.
|
|
|
|
|
if velocity.length_squared() > 0.1:
|
|
|
|
|
target_up = -velocity.normalized()
|
|
|
|
|
# --- THE FIX: Adjust how target_up is calculated ---
|
|
|
|
|
# Calculate velocity components relative to camera orientation
|
|
|
|
|
var forward_velocity_component = velocity.dot(target_forward)
|
|
|
|
|
var right_velocity_component = velocity.dot(camera_pivot.global_transform.basis.x)
|
|
|
|
|
|
|
|
|
|
# Ensure target_up is orthogonal to target_forward
|
|
|
|
|
# Otherwise, basis will skew. Use cross products to correct.
|
|
|
|
|
var target_right = target_up.cross(target_forward).normalized()
|
|
|
|
|
target_up = target_forward.cross(target_right).normalized()
|
|
|
|
|
else:
|
|
|
|
|
# If not moving, just align forward, keep current 'up' reasonably oriented
|
|
|
|
|
target_up = transform.basis.y # Maintain current roll orientation when still
|
|
|
|
|
# Only apply strong "feet trailing" if significant forward/backward movement dominates
|
|
|
|
|
# and we are actually moving.
|
|
|
|
|
#if abs(forward_velocity_component) > abs(right_velocity_component) * 0.5 and velocity.length_squared() > 0.1:
|
|
|
|
|
#target_up = -velocity.normalized()
|
|
|
|
|
## Orthogonalize to prevent basis skew
|
|
|
|
|
#var target_right = target_up.cross(target_forward).normalized()
|
|
|
|
|
## If vectors are parallel, cross product is zero. Fallback needed.
|
|
|
|
|
#if target_right.is_zero_approx():
|
|
|
|
|
#target_up = transform.basis.y # Fallback to current up
|
|
|
|
|
#else:
|
|
|
|
|
#target_up = target_forward.cross(target_right).normalized()
|
|
|
|
|
#else:
|
|
|
|
|
## If primarily strafing or stationary relative to forward,
|
|
|
|
|
## maintain the current body's roll orientation (its local Y-axis).
|
|
|
|
|
target_up = transform.basis.y
|
|
|
|
|
|
|
|
|
|
# --- End Fix ---
|
|
|
|
|
|
|
|
|
|
# Create the target basis
|
|
|
|
|
var target_basis = Basis.looking_at(target_forward, target_up)
|
|
|
|
|
@ -146,13 +170,21 @@ func _orient_pawn(delta: float):
|
|
|
|
|
# Apply the desired 70-degree pitch relative to the forward direction
|
|
|
|
|
# var target_pitch_rad = deg_to_rad(target_body_pitch_degrees)
|
|
|
|
|
# target_basis = target_basis.rotated(target_basis.x, target_pitch_rad) # Rotate around the target right vector
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 2. Smoothly Interpolate Towards Target Basis
|
|
|
|
|
var current_basis = global_transform.basis
|
|
|
|
|
var new_basis = current_basis.slerp(target_basis, delta * orientation_speed)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Store the body's yaw *before* applying the new basis
|
|
|
|
|
var old_body_yaw = current_basis.get_euler().y
|
|
|
|
|
var old_body_pitch = current_basis.get_euler().x
|
|
|
|
|
|
|
|
|
|
# 3. Apply the new orientation
|
|
|
|
|
global_transform.basis = new_basis
|
|
|
|
|
|
|
|
|
|
# 4. Reset camera pivot to rotation to what it was before we rotated the parent
|
|
|
|
|
camera_pivot.global_transform.basis = initial_cam_basis
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func _handle_grab_attempt():
|
|
|
|
|
# If interact was pressed this frame AND we are floating
|
|
|
|
|
@ -194,7 +226,7 @@ func _execute_launch():
|
|
|
|
|
func _check_for_grab_surface() -> bool:
|
|
|
|
|
# Use RayCast3D to check in multiple directions or based on movement
|
|
|
|
|
# For simplicity, let's just check directly forward
|
|
|
|
|
grab_ray.target_position = Vector3.FORWARD * -grab_check_distance # Ray points forward locally
|
|
|
|
|
grab_ray.target_position = Vector3.FORWARD * grab_check_distance # Ray points forward locally
|
|
|
|
|
grab_ray.force_raycast_update()
|
|
|
|
|
if grab_ray.is_colliding():
|
|
|
|
|
grab_surface_normal = grab_ray.get_collision_normal()
|
|
|
|
|
@ -215,3 +247,8 @@ func apply_rotation_input(yaw_delta: float, pitch_delta: float):
|
|
|
|
|
# Buffer the input to be applied in _physics_process
|
|
|
|
|
_yaw_input += yaw_delta
|
|
|
|
|
_pitch_input += pitch_delta
|
|
|
|
|
|
|
|
|
|
func set_click_input(r_pressed: bool, r_held: bool, r_released: bool):
|
|
|
|
|
_r_pressed = r_pressed
|
|
|
|
|
_r_held = r_held
|
|
|
|
|
_r_released = r_released
|
|
|
|
|
|