using DG.Tweening; using QFramework; using System; using UnityEngine; using UnityEngine.EventSystems; public class Show3DCamera : MonoBehaviour { public Vector3 targetPos; // 围绕旋转的目标点 public float rotateSpeed; // 旋转速度 public float moveSpeed; // 移动速度 public float distance; // 相机与目标的距离 public float distanceMin; public float distanceMax; public Vector2 pitchMinMax/* = new Vector2(-20, 80)*/; // 相机俯仰角范围 private Vector3 offset; // 相机与目标的偏移量 private float yaw = 0f; // 偏航角(左右旋转) private float pitch = 0f; // 俯仰角(上下旋转) public static Show3DCamera instance; RectTransform inputRect; Camera self; private GameObject lastHitObject = null; public RenderTexture texture; public bool lockMove = false; private const float DRAG_THRESHOLD = 1f; // 拖拽阈值 private Vector2 mouseDownPosition; // 记录鼠标按下时的位置 public enum RotationType { Orbit, // 原有立式旋转 Spherical // 新增球形旋转 } public RotationType type = RotationType.Orbit; Transform target; Vector3 targetPosition; Vector3 targetRotate; private float prevTouchDistance; // 存储上一帧双指距离 private void Awake() { instance = this; self = transform.GetComponent(); DontDestroyOnLoad(this); gameObject.SetActive(false); TypeEventSystem.Global.Register(OnLockEvent).UnRegisterWhenDisabled(this); } private void OnEnable() { #if Turing gameObject.SetActive(false); #endif } private void OnLockEvent(OnLock islock) { this.lockMove = islock.isLock; } public void Set(Transform target, float rotateSpeed = 10, float moveSpeed = 0.1f, float distance = 0.1f, float pitchMin = -20, float pitchMax = 80, float distanceMin = 0.2f, float distanceMax = 20f, RectTransform inputRect = null, bool isRenderTexture = true, float moveTime = -1) { if (target == null) { Debug.LogError("Target is not assigned!"); return; } this.target = target; this.targetPosition = target.position; this.targetRotate = target.eulerAngles; yaw = 0; pitch = 0; // 初始化相机位置 this.inputRect = inputRect; this.targetPos = target.transform.position; //this.rotateSpeed = rotateSpeed; //this.moveSpeed = moveSpeed; //this.distance = distance; //this.distanceMin = distanceMin; //this.distanceMax = distanceMax; //this.pitchMinMax = new Vector2(pitchMin, pitchMax); // 初始化相机位置 offset = new Vector3(0, 0, -distance); if (isRenderTexture) { self.targetTexture = texture; } else { self.targetTexture = null; } UpdateCameraPosition(moveTime); } public void ResetCamera(Transform target, RectTransform inputRect = null, bool isRenderTexture = false) { if (target == null) { Debug.LogError("Target is not assigned!"); return; } this.target = target; this.targetPosition = target.position; this.targetRotate = target.eulerAngles; yaw = 0; pitch = 0; // 初始化相机位置 this.inputRect = inputRect; this.targetPos = target.transform.position; // 初始化相机位置 offset = new Vector3(0, 0, -distance); if (isRenderTexture) { self.targetTexture = texture; } else { self.targetTexture = null; } UpdateCameraPosition(-1); } void Update() { if (targetPos != null && lockMove == false && EventSystem.current.IsPointerOverGameObject() == false) { // 优先处理三指操作 if (HandleThreeFingerDrag()) return; bool isTouching = Input.touchCount > 0; // 新增:在旋转前先处理双指缩放 bool isZooming = false; if (isTouching && Input.touchCount == 2) { HandleTouchZoom(); isZooming = true; // 标记正在缩放 } // 处理触摸开始(排除双指情况) if (isTouching && Input.touchCount == 1) { if (Input.GetTouch(0).phase == TouchPhase.Began) { mouseDownPosition = Input.GetTouch(0).position; } } // 处理鼠标按下 else if (Input.GetMouseButtonDown(0)) { mouseDownPosition = Input.mousePosition; } bool shouldRotate = false; if (Input.GetMouseButton(0)) { // 使用现有参数计算拖拽距离 float dragDistance = Vector2.Distance(Input.mousePosition, mouseDownPosition); shouldRotate = dragDistance > DRAG_THRESHOLD; } // 修改后的旋转条件(排除缩放状态) if (!isZooming && (isTouching ? (Input.GetTouch(0).phase == TouchPhase.Moved) : shouldRotate)) { RotateCamera(); } // 鼠标滚轮缩放 float scroll = Input.GetAxis("Mouse ScrollWheel"); if (scroll != 0) { ZoomCamera(scroll); } // 按住鼠标右键时移动目标点 if (Input.GetMouseButton(1)) { MoveTarget(); } DetectHoveredObject(); } } // 唯一的三指处理方法(返回bool用于阻断其他操作) private bool HandleThreeFingerDrag() { if (Input.touchCount == 5) { // 计算三个触点的平均移动量 Vector2 totalDelta = Vector2.zero; foreach (Touch t in Input.touches) { totalDelta += t.deltaPosition; } Vector2 delta = totalDelta / 3f; // DPI自适应处理 float dpiScale = Screen.dpi == 0 ? 1 : Screen.dpi / 160f; float sensitivity = moveSpeed * 0.1f / dpiScale; // 应用移动阈值(2像素) if (delta.magnitude > 2f) { // 调用修改后的移动方法 MoveTarget(delta.x * sensitivity, delta.y * sensitivity); } // 阻断其他触摸操作 return true; } return false; } // 新增双指缩放处理方法 // 类变量区添加 private float zoomSmoothVelocity; // 平滑速度缓存 [SerializeField] private float zoomSmoothTime = 0.1f; // 缩放平滑时间 private void HandleTouchZoom() { if (Input.touchCount == 2) { Touch touch0 = Input.GetTouch(0); Touch touch1 = Input.GetTouch(1); // 当双指操作时重置旋转相关变量 if (touch0.phase == TouchPhase.Began || touch1.phase == TouchPhase.Began) { yaw = transform.eulerAngles.y; // 保持当前旋转角度 pitch = transform.eulerAngles.x; mouseDownPosition = Vector2.zero; } Vector2 touch0Pos = touch0.position; Vector2 touch1Pos = touch1.position; float currentDistance = Vector2.Distance(touch0Pos, touch1Pos); // DPI自适应计算 float dpi = Screen.dpi == 0 ? 200 : Screen.dpi; float zoomFactor = 0.01f * (200 / dpi); // 基准DPI为200 if (touch0.phase == TouchPhase.Began || touch1.phase == TouchPhase.Began) { prevTouchDistance = currentDistance; zoomSmoothVelocity = 0; // 重置平滑速度 } else if (touch0.phase == TouchPhase.Moved || touch1.phase == TouchPhase.Moved) { float deltaDistance = currentDistance - prevTouchDistance; // 仅触摸缩放使用平滑 float targetDistance = distance - deltaDistance * zoomFactor; targetDistance = Mathf.Clamp(targetDistance, distanceMin, distanceMax); distance = Mathf.SmoothDamp( distance, targetDistance, ref zoomSmoothVelocity, zoomSmoothTime ); offset = new Vector3(0, 0, -distance); UpdateCameraPosition(); prevTouchDistance = currentDistance; } } } // 检测鼠标悬停的物体 public void DetectHoveredObject() { GameObject obj = null; Vector2 mousePosition = Input.mousePosition; if (inputRect != null) { var pos = (mousePosition - (Vector2)inputRect.position) / inputRect.lossyScale - inputRect.rect.position; mousePosition = pos / inputRect.rect.size; } var ray = self.ViewportPointToRay(mousePosition); RaycastHit raycastHit; if (Physics.Raycast(ray, out raycastHit)) { //Debug.Log(raycastHit.transform.name); obj = raycastHit.transform.gameObject; // 如果击中的物体与上一次击中的物体不同,表示射线进入了新物体 if (obj != lastHitObject) { // 触发进入事件 OnMouseEnterObj(obj); // 如果上一次击中的物体不为空,触发离开事件 if (lastHitObject != null && lastHitObject != obj) { OnMouseExitObj(lastHitObject); // 更新上一次击中的物体 lastHitObject = obj; } } } else { // 如果射线没有击中任何物体,且上一次击中的物体不为空,触发离开事件 if (lastHitObject != null) { OnMouseExitObj(lastHitObject); lastHitObject = null; } } } /// /// 聚焦某个物体 /// public void FocusObj(Vector3 target, float distance = 1f, float moveTime = -1) { // 可以根据需要调整这个距离 Vector3 cameraPos = target - transform.forward * distance; targetPos = target; this.distance = distance; offset = new Vector3(0, 0, -distance); if (moveTime != -1) { transform.DOMove(cameraPos, moveTime); } else { transform.position = cameraPos; } } public void OnMouseEnterObj(GameObject obj) { TipItem tip = obj.GetComponent(); if (tip != null) { tip.OnEnter(); } } public void OnMouseExitObj(GameObject obj) { TipItem tip = obj.GetComponent(); if (tip != null) { tip.OnExit(); } } // 修改后的移动方法(统一处理输入源) private void MoveTarget(float mouseX = 0, float mouseY = 0) { // 自动判断输入源 if (Mathf.Approximately(mouseX, 0) && Mathf.Approximately(mouseY, 0)) { mouseX = Input.GetAxis("Mouse X") * moveSpeed; mouseY = Input.GetAxis("Mouse Y") * moveSpeed; } transform.Translate(new Vector3(-mouseX, -mouseY, 0)); targetPos += new Vector3(-mouseX, -mouseY, 0); } // 缩放相机 private void ZoomCamera(float scroll) { distance -= scroll * 5f; // 调整缩放速度 distance = Mathf.Clamp(distance, distanceMin, distanceMax); // 限制距离范围 offset = new Vector3(0, 0, -distance); UpdateCameraPosition(); } // 修改RotateCamera方法 private void RotateCamera() { // 优先使用触摸输入 float deltaX = 0, deltaY = 0; // 触屏处理 if (Input.touchCount == 1) { Touch touch = Input.GetTouch(0); deltaX = touch.deltaPosition.x; deltaY = touch.deltaPosition.y; } // 鼠标处理 else { deltaX = Input.GetAxis("Mouse X") * 10; // 保持原有灵敏度 deltaY = Input.GetAxis("Mouse Y") * 10; // 当累计移动量超过阈值时才生效 if (Mathf.Abs(deltaX) < DRAG_THRESHOLD && Mathf.Abs(deltaY) < DRAG_THRESHOLD) return; } // 应用DPI缩放(关键!) float dpiScale = Screen.dpi == 0 ? 1 : Screen.dpi / 160f; deltaX *= rotateSpeed * Time.deltaTime / dpiScale; deltaY *= rotateSpeed * Time.deltaTime / dpiScale; switch (type) { case RotationType.Orbit: yaw += deltaX; pitch -= deltaY; pitch = Mathf.Clamp(pitch, pitchMinMax.x, pitchMinMax.y); UpdateCameraPosition(); break; case RotationType.Spherical: target.Rotate(Vector3.up, deltaX, Space.World); target.Rotate(Vector3.right, -deltaY, Space.Self); break; } } public void ChangeMode(RotationType type) { this.type = type; target.position = targetPosition; target.eulerAngles = targetRotate; ResetCamera(this.target); } // 更新相机位置和朝向 private void UpdateCameraPosition(float moveTime = -1) { // 计算旋转后的偏移量 Quaternion rotation = Quaternion.Euler(pitch, yaw, 0); Vector3 rotatedOffset = rotation * offset; // 更新相机位置 if (moveTime != -1) { transform.DOMove(targetPos + rotatedOffset, moveTime).onUpdate = () => // 相机始终朝向目标点 transform.LookAt(targetPos); } else { transform.position = targetPos + rotatedOffset; // 相机始终朝向目标点 transform.LookAt(targetPos); } #if Turing TypeEventSystem.Global.Send(new OnUpdatePos() { pos = gameObject.Position(), rot = gameObject.LocalEulerAngles() }); #endif } }