using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; public class CameraControl : MonoBehaviour { [Header("移动速度")] public float moveSpeed = 1.2f; [Header("旋转速度")] public float rotateSpeed = 45f; [Header("缩放速度")] public float zoomSpeed = 0.2f; [Header("最小高度")] public float minHeight = 0.1f; [Header("最大高度")] public float maxHeight = 100f; [Header("地图中心")] public Vector3 mapCenter = Vector3.zero; [Header("最大距离")] public float maxDistance = 3000f; [Header("最小角度")] public float minAngleX = -60f; [Header("最大角度")] public float maxAngleX = 88f; [Header("射线检测提前量")] public float raycastOffset = 0.1f; // 射线检测提前量 [SerializeField] private LayerMask collisionLayers; // 可碰撞的图层 [SerializeField] private string[] obstacleLayerNames = new string[] { "ObstacleLayer" }; // 障碍物图层名称数组 public float minMoveSpeed = 5f; // 最小移动速度,根据需求设置 // 高度阈值数组 (按升序排列) [SerializeField] private float[] thresholds = { 27f, 50f, 100f, 150f, 200f }; // 对应速度值数组 (与阈值一一对应) [SerializeField] private float[] speeds = { 3f, 15f, 30f, 50f, 80f }; //控制变量 private Vector3 viewPos_0; //鼠标左键初始坐标 private Vector3 viewPos_1; //鼠标右键初始坐标 private Vector3 startCameraPos; //相机初始位置 private Vector3 targetCameraPos; //相机目标位置 private Vector2 startCameraEuler; //初始角度 private Vector2 targetCameraEuler; //目标角度 //相机控制 private Camera mainCamera; private bool isCameraCtrl = true; //相机是否可控制 private float screenAdapter = 1f; //屏幕长度比 //相机操作有效性判断 private bool isClickUI = false; private void Awake() { mainCamera = Camera.main; screenAdapter = Screen.width / (float)Screen.height; targetCameraPos = transform.position; targetCameraEuler = transform.eulerAngles; if (collisionLayers == 0 && obstacleLayerNames.Length > 0) { collisionLayers = LayerMask.GetMask(obstacleLayerNames); } } void Update() { //相机控制 if (isCameraCtrl) { //操作有效性判断 if (EventSystem.current != null) { if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2)) { isClickUI = EventSystem.current.IsPointerOverGameObject(); } if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2)) { isClickUI = false; } } if (!isClickUI) { //平移控制 if (Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2)) { viewPos_0 = mainCamera.ScreenToViewportPoint(Input.mousePosition); startCameraPos = transform.position; startCameraEuler = transform.eulerAngles; } if (Input.GetMouseButton(1) || Input.GetMouseButton(2)) { Vector3 currentViewPos = mainCamera.ScreenToViewportPoint(Input.mousePosition); Vector3 dis = currentViewPos - viewPos_0; dis = dis * GetMoveSpeed(); // 获取相机的右向量和上向量 Vector3 right = transform.right; Vector3 up = transform.up; // 分别计算水平和垂直方向的位移 float horizontalDis = -dis.x * screenAdapter; float verticalDis = -dis.y * screenAdapter; // 根据相机的右向量和上向量计算最终位移 Vector3 finalDis = right * horizontalDis + up * verticalDis; targetCameraPos = startCameraPos + finalDis; // Vector3 newTargetPos = startCameraPos + finalDis; // newTargetPos = CheckCollisionOnMove(transform.position, newTargetPos); // targetCameraPos = newTargetPos; // Debug.Log("targetCameraPos" + targetCameraPos); } //旋转控制 if (Input.GetMouseButtonDown(0)) { viewPos_1 = mainCamera.ScreenToViewportPoint(Input.mousePosition); startCameraPos = transform.position; startCameraEuler = transform.eulerAngles; } if (Input.GetMouseButton(0)) { Vector2 angle = (mainCamera.ScreenToViewportPoint(Input.mousePosition) - viewPos_1) * rotateSpeed; angle = new Vector3(-angle.y, angle.x * screenAdapter); targetCameraEuler = startCameraEuler + angle; } //缩进控制 if (Input.mouseScrollDelta != Vector2.zero) { float moveSpeed = GetMoveSpeed() * zoomSpeed; targetCameraPos = transform.position + transform.forward * moveSpeed * Input.mouseScrollDelta.y; // Vector3 newTargetPos = targetCameraPos + transform.forward * moveSpeed * Input.mouseScrollDelta.y; // newTargetPos = CheckCollisionOnMove(transform.position, newTargetPos); // targetCameraPos = newTargetPos; } //相机运动 LimitCamera(); // CheckCollision(); // 检查碰撞 //控制相机运动 // 控制相机运动 if (!isCheckCollisionOnMove(transform.position, targetCameraPos)) // 无碰撞时执行移动 { if (Vector3.Distance(transform.position, targetCameraPos) > 0.1f) { transform.position = Vector3.Lerp(transform.position, targetCameraPos, 5f * Time.deltaTime); } // 控制相机旋转 if (Quaternion.Angle(transform.rotation, Quaternion.Euler(targetCameraEuler)) > 0.1f) { transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(targetCameraEuler), 5f * Time.deltaTime); } } // if (Vector3.Distance(transform.position, targetCameraPos) > 0.1f) // { // transform.position = Vector3.Lerp(transform.position, targetCameraPos, 5f * Time.deltaTime); // } // // 控制相机旋转 // if (Quaternion.Angle(transform.rotation, Quaternion.Euler(targetCameraEuler)) > 0.1f) // { // transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(targetCameraEuler), 5f * Time.deltaTime); // } } } } public bool isCheckCollisionOnMove(Vector3 position, Vector3 targetCameraPos) { Vector3 newPosition = CheckCollisionOnMove(position, targetCameraPos); // 手动判断是否发生碰撞 bool hasCollision = newPosition != targetCameraPos; return hasCollision; } //开启相机控制 public void StartCameraControl() { isCameraCtrl = true; isClickUI = false; viewPos_0 = mainCamera.ScreenToViewportPoint(Input.mousePosition); viewPos_1 = mainCamera.ScreenToViewportPoint(Input.mousePosition); startCameraPos = transform.position; startCameraEuler = transform.eulerAngles; targetCameraPos = transform.position; targetCameraEuler = transform.eulerAngles; } //停止相机控制 public void StopCameraControl() { isCameraCtrl = false; } //获取相机移动速度 float GetMoveSpeed() { // // 计算高度差(确保不小于0,避免出现负速度) // float highDis = Mathf.Max(transform.position.y - minHeight, 0f); // // 计算基础速度(基于高度的动态速度) // float dynamicSpeed = moveSpeed + moveSpeed * highDis; // // 确保速度不低于最小值,同时保留向上浮动的可能性 // float finalSpeed = Mathf.Max(dynamicSpeed, minMoveSpeed); // // 可选:限制最大速度,防止过高高度下速度过快 // // finalSpeed = Mathf.Min(finalSpeed, maxMoveSpeed); // // 保留整数化处理(如果需要) // finalSpeed = Mathf.RoundToInt(finalSpeed); // return finalSpeed; // return transform.position.y > 80 ? 80f : transform.position.y > 30 ? 40f : 5f; // // 高度阈值数组 (按升序排列) float yPosition = transform.position.y; // 检查每个阈值,返回对应速度 for (int i = 0; i < thresholds.Length; i++) { if (yPosition < thresholds[i]) { return speeds[i]; } } Debug.Log("Speed: " + speeds[speeds.Length - 1]); // 超过最高阈值,返回最高速度 return speeds[speeds.Length - 1]; } //限制相机位置,角度 void LimitCamera() { //位置限制 targetCameraPos.y = Mathf.Clamp(targetCameraPos.y, minHeight, maxHeight); float dis = Vector3.Distance(targetCameraPos, mapCenter); if (dis > maxDistance) { Vector3 dir = (targetCameraPos - mapCenter).normalized; targetCameraPos = mapCenter + dir * maxDistance; } //角度限制 if (targetCameraEuler.x > 180) { targetCameraEuler.x -= 360f; } if (targetCameraEuler.x < -180) { targetCameraEuler.x += 360; } if (targetCameraEuler.y > 180) { targetCameraEuler.y -= 360f; } if (targetCameraEuler.y < -180) { targetCameraEuler.y += 360; } targetCameraEuler.x = Mathf.Clamp(targetCameraEuler.x, minAngleX, maxAngleX); } // 修改CheckCollisionOnMove方法,增加垂直方向检测 Vector3 CheckCollisionOnMove(Vector3 startPos, Vector3 targetPos) { Vector3 direction = targetPos - startPos; float distance = direction.magnitude; Vector3 safePosition = targetPos; // 确保射线检测始终有足够的距离 if (distance < 0.01f) return ApplyGroundCheck(safePosition); float sphereRadius = 0.1f; Bounds startBounds = new Bounds(startPos, Vector3.one * sphereRadius * 2); Bounds endBounds = new Bounds(targetPos, Vector3.one * sphereRadius * 2); Bounds movementBounds = new Bounds(); movementBounds.Encapsulate(startBounds); movementBounds.Encapsulate(endBounds); bool collisionDetected = false; // 主方向检测 if (Physics.SphereCast(startPos, sphereRadius, direction.normalized, out RaycastHit hit, distance + raycastOffset, collisionLayers)) { float safeDistance = hit.distance - raycastOffset - sphereRadius; safePosition = startPos + direction.normalized * Mathf.Max(safeDistance, 0); collisionDetected = true; } // 顶部检测 if (direction.y > 0.1f) { Vector3 topStart = startPos + Vector3.up * sphereRadius; Vector3 topDirection = Vector3.up * (endBounds.max.y - startBounds.max.y); if (Physics.SphereCast(topStart, sphereRadius, topDirection.normalized, out hit, topDirection.magnitude + raycastOffset, collisionLayers)) { float verticalSafeDistance = hit.distance - raycastOffset - sphereRadius; safePosition.y = startPos.y + verticalSafeDistance; collisionDetected = true; } } // 底部检测(防止向下穿透) if (direction.y < -0.1f) { Vector3 bottomStart = startPos - Vector3.up * sphereRadius; Vector3 bottomDirection = Vector3.down * Mathf.Abs(direction.y); if (Physics.SphereCast(bottomStart, sphereRadius, bottomDirection.normalized, out hit, bottomDirection.magnitude + raycastOffset, collisionLayers)) { float verticalSafeDistance = hit.distance - raycastOffset - sphereRadius; safePosition.y = startPos.y - verticalSafeDistance; collisionDetected = true; } } if (collisionDetected) { // 应用地面检测并确保最小高度 safePosition = ApplyGroundCheck(safePosition); return safePosition; } // 无碰撞时仍需确保地面高度 return ApplyGroundCheck(targetPos); } // 地面检测辅助方法 Vector3 ApplyGroundCheck(Vector3 position) { // 执行多次射线检测以提高精度 float groundDistance = float.MaxValue; bool groundFound = false; // 中心射线 Ray centerRay = new Ray(position + Vector3.up, Vector3.down); if (Physics.Raycast(centerRay, out RaycastHit centerHit, 10f, collisionLayers)) { groundDistance = centerHit.distance; groundFound = true; } // 四角射线检测(提高边缘精度) float checkRadius = 0.2f; Vector3[] checkDirections = new Vector3[] { Vector3.forward * checkRadius + Vector3.right * checkRadius, Vector3.forward * checkRadius - Vector3.right * checkRadius, -Vector3.forward * checkRadius + Vector3.right * checkRadius, -Vector3.forward * checkRadius - Vector3.right * checkRadius }; foreach (Vector3 dir in checkDirections) { Ray cornerRay = new Ray(position + Vector3.up + dir, Vector3.down); if (Physics.Raycast(cornerRay, out RaycastHit cornerHit, 10f, collisionLayers)) { if (cornerHit.distance < groundDistance) { groundDistance = cornerHit.distance; groundFound = true; } } } // 应用地面高度 if (groundFound) { float minSafeHeight = centerHit.point.y + minHeight; if (position.y < minSafeHeight) { position.y = minSafeHeight; } } return position; } }