306 lines
12 KiB
C#
306 lines
12 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; // 射线检测提前量
|
||
|
||
|
||
//控制变量
|
||
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;
|
||
}
|
||
|
||
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(0) || Input.GetMouseButtonDown(2))
|
||
{
|
||
viewPos_0 = mainCamera.ScreenToViewportPoint(Input.mousePosition);
|
||
startCameraPos = transform.position;
|
||
startCameraEuler = transform.eulerAngles;
|
||
}
|
||
if (Input.GetMouseButton(0) || 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;
|
||
}
|
||
//旋转控制
|
||
if (Input.GetMouseButtonDown(1))
|
||
{
|
||
|
||
viewPos_1 = mainCamera.ScreenToViewportPoint(Input.mousePosition);
|
||
startCameraPos = transform.position;
|
||
startCameraEuler = transform.eulerAngles;
|
||
}
|
||
if (Input.GetMouseButton(1))
|
||
{
|
||
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() * 0.2f;
|
||
// targetCameraPos += transform.forward * moveSpeed * Input.mouseScrollDelta.y;
|
||
float moveSpeed = GetMoveSpeed() * zoomSpeed;
|
||
Vector3 newTargetPos = targetCameraPos + transform.forward * moveSpeed * Input.mouseScrollDelta.y;
|
||
newTargetPos = CheckCollisionOnMove(transform.position, newTargetPos);
|
||
targetCameraPos = newTargetPos;
|
||
}
|
||
|
||
//相机运动
|
||
LimitCamera();
|
||
// CheckCollision(); // 检查碰撞
|
||
//控制相机运动
|
||
// 控制相机运动
|
||
if (Vector3.Distance(transform.position, targetCameraPos) > 0.1f)
|
||
{
|
||
transform.position = Vector3.Lerp(transform.position, targetCameraPos, 5f * Time.deltaTime);
|
||
// Debug.Log("position"+transform.position);
|
||
}
|
||
|
||
// 控制相机旋转
|
||
if (Quaternion.Angle(transform.rotation, Quaternion.Euler(targetCameraEuler)) > 0.1f)
|
||
{
|
||
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.Euler(targetCameraEuler), 5f * Time.deltaTime);
|
||
// Debug.Log("rotation"+transform.eulerAngles);
|
||
}
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
//开启相机控制
|
||
// 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()
|
||
{
|
||
|
||
float highDis = (transform.position.y - minHeight);
|
||
float speed = moveSpeed + moveSpeed * highDis;
|
||
speed = Mathf.RoundToInt(speed);
|
||
return speed;
|
||
}
|
||
|
||
//限制相机位置,角度
|
||
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);
|
||
}
|
||
|
||
// // 检查碰撞
|
||
// void CheckCollisionOnMove(Vector3 startPos, Vector3 targetPos)
|
||
// {
|
||
// RaycastHit hit;
|
||
// Vector3 direction = targetCameraPos - transform.position;
|
||
// float distance = direction.magnitude;
|
||
|
||
// if (Physics.Raycast(transform.position, direction.normalized, out hit, distance))
|
||
// {
|
||
// targetCameraPos = hit.point;
|
||
// }
|
||
// }
|
||
// 修改CheckCollisionOnMove方法,增加垂直方向检测
|
||
Vector3 CheckCollisionOnMove(Vector3 startPos, Vector3 targetPos)
|
||
{
|
||
Vector3 direction = targetPos - startPos;
|
||
float distance = direction.magnitude;
|
||
|
||
// 存储最终的安全位置
|
||
Vector3 safePosition = targetPos;
|
||
|
||
// 1. 检测移动路径上的障碍物(包括水平和垂直方向)
|
||
if (distance > 0)
|
||
{
|
||
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))
|
||
{
|
||
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))
|
||
{
|
||
float verticalSafeDistance = hit.distance - raycastOffset - sphereRadius;
|
||
safePosition.y = startPos.y + verticalSafeDistance;
|
||
collisionDetected = true;
|
||
}
|
||
}
|
||
|
||
if (collisionDetected)
|
||
{
|
||
// 声明地面检测变量(移到这里避免作用域冲突)
|
||
Ray groundRay1;
|
||
float maxGroundDistance1;
|
||
RaycastHit groundHit1;
|
||
|
||
// 如果检测到碰撞,进行地面高度验证
|
||
groundRay1 = new Ray(safePosition + Vector3.up, Vector3.down);
|
||
maxGroundDistance1 = 0.5f;
|
||
|
||
if (Physics.Raycast(groundRay1, out groundHit1, maxGroundDistance1))
|
||
{
|
||
float minSafeHeight = groundHit1.point.y + minHeight;
|
||
if (safePosition.y < minSafeHeight)
|
||
{
|
||
safePosition.y = minSafeHeight;
|
||
}
|
||
}
|
||
|
||
return safePosition;
|
||
}
|
||
}
|
||
|
||
// 2. 如果没有检测到碰撞,只进行地面高度验证
|
||
// 重用之前声明的变量
|
||
Ray groundRay = new Ray(targetPos + Vector3.up, Vector3.down);
|
||
float maxGroundDistance = 0.5f;
|
||
|
||
if (Physics.Raycast(groundRay, out RaycastHit groundHit, maxGroundDistance))
|
||
{
|
||
float minSafeHeight = groundHit.point.y + minHeight;
|
||
if (targetPos.y < minSafeHeight)
|
||
{
|
||
targetPos.y = minSafeHeight;
|
||
}
|
||
}
|
||
|
||
return targetPos;
|
||
}
|
||
|
||
}
|