Files
3d-fengji/Assets/Scripts/MainCamera/ControlMoving.cs
2026-05-20 17:05:47 +08:00

193 lines
7.1 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 UnityEngine;
using UnityEngine.EventSystems;
public class ControlMoving : MonoBehaviour
{
public float moveSpeed = 50f; // 右键平移速度
public float zoomSpeed = 10f; // 滚轮缩放速度
public DoubleClickToFocus doubleClickToFocusScript; // 引用焦点脚本,提供 core
public float rotationSensitivity = 0.2f; // 左键拖拽旋转灵敏度
private Camera cam;
private bool isRotating = false;
private Vector2 lastMousePos;
void Start()
{
cam = Camera.main;
Vector3 core = doubleClickToFocusScript.core;
if (doubleClickToFocusScript == null)
Debug.LogError("请在 Inspector 中把挂有 ClickToFocus 的物体拖给 inScript");
}
void Update()
{
//if (EventSystem.current != null && EventSystem.current.IsPointerOverGameObject())
//{
// return; // 直接 return你下面所有相机逻辑都不执行
//}
//if (SliderDragDetector.isSliderDragging)
// return;
//if (doubleClickToFocusScript == null) return;
Vector3 core = doubleClickToFocusScript.core;
// —— 右键平移 ——
if (Input.GetMouseButton(1))
{
moveSpeed = Vector3.Distance(core, transform.position) / 2f;//这里涉及到像素和实际空间米的关系转换
float mx = Input.GetAxis("Mouse X");
float my = Input.GetAxis("Mouse Y");
Vector3 desiredPosition = cam.transform.position + (-cam.transform.right * mx + -cam.transform.up * my) * moveSpeed * Time.deltaTime;
// 判断是否与障碍物发生碰撞
if (!WillCollideWithObstacle(desiredPosition))
{
cam.transform.position = desiredPosition;
}
}
// —— 滚轮缩放 ——
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Abs(scroll) > 0.001f)
{
Vector3 desiredPosition = cam.transform.position + cam.transform.forward * scroll * zoomSpeed;
// 判断是否与障碍物发生碰撞
if (!WillCollideWithObstacle(desiredPosition))
{
cam.transform.position = desiredPosition;
}
}
// —— 左键开始/结束旋转 ——
if (Input.GetMouseButtonDown(0))
{
isRotating = true;
lastMousePos = Input.mousePosition;
}
if (Input.GetMouseButtonUp(0))
{
isRotating = false;
}
// —— 左键拖拽RotateAround 实现 + 俯仰限制 ——
if (isRotating && Input.GetMouseButton(0))
{
Vector2 curr = Input.mousePosition;
Vector2 delta = (curr - lastMousePos) * rotationSensitivity;
lastMousePos = curr;
// 1. 水平绕世界 Y 轴旋转
//Vector3 desiredPosition = cam.transform.position;
//cam.transform.RotateAround(core, Vector3.up, delta.x);
Vector3 localPosHorizonal = cam.transform.position - core;
Quaternion rotationHorizonal = Quaternion.AngleAxis(delta.x, Vector3.up);
Vector3 newLocalPosHorizonal = rotationHorizonal * localPosHorizonal;//四元数*向量,旋转轴永远经过向量的起点
Vector3 newPosHorizonal = core + newLocalPosHorizonal;
// 检查旋转后是否发生碰撞
if (!WillCollideWithObstacle(newPosHorizonal))
{
// 如果没有碰撞,执行旋转
cam.transform.RotateAround(core, Vector3.up, delta.x);
}
else
{
// 如果碰撞了,就不动
}
// 2. 俯仰旋转
//Vector3 dir = (cam.transform.position - core).normalized;
//Vector3 flat = new Vector3(dir.x, 0f, dir.z).normalized;
//相机看的方向是正 z 方向。也是 forward 的方向。
float selfFlatX = cam.transform.forward.x;
float selfFlatZ = cam.transform.forward.z;
Vector3 selfFlat = new Vector3(selfFlatX, 0f, selfFlatZ).normalized;//把相机看的方向。在世界空间留下水平X Z。垂直量(Y)去掉。
//第一个参数和第二个参数绕着第三个参数(绕着 right 旋转),取值在-180~180
//cam.transform.right永远是垂直着你所看到的方向的所以它垂直着你的这个selfFlat和cam.transform.forward
//这么做是为了得到你的相机,抬头或者是低头度数是多少
//既然是从平面开始,终点是上或下,原则上应该遵循顺时针为正,逆时针为负。
float selfPitch = Vector3.SignedAngle(selfFlat, cam.transform.forward, cam.transform.right);//俯视为正仰视为负cam.transform.right这玩意是旋转轴
if (selfPitch <= 80f && selfPitch >= -80f)
{
float pitchDelta = delta.y * -1f;
Vector3 localPosVertical = cam.transform.position - core;
Quaternion rotationVertical = Quaternion.AngleAxis(pitchDelta, cam.transform.right);//构造出cam.transform.right绕着旋转pitchDelta的旋转值
Vector3 newPosVertical = core + rotationVertical * localPosVertical;//rotationVertical * localPosVertical这个量是旋转中心指向新位置的向量。
//某个物体的 rotation 等于一个四元数这是一种用法还有就是用四元素来相乘qA * qB = 先旋转 qB再旋转 qA
//我这里是用四元素乘一个向量是把这个向量按照四元素的内容来旋转可以想象把cam.transform.right平移到旋转中心他肯定很永远和地面平行并且和localPosVertical垂直。
//给localPosVertical这个向量按照rotationVertical旋转
// 判断是否与障碍物发生碰撞
//整体思路是先手动判断RotateAround的落脚点是否碰撞然后再使用
if (!WillCollideWithObstacle(newPosVertical))
{
cam.transform.RotateAround(core, cam.transform.right, pitchDelta);
}
}
else if (selfPitch > 80f)
{
float pitchDelta = delta.y * -1f;
if (pitchDelta < 0f)
{
cam.transform.RotateAround(core, cam.transform.right, pitchDelta);
}
}
else if (selfPitch < -80f)
{
float pitchDelta = delta.y * -1f;
if (pitchDelta > 0f)
{
cam.transform.RotateAround(core, cam.transform.right, pitchDelta);
}
}
}
}
// 碰撞检测方法
bool WillCollideWithObstacle(Vector3 targetPosition)
{
// 计算从当前位置到目标位置的方向和距离
Vector3 moveDir = targetPosition - cam.transform.position;
float moveDist = moveDir.magnitude;
// 使用 SphereCast 检查即将到达的位置是否会与障碍物发生碰撞
RaycastHit hit;
if (Physics.SphereCast(cam.transform.position, 0.08f, moveDir, out hit, moveDist, LayerMask.GetMask("Default"), QueryTriggerInteraction.Ignore) )
{
// 如果检测到碰撞,返回 true
Debug.Log("collider");
return true;
}
else if ( Mathf.Pow(targetPosition.x+36f, 2f)+ Mathf.Pow(targetPosition.z+15f , 2f)<250000f && targetPosition.y<300f)
{
// 如果在范围内,返回 false
return false;
}
else
Debug.Log("outRange");
return true;//这个true是为了有返回值实际上走不到这一步
}
}