- 功能需求
如图所示,需要让左侧的UI框调整适当的大小及位置,以包住右侧的三维物体。 - 实现思路
- 获取三维物体的Bounds边界盒顶点集合;
- 顶点集合中各点由世界坐标转换为屏幕坐标;
- 在屏幕坐标中获取最大最小x值和y值即可获得UI框的大小,并调整其位置。
获取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;
}
世界坐标转屏幕坐标
//世界转屏幕坐标
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;
}
}