2025-03-26 16:27:45 +08:00

467 lines
14 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; // 记录鼠标按下时的位置
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<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;
//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;
}
}
}
/// <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)
{
// 计算旋转后的偏移量
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<OnUpdatePos>(new OnUpdatePos() { pos = gameObject.Position(), rot = gameObject.LocalEulerAngles() });
#endif
}
}