public partial class CameraController : SpringArm3D { [Export] public Vector2 MouseSensitivity { get; set; } = new(0.2f, 0.2f); // Degrees per pixel. [Export] public float PitchMinimum { get; set; } = -90; [Export] public float PitchMaximum { get; set; } = -25; // FIXME: Fix the "snappyness" when moving slowly. // TODO: Add a "soft" minimum / maximum for pitch. // TODO: Gradually return to maximum spring length. // TODO: Turn player transparent as the camera moves closer. public static bool IsMouseCaptured => Input.MouseMode == Input.MouseModeEnum.Captured; public override void _Input(InputEvent ev) { if (IsMouseCaptured && ev.IsActionPressed("ui_cancel")) { // When escape is pressed, release the mouse. Input.MouseMode = Input.MouseModeEnum.Visible; GetViewport().SetInputAsHandled(); } } public override void _UnhandledInput(InputEvent ev) { switch (ev) { case InputEventMouseButton { ButtonIndex: MouseButton.Left, Pressed: true } when !IsMouseCaptured: // When left mouse button is pressed, capture the mouse. Input.MouseMode = Input.MouseModeEnum.Captured; GetViewport().SetInputAsHandled(); break; case InputEventMouseMotion motion when IsMouseCaptured: ApplyRotation(-motion.Relative * MouseSensitivity); break; } } void ApplyRotation(Vector2 delta) { delta *= Tau / 360; // degrees to radians var (pitch, yaw, _) = Rotation; yaw += delta.X; pitch += delta.Y; pitch = Clamp(pitch, DegToRad(PitchMinimum), DegToRad(PitchMaximum)); Rotation = new(pitch, yaw, 0); } }