389 lines
15 KiB
C#
389 lines
15 KiB
C#
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;
|
||
}
|
||
}
|