1、效果展示
2、主要代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LabelUiItem : MonoBehaviour
{
private Vector3 center;
private Image linkLine;
private Image Line;
private Text contentText;
private GameObject contentBG;
private Transform targetTrans;
public struct LabelUiData
{
public float zValue;
public GameObject labelUiItemGame;
public string targetName;
}
private LabelUiData labelUiData;
//初始化
public void Init(Transform targetTrans, Transform canvasTrans)
{
this.targetTrans = targetTrans;
Line = transform.Find("line").GetComponent<Image>();
linkLine = transform.Find("linkLine").GetComponent<Image>();
contentBG = transform.Find("contentBG").gameObject;
contentText = contentBG.transform.Find("content").GetComponent<Text>();
contentText.text = targetTrans.name;
transform.SetParent(canvasTrans, false);
center = GetModelCenterPoint();
gameObject.SetActive(true);
labelUiData = new LabelUiData();
labelUiData.labelUiItemGame = gameObject;
labelUiData.targetName = targetTrans.name;
gameObject.name = targetTrans.name;
}
/// <summary>
/// 判断当前位置是否被遮挡
/// </summary>
private bool IsBaffle()
{
Camera curSceneCamera = Camera.main;
Vector3 directionToTarget = targetTrans.position - curSceneCamera.transform.position;
Ray ray = new Ray(curSceneCamera.transform.position, directionToTarget);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, directionToTarget.magnitude))
{
// 如果射线击中的物体不是目标物体,则表示目标物体被遮挡
if (hit.collider.gameObject.name.Equals(targetTrans.gameObject.name))
{
return true;
}
else
{
return false;
}
}
return true;
}
private Vector3 GetModelCenterPoint()
{
Transform partentTrans = targetTrans.parent;
Renderer[] renderers = partentTrans.GetComponentsInChildren<Renderer>();
Bounds box = new Bounds(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
for (int i = 0; i < renderers.Length; i++)
{
Renderer renderer = renderers[i];
box.Encapsulate(renderer.bounds.min);
box.Encapsulate(renderer.bounds.max);
}
return box.center;
}
public void SetDir(Vector3 lineDir)
{
if (lineDir == Vector3.zero)
lineDir = Vector3.right;
bool isLeft = lineDir.x < 0;
Vector3 xDirection = isLeft ? Vector3.left : Vector3.right;
Vector3 normalDir = lineDir.normalized;
linkLine.transform.localPosition = Vector3.zero;
Quaternion rotation = Quaternion.FromToRotation(Vector3.right, normalDir);
linkLine.transform.localRotation = rotation;
Vector2 pivot = isLeft ? new Vector2(1f, 0.5f) : new Vector2(0f, 0.5f);
Line.rectTransform.pivot = pivot;
contentText.rectTransform.pivot = pivot;
contentBG.GetComponent<RectTransform>().pivot = pivot;
Line.transform.localPosition = normalDir * linkLine.rectTransform.rect.width - xDirection;
Vector3 contentPos = Line.transform.localPosition + new Vector3(0, 10, 0) + xDirection * (Line.rectTransform.rect.width + 5) * 1;
contentBG.transform.localPosition = new Vector3(contentPos.x, Line.transform.localPosition.y, contentPos.z);
}
private bool IsModelVisible()
{
Renderer modelRenderer = targetTrans.GetComponent<Renderer>();
Vector3 screenPos = Camera.main.WorldToScreenPoint(modelRenderer.bounds.center);
return screenPos.z > 0 &&
screenPos.x >= 0 &&
screenPos.x <= Screen.width &&
screenPos.y >= 0 &&
screenPos.y <= Screen.height;
}
public void LabelLateUpdate()
{
// 获取目标物体的世界坐标
Vector3 worldPosition = targetTrans.position;
// 将世界坐标转换为屏幕坐标
Vector3 screenPosition = Camera.main.WorldToScreenPoint(worldPosition);
if (IsBaffle() && IsModelVisible())
{
gameObject.SetActive(true);
Vector3 screenCenter = Camera.main.WorldToScreenPoint(center);
var angleValue = (screenPosition - screenCenter);
Debug.Log($"{targetTrans.name} {angleValue.z}");
labelUiData.zValue = angleValue.z;
SetDir(angleValue);
gameObject.transform.position = screenPosition;
LabelUiManager.Instance.AddOrUpdataLabelUiData(labelUiData);
}
else
{
gameObject.SetActive(false);
}
}
}
需要一个管理 LabelUiManager
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class LabelUiManager : MonoBehaviour
{
public static LabelUiManager Instance;
private Dictionary<string, LabelUiItem.LabelUiData> labelUiDatas;
public GameObject labelUIItem;
public Transform canvasTrans;
public void Awake()
{
Instance = this;
labelUiDatas = new Dictionary<string, LabelUiItem.LabelUiData>();
}
//添加 或者 刷新标注ui的数据
public void AddOrUpdataLabelUiData(LabelUiItem.LabelUiData labelUiData)
{
if (labelUiDatas.ContainsKey(labelUiData.targetName))
{
labelUiDatas[labelUiData.targetName] = labelUiData;
}
else
{
labelUiDatas.Add(labelUiData.targetName, labelUiData);
}
}
//设置刷新标注ui的层级
public void SetLabelUiHierarchy()
{
List<LabelUiItem.LabelUiData> labelUiOrderByDatas = labelUiDatas.Values.OrderBy(n => n.zValue).ToList();
for (int i = 0; i < labelUiOrderByDatas.Count; i++)
{
LabelUiItem.LabelUiData labelUiData = labelUiOrderByDatas[i];
labelUiData.labelUiItemGame.transform.SetAsFirstSibling();
}
}
//创建标注item
public LabelUiItem CreateLabelUiItem(Transform targetTrans)
{
GameObject labelUIItemGame = Instantiate(labelUIItem);
LabelUiItem labelUiItem = labelUIItemGame.GetComponent<LabelUiItem>();
labelUiItem.Init(targetTrans, canvasTrans);
return labelUiItem;
}
// Start is called before the first frame update
private void Start()
{
}
// Update is called once per frame
private void Update()
{
SetLabelUiHierarchy();
}
}
3、项目Demo
链接:https://pan.baidu.com/s/1ri_xKNrvBDI1aW-1xXBG6g?pwd=xm61
提取码:xm61
--来自百度网盘超级会员V7的分享