Cyclone-Tracker/addons/Free fly camera/Src/free_fly_startup.gd
2025-06-28 23:00:54 +10:00

143 lines
5.3 KiB
GDScript

extends CharacterBody3D
# Movement Settings
@export_subgroup("Movement settings")
@export var movement_speed : float = 10.0 # Default movement speed
@export var sprint_multiplier: float = 2.0 # Sprint speed multiplier
# Key Bindings
@export_subgroup("Movement keybinds")
@export var KEY_FORWARD: String = "move_forward"
@export var KEY_BACKWARD: String = "move_backward"
@export var KEY_LEFT: String = "move_left"
@export var KEY_RIGHT: String = "move_right"
@export var KEY_UP: String = "move_up"
@export var KEY_DOWN: String = "move_down"
@export var KEY_SPRINT: String = "sprint"
# Mouse Settings
@export_subgroup("Mouse")
@export var MOUSE_ACCEL_STATE := true # Enable mouse acceleration
@export var MOUSE_SENS := 0.005 # Mouse sensitivity
@export var MOUSE_ACCEL := 50 # Mouse acceleration factor
# Head Rotation Clamping
@export_subgroup("Clamp Head Rotation")
@export var CLAMP_HEAD_ROTATION := true # Enable head rotation clamping
@export var CLAMP_HEAD_ROTATION_MIN := -90.0 # Min head rotation
@export var CLAMP_HEAD_ROTATION_MAX := 90.0 # Max head rotation
# Camera Node
@export_subgroup("Camera node")
@export var camera: Camera3D # Camera attached to the character
# Collision Settings (Optional)
@export_subgroup("Collision (Optional)")
@export var collision_state := true # Enable or disable collision
@export var collision: CollisionShape3D # Collision shape for the character
# Advanced Settings
@export_category("Advanced")
@export var UPDATE_ON_PHYSICS := true # Should the update happen on physics ticks?
@export var KEY_ESCAPE: String = "KEY_ESCAPE" # Escape key for toggling mouse capture
# Internal variables for rotation and mouse capture
var rotation_target_player : float
var rotation_target_head : float
var mouse_captured: bool = true
# Called when the node enters the scene tree for the first time
func _ready():
# Toggle collision state if set
if collision != null:
collision.disabled = !collision_state
# If no camera is assigned, create a new one and add it to the node
if camera == null:
camera = Camera3D.new()
add_child(camera)
# Capture the mouse at the start
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
# Process the input every frame if not using physics
func _process(delta: float) -> void:
if !UPDATE_ON_PHYSICS:
handle_input() # Handle player input
rotate_player(delta) # Apply player and camera rotation
# Process input and rotation on physics ticks
func _physics_process(delta: float) -> void:
if UPDATE_ON_PHYSICS:
handle_input()
rotate_player(delta)
# Handle player movement input and apply to the character
func handle_input():
var move_direction = Vector3.ZERO # Initial movement direction is zero
# Toggle mouse capture when the escape key is pressed
if Input.is_action_just_pressed(KEY_ESCAPE):
mouse_captured = not mouse_captured
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if mouse_captured else Input.MOUSE_MODE_VISIBLE
if mouse_captured:
# Movement input for each direction
if Input.is_action_pressed(KEY_FORWARD):
move_direction.z += 1
if Input.is_action_pressed(KEY_BACKWARD):
move_direction.z -= 1
if Input.is_action_pressed(KEY_LEFT):
move_direction.x -= 1
if Input.is_action_pressed(KEY_RIGHT):
move_direction.x += 1
if Input.is_action_pressed(KEY_UP):
move_direction.y += 1
if Input.is_action_pressed(KEY_DOWN):
move_direction.y -= 1
# Normalize the movement vector to ensure consistent speed
move_direction = move_direction.normalized()
# Get the direction relative to the camera orientation
var forward = -camera.global_transform.basis.z
var right = camera.global_transform.basis.x
var up = camera.global_transform.basis.y
# Sprinting multiplier if sprint key is pressed
var speed_mod = sprint_multiplier if Input.is_action_pressed(KEY_SPRINT) else 1.0
velocity = (forward * move_direction.z + right * move_direction.x + up * move_direction.y) * movement_speed * speed_mod
# Move the character
move_and_slide()
# Capture mouse motion input and update rotations
func _input(event):
if Engine.is_editor_hint():
return
# Process mouse motion when the mouse is captured
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
set_rotation_target(event.relative)
# Update the target rotations for the player and camera based on mouse motion
func set_rotation_target(mouse_motion: Vector2):
# Update player rotation based on mouse X movement
rotation_target_player += -mouse_motion.x * MOUSE_SENS
# Update head rotation based on mouse Y movement
rotation_target_head += -mouse_motion.y * MOUSE_SENS
# Clamp the head rotation if enabled
if CLAMP_HEAD_ROTATION:
rotation_target_head = clamp(rotation_target_head, deg_to_rad(CLAMP_HEAD_ROTATION_MIN), deg_to_rad(CLAMP_HEAD_ROTATION_MAX))
# Rotate the player and camera smoothly based on target rotation
func rotate_player(delta):
if MOUSE_ACCEL_STATE:
# Apply spherical interpolation (slerp) for smooth rotation
quaternion = quaternion.slerp(Quaternion(Vector3.UP, rotation_target_player), MOUSE_ACCEL * delta)
camera.quaternion = camera.quaternion.slerp(Quaternion(Vector3.RIGHT, rotation_target_head), MOUSE_ACCEL * delta)
else:
# If mouse acceleration is off, directly set to target rotation
quaternion = Quaternion(Vector3.UP, rotation_target_player)
camera.quaternion = Quaternion(Vector3.RIGHT, rotation_target_head)