Give-LIFE-Cybers/Scripts/freecam.gd
2025-04-07 20:16:09 -06:00

146 lines
4.8 KiB
GDScript

extends Camera3D
## Camera with flying script attached to it.
class_name Freecam3D
##
## Camera with toggleable freecam mode for prototyping when creating levels, shaders, lighting, etc.
##
## Usage: Run your game, press <TAB> and fly around freely. Uses Minecraft-like controls.
##
## Customize your own toggle key to avoid collisions with your current mappings.
@export var toggle_key: Key = KEY_TAB
## Speed up / down by scrolling the mouse whell down / up
@export var invert_speed_controls: bool = false
@export var overlay_text: bool = true
## Pivot node for camera looking around
@onready var pivot := Node3D.new()
## Main parent for camera overlay.
@onready var screen_overlay := VBoxContainer.new()
## Container for the chat-like event log.
@onready var event_log := VBoxContainer.new()
const MAX_SPEED := 0.25
const MIN_SPEED := 0.01
const ACCELERATION := 0.1
const MOUSE_SENSITIVITY := 0.002
## Whether or not the camera can move.
var movement_active := false:
set(val):
movement_active = val
display_message("[Movement ON]" if movement_active else "[Movement OFF]")
## The current maximum speed. Lower or higher it by scrolling the mouse wheel.
var target_speed := MIN_SPEED
## Movement velocity.
var velocity := Vector3.ZERO
## Sets up pivot and UI overlay elements.
func _setup_nodes() -> void:
self.add_sibling(pivot)
pivot.position = position
pivot.rotation = rotation
pivot.name = "FreecamPivot"
self.reparent(pivot)
self.position = Vector3.ZERO
self.rotation = Vector3.ZERO
# UI stuff
screen_overlay.add_theme_constant_override("Separation", 8)
self.add_child(screen_overlay)
screen_overlay.add_child(_make_label("Debug Camera"))
screen_overlay.add_spacer(false)
screen_overlay.add_child(event_log)
screen_overlay.visible = overlay_text
func _ready() -> void:
_setup_nodes.call_deferred()
_add_keybindings()
movement_active = true
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _process(delta: float) -> void:
if Input.is_action_just_released("__debug_camera_toggle"):
movement_active = not movement_active
if movement_active:
var dir = Vector3.ZERO
if Input.is_action_pressed("__debug_camera_forward"): dir.z -= 1
if Input.is_action_pressed("__debug_camera_back"): dir.z += 1
if Input.is_action_pressed("__debug_camera_left"): dir.x -= 1
if Input.is_action_pressed("__debug_camera_right"): dir.x += 1
if Input.is_action_pressed("__debug_camera_up"): dir.y += 1
if Input.is_action_pressed("__debug_camera_down"): dir.y -= 1
dir = dir.normalized()
dir = dir.rotated(Vector3.UP, pivot.rotation.y)
velocity = lerp(velocity, dir * target_speed, ACCELERATION)
pivot.position += velocity
func _input(event: InputEvent) -> void:
if movement_active:
# Turn around
if event is InputEventMouseMotion:
pivot.rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
rotate_x(-event.relative.y * MOUSE_SENSITIVITY)
rotation.x = clamp(rotation.x, -PI/2, PI/2)
var speed_up = func():
target_speed = clamp(target_speed + 0.015, MIN_SPEED, MAX_SPEED)
display_message("[Speed up] " + str(target_speed))
var slow_down = func():
target_speed = clamp(target_speed - 0.015, MIN_SPEED, MAX_SPEED)
display_message("[Slow down] " + str(target_speed))
# Speed up and down with the mouse wheel
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_WHEEL_UP and event.pressed:
slow_down.call() if invert_speed_controls else speed_up.call()
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN and event.pressed:
speed_up.call() if invert_speed_controls else slow_down.call()
## Pushes new message label into "chat" and removes the old ones if necessary
func display_message(text: String) -> void:
while event_log.get_child_count() >= 3:
event_log.remove_child(event_log.get_child(0))
event_log.add_child(_make_label(text))
func _make_label(text: String) -> Label:
var l = Label.new()
l.text = text
return l
func _add_keybindings() -> void:
var actions = InputMap.get_actions()
if "__debug_camera_forward" not in actions: _add_key_input_action("__debug_camera_forward", KEY_W)
if "__debug_camera_back" not in actions: _add_key_input_action("__debug_camera_back", KEY_S)
if "__debug_camera_left" not in actions: _add_key_input_action("__debug_camera_left", KEY_A)
if "__debug_camera_right" not in actions: _add_key_input_action("__debug_camera_right", KEY_D)
if "__debug_camera_up" not in actions: _add_key_input_action("__debug_camera_up", KEY_E)
if "__debug_camera_down" not in actions: _add_key_input_action("__debug_camera_down", KEY_Q)
if "__debug_camera_toggle" not in actions: _add_key_input_action("__debug_camera_toggle", toggle_key)
func _add_key_input_action(name: String, key: Key) -> void:
var ev = InputEventKey.new()
ev.physical_keycode = key
InputMap.add_action(name)
InputMap.action_add_event(name, ev)