using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.InputSystem; using Cinemachine; /// /// Custom Cinemachine input handler that replaces CinemachineInputProvider. /// Uses CinemachineCore.GetInputAxis delegate for total control over camera orbit. /// /// Supports: /// - Gamepad right stick (X = horizontal orbit, Y = vertical orbit) /// - Mobile touch drag (right half of screen, ignoring UI) /// - Mouse delta (desktop/editor) /// /// Attach this to the CinemachineFreeLook camera GameObject. /// Remove or disable CinemachineInputProvider if present. /// public class CinemachineCameraInput : MonoBehaviour { [Header("Sensitivity")] [SerializeField] private float gamepadSensitivityX = 200f; [SerializeField] private float gamepadSensitivityY = 3f; [SerializeField] private float touchSensitivityX = 0.15f; [SerializeField] private float touchSensitivityY = 0.003f; [SerializeField] private float mouseSensitivityX = 3f; [SerializeField] private float mouseSensitivityY = 0.03f; [Header("Touch Settings")] [Tooltip("Only process touches on the right portion of the screen (0.5 = right half)")] [Range(0f, 1f)] [SerializeField] private float touchActiveScreenRatio = 0.4f; [Tooltip("Ignore touches over UI elements (buttons, joysticks, etc.)")] [SerializeField] private bool ignoreUITouches = true; [Header("Filtering")] [SerializeField] private float stickDeadzone = 0.15f; [SerializeField] private float smoothing = 0.1f; // Touch tracking private int activeTouchId = -1; private Vector2 lastTouchPos; // Smoothed output private float smoothedX; private float smoothedY; private float rawX; private float rawY; // Axis names used by CinemachineFreeLook private const string XAxisName = "Mouse X"; private const string YAxisName = "Mouse Y"; private CinemachineFreeLook freeLookCam; private void Awake() { freeLookCam = GetComponent(); } private void OnEnable() { // Override Cinemachine's input gathering with our custom handler CinemachineCore.GetInputAxis = HandleCinemachineInput; // Disable CinemachineInputProvider if present (we replace it) var provider = GetComponent(); if (provider != null) { provider.enabled = false; Debug.Log("[CinemachineCameraInput] Disabled CinemachineInputProvider — using custom input handler"); } // Clear the default input axis names on the FreeLook camera // This prevents Cinemachine from also reading legacy Input Manager axes if (freeLookCam != null) { freeLookCam.m_XAxis.m_InputAxisName = ""; freeLookCam.m_YAxis.m_InputAxisName = ""; } Debug.Log("[CinemachineCameraInput] Custom camera input enabled (gamepad + touch + mouse)"); } private void OnDisable() { // Restore default input handling when disabled CinemachineCore.GetInputAxis = UnityEngine.Input.GetAxis; } private void Update() { // Gather raw input from all sources rawX = 0f; rawY = 0f; ReadGamepadInput(); ReadTouchInput(); ReadMouseInput(); // Smooth the input if (smoothing > 0.001f) { smoothedX = Mathf.Lerp(smoothedX, rawX, Time.unscaledDeltaTime / smoothing); smoothedY = Mathf.Lerp(smoothedY, rawY, Time.unscaledDeltaTime / smoothing); } else { smoothedX = rawX; smoothedY = rawY; } } /// /// Delegate callback — Cinemachine calls this instead of Input.GetAxis /// private float HandleCinemachineInput(string axisName) { if (axisName == XAxisName) return smoothedX; if (axisName == YAxisName) return smoothedY; return 0f; } // ======================== GAMEPAD ======================== private void ReadGamepadInput() { if (Gamepad.current == null) return; Vector2 rightStick = Gamepad.current.rightStick.ReadValue(); // Apply deadzone if (rightStick.magnitude < stickDeadzone) return; rawX += rightStick.x * gamepadSensitivityX * Time.unscaledDeltaTime; rawY += rightStick.y * gamepadSensitivityY * Time.unscaledDeltaTime; } // ======================== TOUCH ======================== private void ReadTouchInput() { if (Touchscreen.current == null) return; var touches = Touchscreen.current.touches; bool foundActiveTouch = false; for (int i = 0; i < touches.Count; i++) { var touch = touches[i]; if (!touch.press.isPressed) continue; int id = touch.touchId.ReadValue(); Vector2 pos = touch.position.ReadValue(); // If no active touch, try to start tracking this one if (activeTouchId == -1) { // Only track touches on the right portion of the screen if (pos.x < Screen.width * touchActiveScreenRatio) continue; // Ignore touches over UI elements if (ignoreUITouches && EventSystem.current != null && EventSystem.current.IsPointerOverGameObject(id)) continue; activeTouchId = id; lastTouchPos = pos; continue; } if (touch.touchId.ReadValue() != activeTouchId) continue; foundActiveTouch = true; Vector2 delta = pos - lastTouchPos; lastTouchPos = pos; rawX += delta.x * touchSensitivityX; rawY += delta.y * touchSensitivityY; } // Reset tracking if active touch released if (activeTouchId != -1 && !foundActiveTouch) { activeTouchId = -1; } } // ======================== MOUSE ======================== private void ReadMouseInput() { if (Mouse.current == null) return; // Only use mouse if right button is held (prevents accidental camera rotation) if (!Mouse.current.rightButton.isPressed) return; Vector2 mouseDelta = Mouse.current.delta.ReadValue(); rawX += mouseDelta.x * mouseSensitivityX * Time.unscaledDeltaTime; rawY += mouseDelta.y * mouseSensitivityY * Time.unscaledDeltaTime; } }