fix:代码提交
This commit is contained in:
388
Assets/Scripts/App/CameraControl.cs
Normal file
388
Assets/Scripts/App/CameraControl.cs
Normal file
@@ -0,0 +1,388 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user