Files
3d-ybskdq/Assets/Scripts/App/CameraControl.cs
zhangjiajia dcf1199970 fix:1
2026-03-05 11:30:53 +08:00

306 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}