在UI Canvas的Render Mode模式是Screen Space-Camera模式极其容易3D坐标值转UI坐标出错。
**具体原因是:**Canvas的Rect Transform锁定,其Scale的倍数值不是1,所以导致从3D的坐标数值转换成UI坐标时就会出现问题。
正确做法:
1、 声明变量: 3D物体、 UI的RectTransform、 Canvas的RectTransform、 Canvas所使用的Camera2、将物体的世界坐标转换为Canvas内的局部坐标3、设置UI元素的位置为Canvas内的局部坐标
代码:
public class TitileMove : MonoBehaviour
{// 3D物体public Transform object3D;// UI Image的RectTransformpublic RectTransform uiImageRectTransform;// Canvas的RectTransformpublic RectTransform canvasRectTransform;// Canvas所使用的Camerapublic Camera canvasCamera;public GameObject lastSelectedObject;private bool isDragging = false;private bool isRotating = false;private Vector3 lastMousePosition;private void OnBecameInvisible(){// 当物体不可见时,隐藏UI元素uiImageRectTransform.gameObject.SetActive(false);}void Update(){// 如果鼠标左键按下if (Input.GetMouseButtonDown(0)){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;// 射线检测是否点击到物体if (Physics.Raycast(ray, out hit)){GameObject selectedObject = hit.collider.gameObject;// 判断是否点击到了我们想要拖拽的物体if (selectedObject.transform.name == "3"){uiImageRectTransform.gameObject.SetActive(true);// 开始拖拽// 将物体的世界坐标转换为Canvas内的局部坐标Vector2 localPos;RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, Camera.main.WorldToScreenPoint(selectedObject.transform.position), canvasCamera, out localPos);// 设置UI元素的位置为Canvas内的局部坐标localPos = new Vector2(localPos.x - 249, localPos.y + 70);uiImageRectTransform.localPosition = localPos;lastSelectedObject = selectedObject;Vector3 objectPosition = selectedObject.transform.position;lastMousePosition = Input.mousePosition;}else{// 隐藏UI元素uiImageRectTransform.gameObject.SetActive(false);}}isRotating = true;// 检测物体是否不可见并隐藏UI元素if (lastSelectedObject != null && IsObjectInvisible(lastSelectedObject)){uiImageRectTransform.gameObject.SetActive(false);}}if (isRotating && object3D != null){// 获取当前鼠标位置和上一帧鼠标位置之间的差值Vector3 deltaMouse = Input.mousePosition - lastMousePosition;// 根据差值来计算旋转角度float rotationSpeed = 0.25f; // 调整旋转速度float rotationY = deltaMouse.x * rotationSpeed;// 应用旋转object3D.transform.Rotate(Vector3.up, -rotationY, Space.World);// 更新上一帧鼠标位置lastMousePosition = Input.mousePosition;if (uiImageRectTransform != null){// uiImageRectTransform.GetComponent<RectTransform>().anchoredPosition = new Vector2(uiImageRectTransform.GetComponent<RectTransform>().anchoredPosition.x + deltaMouse.x, uiImageRectTransform.GetComponent<RectTransform>().anchoredPosition.y);// 将物体的世界坐标转换为屏幕坐标Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(null, uiImageRectTransform.position);// 将屏幕坐标转换为Canvas内的局部坐标RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, screenPos, null, out Vector2 localPos);localPos = new Vector2(localPos.x + deltaMouse.x, localPos.y);// 设置物体的局部坐标uiImageRectTransform.anchoredPosition = localPos;}}// 如果鼠标左键释放if (Input.GetMouseButtonUp(0)){isRotating = false;}}// 检测物体是否不可见private bool IsObjectInvisible(GameObject obj){// 获取物体的包围盒Bounds bounds = obj.GetComponent<Renderer>().bounds;// 获取摄像机的视锥体平面Plane[] frustumPlanes = GeometryUtility.CalculateFrustumPlanes(Camera.main);// 检查包围盒是否与视锥体相交return !GeometryUtility.TestPlanesAABB(frustumPlanes, bounds);}// 检测物体是否被遮挡private bool IsObjectOccluded(GameObject obj){// 获取摄像机到物体的方向Vector3 directionToTarget = obj.transform.position - Camera.main.transform.position;// 发射射线Ray ray = new Ray(Camera.main.transform.position, directionToTarget);RaycastHit hit;// 射线检测是否有其他碰撞器位于射线路径上if (Physics.Raycast(ray, out hit, directionToTarget.magnitude)){// 如果射线击中的物体不是目标物体,则表示目标物体被遮挡if (hit.collider.gameObject != obj){return true;}}return false;}
}
重点
// 将物体的世界坐标转换为Canvas内的局部坐标Vector2 localPos;RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTransform, Camera.main.WorldToScreenPoint(selectedObject.transform.position), canvasCamera, out localPos);// 设置UI元素的位置为Canvas内的局部坐标localPos = new Vector2(localPos.x - 249, localPos.y + 70);uiImageRectTransform.localPosition = localPos;