using UnityEngine; using Cinemachine; using System.Collections; using Unity.BossRoom.CameraUtils; public class EdgePanningController : MonoBehaviour { public CinemachineFreeLook virtualCamera; private CinemachineCameraOffset cameraOffset; private Coroutine shakeCoroutine; public float edgeThreshold = 50f; // Edge detection distance from screen edges public float resetRadiusPercentage = 3f; // Center reset radius in percentage of screen public float panSpeed = 0.1f; // How much to offset per frame public float resetSpeed = 2f; // Speed of resetting panning public Vector2 panLimit = new Vector2(0.5f, 0.5f); // Max panning limit (x, y) private Vector3 defaultOffset; private Vector3 panningOffset; private bool isPanning = false; private bool targetFound = false; private bool shouldReset = false; // Flag to trigger reset immediately private Transform targetCharacter; // The character the camera follows private void Start() { cameraOffset = virtualCamera.GetComponent(); if (cameraOffset == null) { Debug.LogError("[EdgePanningController] No CinemachineCameraOffset component found! Add it to the FreeLook Camera."); return; } defaultOffset = cameraOffset.m_Offset; panningOffset = defaultOffset; } private void Awake() { CameraController.OnCameraAttached += SetTargetCharacter; } private void OnDestroy() { CameraController.OnCameraAttached -= SetTargetCharacter; } private void SetTargetCharacter(Transform characterTransform) { targetCharacter = characterTransform.parent; targetFound = true; } private void Update() { if (targetFound) HandleEdgePanning(); } private void HandleEdgePanning() { if (targetCharacter == null) return; Vector3 newOffset = panningOffset; Vector3 mousePos = Input.mousePosition; float screenWidth = Screen.width; float screenHeight = Screen.height; // Center Reset Radius (3% of screen size) float resetRadiusX = screenWidth * (resetRadiusPercentage / 100f); float resetRadiusY = screenHeight * (resetRadiusPercentage / 100f); Vector2 screenCenter = new Vector2(screenWidth / 2, screenHeight / 2); bool isNearCenter = Mathf.Abs(mousePos.x - screenCenter.x) <= resetRadiusX && Mathf.Abs(mousePos.y - screenCenter.y) <= resetRadiusY; bool isHoveringTarget = IsCursorOverTargetCharacter(); // ✅ If the cursor touches the center or target **even for a millisecond**, trigger reset if (isNearCenter || isHoveringTarget) { shouldReset = true; // Set the flag so reset starts immediately } // ✅ If reset is triggered, smoothly return to default position if (shouldReset && isPanning) { cameraOffset.m_Offset = Vector3.Lerp(cameraOffset.m_Offset, defaultOffset, Time.deltaTime * resetSpeed); // ✅ Once back to default, stop resetting and reset flag if (Vector3.Distance(cameraOffset.m_Offset, defaultOffset) < 0.01f) { isPanning = false; panningOffset = defaultOffset; shouldReset = false; // Reset flag } return; } // If cursor is near the edges, start panning (up to limit) if (mousePos.x <= edgeThreshold) newOffset.x = Mathf.Max(defaultOffset.x - panLimit.x, newOffset.x - panSpeed); if (mousePos.x >= screenWidth - edgeThreshold) newOffset.x = Mathf.Min(defaultOffset.x + panLimit.x, newOffset.x + panSpeed); if (mousePos.y <= edgeThreshold) newOffset.y = Mathf.Max(defaultOffset.y - panLimit.y, newOffset.y - panSpeed); if (mousePos.y >= screenHeight - edgeThreshold) newOffset.y = Mathf.Min(defaultOffset.y + panLimit.y, newOffset.y + panSpeed); if (newOffset != panningOffset) { isPanning = true; } cameraOffset.m_Offset = Vector3.Lerp(cameraOffset.m_Offset, newOffset, Time.deltaTime * 3f); panningOffset = cameraOffset.m_Offset; } private bool IsCursorOverTargetCharacter() { if (targetCharacter == null) return false; Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity)) { return hit.transform == targetCharacter; } return false; } public void ShakeCamera(float duration = 0.3f, float magnitude = 0.2f) { if (shakeCoroutine != null) { StopCoroutine(shakeCoroutine); } shakeCoroutine = StartCoroutine(ShakeRoutine(duration, magnitude)); } private IEnumerator ShakeRoutine(float duration, float magnitude) { float elapsed = 0f; Vector3 originalOffset = defaultOffset; while (elapsed < duration) { float x = Random.Range(-1f, 1f) * magnitude; float y = Random.Range(-1f, 1f) * magnitude; float z = Random.Range(-1f, 1f) * magnitude; cameraOffset.m_Offset = originalOffset + new Vector3(x, y, z); elapsed += Time.deltaTime; yield return null; } cameraOffset.m_Offset = originalOffset; // Reset to default offset } }