Unity 如何使UI框动态包住一个三维物体

  • 功能需求

    如图所示,需要让左侧的UI框调整适当的大小及位置,以包住右侧的三维物体。
  • 实现思路
    1. 获取三维物体的Bounds边界盒顶点集合;
    2. 顶点集合中各点由世界坐标转换为屏幕坐标;
    3. 在屏幕坐标中获取最大最小x值和y值即可获得UI框的大小,并调整其位置。

获取Bounds顶点集合

Bounds

//获取边界盒的顶点集合
private Vector3[] GetVertexFromBounds(Bounds bounds, Transform tf)
{
	Vector3[] verts = new Vector3[8];
	verts[0] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, -bounds.size.y, -bounds.size.z) * 0.5f);
	verts[1] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, -bounds.size.y, -bounds.size.z) * 0.5f);
	verts[2] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, -bounds.size.y, bounds.size.z) * 0.5f);
	verts[3] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, -bounds.size.y, bounds.size.z) * 0.5f);
	verts[4] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, bounds.size.y, -bounds.size.z) * 0.5f);
	verts[5] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, bounds.size.y, -bounds.size.z) * 0.5f);
	verts[6] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, bounds.size.y, bounds.size.z) * 0.5f);
	verts[7] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, bounds.size.y, bounds.size.z) * 0.5f);
	return verts;
}

世界坐标转屏幕坐标

WorldToScreenPoint

//世界转屏幕坐标
private Vector3[] World2ScreenPoints(Vector3[] points)
{
	//获取主相机
	Camera mainCamera = Camera.main;
	if (mainCamera == null) mainCamera = FindObjectOfType<Camera>();
	for (int i = 0; i < points.Length; i++)
	{
		Vector3 point = points[i];
		points[i] = mainCamera.WorldToScreenPoint(point);
	}
	return points;
}

获取屏幕坐标系中的边界

//获取边界值
private void GetBoundaryPos(Vector3[] points, out float minX, out float maxX, out float minY, out float maxY)
{
	minX = float.MaxValue;
	maxX = float.MinValue;
	minY = float.MaxValue;
	maxY = float.MinValue;
	for (int i = 0; i < points.Length; i++)
	{
		Vector3 point = points[i];
		minX = point.x < minX ? point.x : minX;
		maxX = point.x > maxX ? point.x : maxX;
		minY = point.y < minY ? point.y : minY;
		maxY = point.y > maxY ? point.y : maxY;
	}
}

Example示例:

using UnityEngine;

public class Example : MonoBehaviour
{
	[SerializeField] private RectTransform uiFrameRt; //UI框

    private void Start()
    {
		Execute(uiFrameRt, GetComponent<MeshFilter>().sharedMesh.bounds, transform, 50, 60f);
	}

    /// <summary>
    /// 使UI框包围住3D物体
    /// </summary>
    /// <param name="uiFrameRt">UI框 RectTransform</param>
    /// <param name="bounds">3D物体边界盒</param>
    /// <param name="tf">3D物体Transform</param>
    /// <param name="paddingHorizontal">水平方向上的边距</param>
    /// <param name="paddingVertical">垂直方向上的边距</param>
    public void Execute(RectTransform uiFrameRt, Bounds bounds, Transform tf, float paddingHorizontal, float paddingVertical)
    {
		Vector3[] points = GetVertexFromBounds(bounds, tf);
		points = World2ScreenPoints(points);
		GetBoundaryPos(points, out float minX, out float maxX, out float minY, out float maxY);
		//设置宽度
		uiFrameRt.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, maxX - minX + paddingHorizontal);
		//设置高度
		uiFrameRt.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, maxY - minY + paddingVertical);
		//设置锚点
		uiFrameRt.anchorMin = Vector2.zero;
		uiFrameRt.anchorMax = Vector2.zero;
		//设置位置
		uiFrameRt.anchoredPosition3D = new Vector2((minX + maxX) * .5f, (minY + maxY) * .5f);
	}

	//获取边界盒的顶点数组
	private Vector3[] GetVertexFromBounds(Bounds bounds, Transform tf)
    {
		Vector3[] verts = new Vector3[8];
		verts[0] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, -bounds.size.y, -bounds.size.z) * 0.5f);
		verts[1] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, -bounds.size.y, -bounds.size.z) * 0.5f);
		verts[2] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, -bounds.size.y, bounds.size.z) * 0.5f);
		verts[3] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, -bounds.size.y, bounds.size.z) * 0.5f);
		verts[4] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, bounds.size.y, -bounds.size.z) * 0.5f);
		verts[5] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, bounds.size.y, -bounds.size.z) * 0.5f);
		verts[6] = tf.TransformPoint(bounds.center + new Vector3(bounds.size.x, bounds.size.y, bounds.size.z) * 0.5f);
		verts[7] = tf.TransformPoint(bounds.center + new Vector3(-bounds.size.x, bounds.size.y, bounds.size.z) * 0.5f);
		return verts;
	}

	//获取边界值
	private void GetBoundaryPos(Vector3[] points, out float minX, out float maxX, out float minY, out float maxY)
    {
		minX = float.MaxValue;
		maxX = float.MinValue;
		minY = float.MaxValue;
		maxY = float.MinValue;
		for (int i = 0; i < points.Length; i++)
        {
			Vector3 point = points[i];
			minX = point.x < minX ? point.x : minX;
			maxX = point.x > maxX ? point.x : maxX;
			minY = point.y < minY ? point.y : minY;
			maxY = point.y > maxY ? point.y : maxY;
        }
    }

	//世界转屏幕坐标
	private Vector3[] World2ScreenPoints(Vector3[] points)
    {
		//获取主相机
		Camera mainCamera = Camera.main;
		if (mainCamera == null) mainCamera = FindObjectOfType<Camera>();
		for (int i = 0; i < points.Length; i++)
        {
			Vector3 point = points[i];
			points[i] = mainCamera.WorldToScreenPoint(point);
        }
		return points;
    }
}

Example

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CoderZ1010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值