2025-05-07 11:51:21 +08:00

465 lines
13 KiB
C#
Raw 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 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; // 记录鼠标按下时的位置
private bool isDoubleTouching = false; // 记录双指操作状态
private bool isZooming = false; // 记录双指缩放状态
public enum RotationType
{
Orbit, // 原有立式旋转
Spherical // 新增球形旋转
}
public RotationType type = RotationType.Orbit;
Transform target;
Vector3 targetPosition;
Vector3 targetRotate;
private float prevTouchDistance; // 存储上一帧双指距离
// 新增:跟踪当前旋转状态
private Quaternion currentRotation;
private void Awake()
{
instance = this;
self = transform.GetComponent<Camera>();
DontDestroyOnLoad(this);
gameObject.SetActive(false);
TypeEventSystem.Global.Register<OnLock>(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;
offset = new Vector3(0, 0, -distance);
if (isRenderTexture)
{
self.targetTexture = texture;
}
else
{
self.targetTexture = null;
}
// 初始化旋转状态
currentRotation = Quaternion.Euler(pitch, yaw, 0);
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;
}
// 重置旋转状态
currentRotation = Quaternion.Euler(pitch, yaw, 0);
UpdateCameraPosition(-1);
}
void Update()
{
if (targetPos != null && lockMove == false && EventSystem.current.IsPointerOverGameObject() == false)
{
// 优先处理三指操作
if (HandleThreeFingerDrag()) return;
bool isTouching = Input.touchCount > 0;
// 处理双指缩放
if (isTouching && Input.touchCount == 2)
{
HandleTouchZoom();
isZooming = true;
isDoubleTouching = true;
return;
}
else if (Input.touchCount == 0)
{
isDoubleTouching = false;
isZooming = false;
}
bool shouldRotate = false;
// 处理触摸开始(排除双指情况)
if (isTouching && Input.touchCount == 1 && !isDoubleTouching)
{
if (Input.GetTouch(0).phase == TouchPhase.Began)
{
mouseDownPosition = Input.GetTouch(0).position;
// 记录当前旋转状态
if (type == RotationType.Orbit)
{
currentRotation = Quaternion.Euler(pitch, yaw, 0);
}
}
if (Input.GetTouch(0).phase == TouchPhase.Moved)
{
float dragDistance = Vector2.Distance(Input.mousePosition, mouseDownPosition);
shouldRotate = dragDistance > DRAG_THRESHOLD;
}
}
// 处理鼠标按下
else if (Input.GetMouseButtonDown(0) && !isDoubleTouching)
{
mouseDownPosition = Input.mousePosition;
// 记录当前旋转状态
if (type == RotationType.Orbit)
{
currentRotation = Quaternion.Euler(pitch, yaw, 0);
}
}
if (Input.GetMouseButton(0) && !isDoubleTouching)
{
float dragDistance = Vector2.Distance(Input.mousePosition, mouseDownPosition);
shouldRotate = dragDistance > DRAG_THRESHOLD;
}
// 旋转相机
if (!isZooming && 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 == 3)
{
// 计算三个触点的平均移动量
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 void HandleTouchZoom()
{
if (Input.touchCount == 2)
{
Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1);
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);
if (touch0.phase == TouchPhase.Began || touch1.phase == TouchPhase.Began)
{
prevTouchDistance = currentDistance;
}
else if (touch0.phase == TouchPhase.Moved || touch1.phase == TouchPhase.Moved)
{
float deltaDistance = currentDistance - prevTouchDistance;
// 添加判断:如果双指距离变化小于某个阈值,则不进行缩放操作
if (Mathf.Abs(deltaDistance) > 1f)
{
float scroll = deltaDistance * zoomFactor;
ZoomCamera(scroll);
}
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))
{
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;
}
}
}
/// <summary>
/// 聚焦某个物体
/// </summary>
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<TipItem>();
if (tip != null)
{
tip.OnEnter();
}
}
public void OnMouseExitObj(GameObject obj)
{
TipItem tip = obj.GetComponent<TipItem>();
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)
{
if (type == RotationType.Orbit)
{
// 使用四元数计算旋转后的偏移量
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);
}
}
else if (type == RotationType.Spherical)
{
// 球形模式下的相机跟随
if (moveTime != -1)
{
transform.DOMove(targetPos + offset, moveTime).onUpdate = () =>
transform.LookAt(targetPos);
}
else
{
transform.position = targetPos + offset;
transform.LookAt(targetPos);
}
}
}
}