using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraFollow : MonoBehaviour { [SerializeField] private Transform target; [SerializeField] private float smoothSpeed = 0.125f; [SerializeField] private Vector3 offset = new Vector3(0f, 10f, -10f); [SerializeField] private float rotationSpeed = 10f; [SerializeField] private LayerMask terrainLayer; [SerializeField] private float minDistanceToTerrain = 3f; [SerializeField] private float cameraRadius = 0.5f; [SerializeField] private float swipeSpeed = 0.3f; [SerializeField] private float swipeRotationSpeed = 20f; [SerializeField] private float horizontalEdgeThreshold = 400f; [SerializeField] private float verticalEdgeThreshold = 200f; [SerializeField] private bool invertHorizontalSwipe = false; [SerializeField] private bool invertVerticalSwipe = false; [SerializeField] private float lookAtTargetDelay = 0.5f; [SerializeField] private float lookAtTargetSpeed = 0.3f; private Vector3 currentVelocity; private Vector2 startTouchPosition; private Vector2 currentTouchPosition; private bool isSwiping = false; private float lastSwipeTime; private Vector3 originalOffset; [SerializeField] private float returnSpeed = 0.005f; [SerializeField] private float returnDelay = 100f; [SerializeField] private float zoomSpeed = 0.3f; private Quaternion originalRotation; private bool isReturningToOffset = false; private Coroutine returnToOffsetCoroutine; [SerializeField] private bool invertZoom = true; [SerializeField] private bool invertTwoFingerRotation = false; [SerializeField] private float damping = 3.0f; private bool shouldReturnToHorizontal = false; [SerializeField] private float horizontalReturnSpeed = 5.0f; private Quaternion originRotation; private bool shouldReturnToOriginalRotation = false; [SerializeField] private float edgeRotationSpeed = 10f; void Start() { originalOffset = offset; originalRotation = transform.rotation; originRotation = transform.rotation; } void Update() { if (!isReturningToOffset && returnToOffsetCoroutine == null) { returnToOffsetCoroutine = StartCoroutine(MoveCameraBackToOffset()); } if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began) { startTouchPosition = touch.position; isSwiping = true; shouldReturnToOriginalRotation = true; } else if (touch.phase == TouchPhase.Moved && isSwiping) { currentTouchPosition = touch.position; startTouchPosition = currentTouchPosition; lastSwipeTime = Time.time; } if (touch.phase == TouchPhase.Ended) { isSwiping = false; shouldReturnToOriginalRotation = false; } float rotationSpeed = swipeRotationSpeed; bool isNearHorizontalEdge = currentTouchPosition.x < horizontalEdgeThreshold || currentTouchPosition.x > Screen.width - horizontalEdgeThreshold; bool isNearVerticalEdge = currentTouchPosition.y < verticalEdgeThreshold || currentTouchPosition.y > Screen.height - verticalEdgeThreshold; if (isNearHorizontalEdge || isNearVerticalEdge) { if (currentTouchPosition.x < horizontalEdgeThreshold) { transform.RotateAround(target.position, Vector3.up, -edgeRotationSpeed * Time.deltaTime * (invertHorizontalSwipe ? -1 : 1)); } else if (currentTouchPosition.x > Screen.width - horizontalEdgeThreshold) { transform.RotateAround(target.position, Vector3.up, edgeRotationSpeed * Time.deltaTime * (invertHorizontalSwipe ? -1 : 1)); } if (currentTouchPosition.y < verticalEdgeThreshold) { transform.RotateAround(target.position, transform.right, edgeRotationSpeed * Time.deltaTime * (invertVerticalSwipe ? -1 : 1)); } else if (currentTouchPosition.y > Screen.height - verticalEdgeThreshold) { transform.RotateAround(target.position, transform.right, -edgeRotationSpeed * Time.deltaTime * (invertVerticalSwipe ? -1 : 1)); } } else { Vector2 deltaPosition = touch.deltaPosition; offset += transform.right * deltaPosition.x * swipeSpeed * Time.deltaTime * (invertHorizontalSwipe ? -1 : 1); offset.y += deltaPosition.y * swipeSpeed * Time.deltaTime * (invertVerticalSwipe ? -1 : 1); } if (shouldReturnToOriginalRotation && touch.phase == TouchPhase.Moved && isSwiping) { float angleToOriginalRotation = Quaternion.Angle(transform.rotation, originRotation); Quaternion targetRotation = Quaternion.Slerp(transform.rotation, originRotation, rotationSpeed * Time.deltaTime); transform.rotation = targetRotation; if (angleToOriginalRotation < 1.0f) { shouldReturnToOriginalRotation = false; } } else if (Input.touchCount == 2) { Touch touchZero = Input.GetTouch(0); Touch touchOne = Input.GetTouch(1); Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition; Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition; float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude; float touchDeltaMag = (touchZero.position - touchOne.position).magnitude; float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag; if (invertZoom) { deltaMagnitudeDiff *= -1; } offset.z += deltaMagnitudeDiff * zoomSpeed * Time.deltaTime; Vector2 currentDirection = touchOne.position - touchZero.position; Vector2 previousDirection = touchOnePrevPos - touchZeroPrevPos; float angle = Vector2.SignedAngle(previousDirection, currentDirection); transform.Rotate(0f, 0f, angle * swipeRotationSpeed * Time.deltaTime, Space.Self); } if (!isSwiping && Time.time - lastSwipeTime > lookAtTargetDelay) { Quaternion targetRotation = Quaternion.LookRotation(target.position + target.forward - transform.position); transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, lookAtTargetSpeed * Time.deltaTime); } if (shouldReturnToHorizontal && !isSwiping) { transform.rotation = Quaternion.Lerp(transform.rotation, originalRotation, horizontalReturnSpeed * Time.deltaTime); } } } void FixedUpdate() { Vector3 desiredPosition = target.position + target.forward * offset.z + target.right * offset.x + target.up * offset.y; RaycastHit hit; if (Physics.SphereCast(desiredPosition + Vector3.up * 1000f, cameraRadius, Vector3.down, out hit, Mathf.Infinity, terrainLayer)) { desiredPosition.y = Mathf.Max(desiredPosition.y, hit.point.y + offset.y + cameraRadius); } if (Physics.SphereCast(transform.position, cameraRadius, (desiredPosition - transform.position).normalized, out hit, Vector3.Distance(transform.position, desiredPosition), terrainLayer)) { desiredPosition = hit.point + hit.normal * cameraRadius + minDistanceToTerrain * hit.normal; } transform.position = Vector3.SmoothDamp(transform.position, desiredPosition, ref currentVelocity, smoothSpeed * (1 / damping)); if (!isSwiping && Time.time - lastSwipeTime > lookAtTargetDelay) { Quaternion targetRotation = Quaternion.LookRotation(target.position + target.forward - transform.position); transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, smoothSpeed * Time.deltaTime); } } IEnumerator MoveCameraBackToOffset() { yield return new WaitForSeconds(returnDelay); isReturningToOffset = true; while (Vector3.Distance(offset, originalOffset) > 0.1f) { offset = Vector3.Lerp(offset, originalOffset, returnSpeed * Time.deltaTime); yield return null; } offset = originalOffset; isReturningToOffset = false; returnToOffsetCoroutine = null; } }