465 lines
13 KiB
C#
465 lines
13 KiB
C#
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);
|
||
}
|
||
}
|
||
}
|
||
} |