Working camera controller

This commit is contained in:
2025-10-23 10:17:55 +02:00
parent 20a37dda17
commit 138e17503a
5 changed files with 100 additions and 35 deletions

View File

@ -127,6 +127,11 @@ spacebar_3d={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
]
}
right_click={
"deadzone": 0.2,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":2,"canceled":false,"pressed":false,"double_click":false,"script":null)
]
}
[layer_names]

View File

@ -24,10 +24,14 @@ func _physics_process(_delta):
# 1. Gather Input
var move_vec = Input.get_vector("move_left_3d", "move_right_3d", "move_backward_3d", "move_forward_3d")
var roll_input = Input.get_action_strength("roll_right_3d") - Input.get_action_strength("roll_left_3d")
var interact_pressed = Input.is_action_just_pressed("interact_3d")
var interact_released = Input.is_action_just_released("interact_3d") # Send release too
var interact_pressed = Input.is_action_just_pressed("spacebar_3d")
var interact_released = Input.is_action_just_released("spacebar_3d") # Send release too
var right_click_pressed = Input.is_action_just_pressed("right_click")
var right_click_held = Input.is_action_pressed("right_click")
var right_click_released = Input.is_action_just_released("right_click")
server_process_movement_input.rpc_id(1, move_vec, roll_input, interact_pressed, interact_released)
server_process_clicks.rpc_id(1, right_click_pressed, right_click_held, right_click_released)
@rpc("any_peer", "call_local")
func server_process_movement_input(move: Vector2, roll: float, interact_p: bool, interact_r: bool):
@ -39,6 +43,11 @@ func server_process_movement_input(move: Vector2, roll: float, interact_p: bool,
func server_process_rotation_input(yaw_delta: float, pitch_delta: float):
if is_instance_valid(possessed_pawn):
possessed_pawn.apply_rotation_input(yaw_delta, pitch_delta)
@rpc("any_peer", "call_local")
func server_process_clicks(r_pressed, r_held, r_released):
if is_instance_valid(possessed_pawn):
possessed_pawn.set_click_input(r_pressed, r_held, r_released)
func possess(pawn_to_control: ZeroGPawn):
possessed_pawn = pawn_to_control

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=4 format=3 uid="uid://ddfsn0rtdnfda"]
[gd_scene load_steps=5 format=3 uid="uid://ddfsn0rtdnfda"]
[ext_resource type="PackedScene" uid="uid://7yc6a07xoccy" path="res://scenes/tests/3d/zero_g_pawn.tscn" id="1_nvgim"]
@ -8,6 +8,10 @@ size = Vector3(10, 1, 10)
[sub_resource type="BoxShape3D" id="BoxShape3D_25xtv"]
size = Vector3(10, 1, 10)
[sub_resource type="TorusMesh" id="TorusMesh_nvgim"]
inner_radius = 2.0
outer_radius = 2.4
[node name="ZeroG3DTest" type="Node3D"]
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
@ -25,3 +29,10 @@ shape = SubResource("BoxShape3D_25xtv")
[node name="ZeroGPawn" parent="." instance=ExtResource("1_nvgim")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
[node name="StaticBody3D2" type="StaticBody3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10.4942, 0, 0)
[node name="MeshInstance3D" type="MeshInstance3D" parent="StaticBody3D2"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -10.4935, 3.97052, 0)
mesh = SubResource("TorusMesh_nvgim")

View File

@ -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

View File

@ -26,6 +26,9 @@ script = ExtResource("2_r62el")
metadata/_custom_type_script = "uid://vjfk3xnapfti"
[node name="CameraPivot" type="Node3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 0)
[node name="Camera3D" type="Camera3D" parent="CameraPivot"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 2)
[node name="SpringArm" type="SpringArm3D" parent="CameraPivot"]
spring_length = 3.0
[node name="Camera3D" type="Camera3D" parent="CameraPivot/SpringArm"]