Canvas 模式是UI与3D混合模式(Render model=Screen space-Camera)
实现在3D模型标记,旋转跟随是UI不在3D物体下
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ClickHandler : MonoBehaviour
{
public Transform object3D; // 总体模型
public GameObject imgUIPrefab;
public Canvas canvas;
public float fadeSpeed = 2.0f; // 淡入淡出的速度
private bool isRotating = false;
private GameObject clickedObject;
private Vector3 lastMousePosition;
private Vector3 delta;
private Dictionary<GameObject, GameObject> generatedUIs = new Dictionary<GameObject, GameObject>();
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
lastMousePosition = Input.mousePosition;
isRotating = true;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
clickedObject = hit.collider.gameObject;
// 检查是否为小模型
if (clickedObject.CompareTag("Model"))
{
isRotating = false;
if (!HasGeneratedUI(clickedObject.name))
{
// 创建UI
GameObject imgUI = CreateUIForModel(clickedObject);
if (imgUI != null)
{
// 添加到字典中
generatedUIs.Add(imgUI, clickedObject);
}
}
}
}
UpdateImgUIPosition();
}
if (Input.GetMouseButtonUp(0))
{
isRotating = false;
}
if (isRotating)
{
delta = Input.mousePosition - lastMousePosition;
float rotationSpeed = 0.5f;
object3D.Rotate(Vector3.up, delta.x * -rotationSpeed, Space.World);
UpdateImgUIPosition();
}
lastMousePosition = Input.mousePosition;
}
private GameObject CreateUIForModel(GameObject model)
{
// 获取被点击物体的中心点位置
Vector3 modelCenter = model.transform.position;
// 将模型的世界坐标转换为屏幕坐标
Vector3 screenPoint = Camera.main.WorldToScreenPoint(modelCenter);
// 将屏幕坐标转换为Canvas内的局部坐标
RectTransform canvasRect = canvas.GetComponent<RectTransform>();
Vector2 canvasLocalPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, screenPoint, Camera.main, out canvasLocalPoint);
// 设置预制体的位置为Canvas内的局部坐标,将Z轴位置设置为-150
Vector3 prefabPosition = new Vector3(canvasLocalPoint.x, canvasLocalPoint.y,-150f);
// 实例化Img UI预制件,并设置其位置为转换后的局部坐标
GameObject imgUI = Instantiate(imgUIPrefab, prefabPosition, Quaternion.identity);
imgUI.name = model.name; // 生成UI名字为3D模型名字
imgUI.transform.SetParent(canvas.transform, false); // 设置Img UI的父对象为Canvas,确保其显示在屏幕上
return imgUI;
}
private void UpdateImgUIPosition()
{
foreach (var uiEntry in generatedUIs)
{
GameObject ui = uiEntry.Key;
GameObject matchedModel = uiEntry.Value;
bool isOccluded = IsObjectOccluded(matchedModel);
// ui.SetActive(!isOccluded); // 如果模型被遮挡,则隐藏UI;否则显示UI
// 如果模型被遮挡,则UI淡出 否则淡入
Image uiImage = ui.GetComponent<Image>();
Color color = uiImage.color;
float targetAlpha = isOccluded ? 0.0f : 1.0f;
color.a = Mathf.MoveTowards(color.a, targetAlpha, Time.deltaTime * fadeSpeed);
uiImage.color = color;
if (!isOccluded)
{
// 获取匹配模型的中心点位置
Vector3 modelCenter = matchedModel.transform.position;
// 将模型的世界坐标转换为屏幕坐标
Vector3 screenPoint = Camera.main.WorldToScreenPoint(modelCenter);
// 将屏幕坐标转换为Canvas内的局部坐标
RectTransform canvasRect = canvas.GetComponent<RectTransform>();
Vector2 canvasLocalPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRect, screenPoint, Camera.main, out canvasLocalPoint);
Vector3 prefabPosition = new Vector3(canvasLocalPoint.x, canvasLocalPoint.y, -150f);
// 更新UI的位置
ui.GetComponent<RectTransform>().anchoredPosition = prefabPosition;
}
}
}
private bool HasGeneratedUI(string name)
{
foreach (var ui in generatedUIs.Keys)
{
if (ui.name == name)
{
return true;
}
}
return false;
}
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;
}
}