使用渲染器 (world space) 在游戏对象周围绘制边界矩形 (screen space) (Unity)
Draw bounding rectangle (screen space) around a game object with a renderer (world space) (Unity)
我的场景中有一个游戏对象存在于世界 space 中。我想在屏幕 space 中获取此游戏对象的渲染器的边界矩形角的坐标,因为我有 UI 个元素想要围绕此框定位。
上下文:我正在制作一个教程,我正在使用面板使所有内容变暗,但游戏对象除外,该对象将不变暗。我可以使用已经存在于屏幕 space 中并对其进行矩形变换的按钮轻松地做到这一点,但我不知道如何围绕世界 space 中的游戏对象做到这一点。我们正在使用具有正交投影的相机并且正在使用
Unity版本2019.2.17f1.
这是我试过的方法:
public void FocusOnRenderer(Renderer renderer) {
// left, top, right, and bottom are Panels whose pivots are set as follows:
// top: (1, 0)
// right: (0, 0)
// bottom: (0, 1)
// left: (1, 1)
// so when their positions are set to be the corners of the target bounding box, they will fit together nicely.
left.gameObject.SetActive(true);
top.gameObject.SetActive(true);
right.gameObject.SetActive(true);
bottom.gameObject.SetActive(true);
Vector3 center = HandleUtility.WorldToGUIPoint(renderer.bounds.center); // center of bounding box
Vector3 halfSize = HandleUtility.WorldToGUIPoint(renderer.bounds.extents)); // half size of bounding box
Vector3 topRight = center + halfSize;
Vector3 topLeft = center + new Vector3(-halfSize.x, halfSize.y, halfSize.z);
Vector3 bottomRight = center + new Vector3(halfSize.x, -halfSize.y, halfSize.z);
Vector3 bottomLeft = center + new Vector3(-halfSize.x, -halfSize.y, halfSize.z);
left.position = topLeft;
top.position = topRight;
right.position = bottomRight;
bottom.position = bottomLeft;
}
我认为这是错误的,因为我使用渲染器的边界来计算 halfSize 和 center 并没有给我一个边界矩形。我希望有一种简单的内置方法可以做到这一点,但到目前为止我还没有找到任何东西。
感谢您的帮助!
我找到了答案(来自 @SparrowsNest on the Unity Forum)! Here's the video starting at the relevant timestamp: https://youtu.be/2Tgqr1_ajqE?t=1061
的视频建议
步骤:
- 从渲染器的边界获取边界框的角
- 将这些角转换成屏幕 space
- 获取最小和最大 x 和 y 值
- 使用那些最小和最大 x 和 y 值设置我的面板的位置
这是我的代码:
public void FocusOnBounds(Bounds bounds) {
// left, top, right, and bottom are Panels whose pivots are set as follows:
// top: (1, 0)
// right: (0, 0)
// bottom: (0, 1)
// left: (1, 1)
// so when their positions are set to be the corners of the target bounding box, they will fit together nicely.
left.gameObject.SetActive(true);
top.gameObject.SetActive(true);
right.gameObject.SetActive(true);
bottom.gameObject.SetActive(true);
Vector3 c = bounds.center;
Vector3 e = bounds.extents;
Vector3[] worldCorners = new [] {
new Vector3( c.x + e.x, c.y + e.y, c.z + e.z ),
new Vector3( c.x + e.x, c.y + e.y, c.z - e.z ),
new Vector3( c.x + e.x, c.y - e.y, c.z + e.z ),
new Vector3( c.x + e.x, c.y - e.y, c.z - e.z ),
new Vector3( c.x - e.x, c.y + e.y, c.z + e.z ),
new Vector3( c.x - e.x, c.y + e.y, c.z - e.z ),
new Vector3( c.x - e.x, c.y - e.y, c.z + e.z ),
new Vector3( c.x - e.x, c.y - e.y, c.z - e.z ),
};
IEnumerable<Vector3> screenCorners = worldCorners.Select(corner => Camera.main.WorldToScreenPoint(corner));
float maxX = screenCorners.Max(corner => corner.x);
float minX = screenCorners.Min(corner => corner.x);
float maxY = screenCorners.Max(corner => corner.y);
float minY = screenCorners.Min(corner => corner.y);
Vector3 topRight = new Vector3(maxX, maxY, 0);
Vector3 topLeft = new Vector3(minX, maxY, 0);
Vector3 bottomRight = new Vector3(maxX, minY, 0);
Vector3 bottomLeft = new Vector3(minX, minY, 0);
left.position = topLeft;
top.position = topRight;
right.position = bottomRight;
bottom.position = bottomLeft;
}
如果上面的代码不起作用,下面是从
中提取的代码
http://quill18.com/unity_tutorials/unit_selection/
函数输入:任何呈现在屏幕上的可见内容。
输出:可用于 canvas
的二维矩形边界
static Rect RendererBoundsInScreenSpace(Renderer r) {
// This is the space occupied by the object's visuals
// in WORLD space.
Bounds bigBounds = r.bounds;
if(screenSpaceCorners == null)
screenSpaceCorners = new Vector3[8];
Camera theCamera = Camera.main;
// For each of the 8 corners of our renderer's world space bounding box,
// convert those corners into screen space.
screenSpaceCorners[0] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[1] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
screenSpaceCorners[2] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[3] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
screenSpaceCorners[4] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[5] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
screenSpaceCorners[6] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[7] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
// Now find the min/max X & Y of these screen space corners.
float min_x = screenSpaceCorners[0].x;
float min_y = screenSpaceCorners[0].y;
float max_x = screenSpaceCorners[0].x;
float max_y = screenSpaceCorners[0].y;
for (int i = 1; i < 8; i++) {
if(screenSpaceCorners[i].x < min_x) {
min_x = screenSpaceCorners[i].x;
}
if(screenSpaceCorners[i].y < min_y) {
min_y = screenSpaceCorners[i].y;
}
if(screenSpaceCorners[i].x > max_x) {
max_x = screenSpaceCorners[i].x;
}
if(screenSpaceCorners[i].y > max_y) {
max_y = screenSpaceCorners[i].y;
}
}
return Rect.MinMaxRect( min_x, min_y, max_x, max_y );
}
我的场景中有一个游戏对象存在于世界 space 中。我想在屏幕 space 中获取此游戏对象的渲染器的边界矩形角的坐标,因为我有 UI 个元素想要围绕此框定位。
上下文:我正在制作一个教程,我正在使用面板使所有内容变暗,但游戏对象除外,该对象将不变暗。我可以使用已经存在于屏幕 space 中并对其进行矩形变换的按钮轻松地做到这一点,但我不知道如何围绕世界 space 中的游戏对象做到这一点。我们正在使用具有正交投影的相机并且正在使用 Unity版本2019.2.17f1.
这是我试过的方法:
public void FocusOnRenderer(Renderer renderer) {
// left, top, right, and bottom are Panels whose pivots are set as follows:
// top: (1, 0)
// right: (0, 0)
// bottom: (0, 1)
// left: (1, 1)
// so when their positions are set to be the corners of the target bounding box, they will fit together nicely.
left.gameObject.SetActive(true);
top.gameObject.SetActive(true);
right.gameObject.SetActive(true);
bottom.gameObject.SetActive(true);
Vector3 center = HandleUtility.WorldToGUIPoint(renderer.bounds.center); // center of bounding box
Vector3 halfSize = HandleUtility.WorldToGUIPoint(renderer.bounds.extents)); // half size of bounding box
Vector3 topRight = center + halfSize;
Vector3 topLeft = center + new Vector3(-halfSize.x, halfSize.y, halfSize.z);
Vector3 bottomRight = center + new Vector3(halfSize.x, -halfSize.y, halfSize.z);
Vector3 bottomLeft = center + new Vector3(-halfSize.x, -halfSize.y, halfSize.z);
left.position = topLeft;
top.position = topRight;
right.position = bottomRight;
bottom.position = bottomLeft;
}
我认为这是错误的,因为我使用渲染器的边界来计算 halfSize 和 center 并没有给我一个边界矩形。我希望有一种简单的内置方法可以做到这一点,但到目前为止我还没有找到任何东西。
感谢您的帮助!
我找到了答案(来自 @SparrowsNest on the Unity Forum)! Here's the video starting at the relevant timestamp: https://youtu.be/2Tgqr1_ajqE?t=1061
的视频建议步骤:
- 从渲染器的边界获取边界框的角
- 将这些角转换成屏幕 space
- 获取最小和最大 x 和 y 值
- 使用那些最小和最大 x 和 y 值设置我的面板的位置
这是我的代码:
public void FocusOnBounds(Bounds bounds) {
// left, top, right, and bottom are Panels whose pivots are set as follows:
// top: (1, 0)
// right: (0, 0)
// bottom: (0, 1)
// left: (1, 1)
// so when their positions are set to be the corners of the target bounding box, they will fit together nicely.
left.gameObject.SetActive(true);
top.gameObject.SetActive(true);
right.gameObject.SetActive(true);
bottom.gameObject.SetActive(true);
Vector3 c = bounds.center;
Vector3 e = bounds.extents;
Vector3[] worldCorners = new [] {
new Vector3( c.x + e.x, c.y + e.y, c.z + e.z ),
new Vector3( c.x + e.x, c.y + e.y, c.z - e.z ),
new Vector3( c.x + e.x, c.y - e.y, c.z + e.z ),
new Vector3( c.x + e.x, c.y - e.y, c.z - e.z ),
new Vector3( c.x - e.x, c.y + e.y, c.z + e.z ),
new Vector3( c.x - e.x, c.y + e.y, c.z - e.z ),
new Vector3( c.x - e.x, c.y - e.y, c.z + e.z ),
new Vector3( c.x - e.x, c.y - e.y, c.z - e.z ),
};
IEnumerable<Vector3> screenCorners = worldCorners.Select(corner => Camera.main.WorldToScreenPoint(corner));
float maxX = screenCorners.Max(corner => corner.x);
float minX = screenCorners.Min(corner => corner.x);
float maxY = screenCorners.Max(corner => corner.y);
float minY = screenCorners.Min(corner => corner.y);
Vector3 topRight = new Vector3(maxX, maxY, 0);
Vector3 topLeft = new Vector3(minX, maxY, 0);
Vector3 bottomRight = new Vector3(maxX, minY, 0);
Vector3 bottomLeft = new Vector3(minX, minY, 0);
left.position = topLeft;
top.position = topRight;
right.position = bottomRight;
bottom.position = bottomLeft;
}
如果上面的代码不起作用,下面是从
中提取的代码http://quill18.com/unity_tutorials/unit_selection/
函数输入:任何呈现在屏幕上的可见内容。
输出:可用于 canvas
的二维矩形边界static Rect RendererBoundsInScreenSpace(Renderer r) {
// This is the space occupied by the object's visuals
// in WORLD space.
Bounds bigBounds = r.bounds;
if(screenSpaceCorners == null)
screenSpaceCorners = new Vector3[8];
Camera theCamera = Camera.main;
// For each of the 8 corners of our renderer's world space bounding box,
// convert those corners into screen space.
screenSpaceCorners[0] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[1] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
screenSpaceCorners[2] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[3] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x + bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
screenSpaceCorners[4] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[5] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
screenSpaceCorners[6] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z ) );
screenSpaceCorners[7] = theCamera.WorldToScreenPoint( new Vector3( bigBounds.center.x - bigBounds.extents.x, bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z ) );
// Now find the min/max X & Y of these screen space corners.
float min_x = screenSpaceCorners[0].x;
float min_y = screenSpaceCorners[0].y;
float max_x = screenSpaceCorners[0].x;
float max_y = screenSpaceCorners[0].y;
for (int i = 1; i < 8; i++) {
if(screenSpaceCorners[i].x < min_x) {
min_x = screenSpaceCorners[i].x;
}
if(screenSpaceCorners[i].y < min_y) {
min_y = screenSpaceCorners[i].y;
}
if(screenSpaceCorners[i].x > max_x) {
max_x = screenSpaceCorners[i].x;
}
if(screenSpaceCorners[i].y > max_y) {
max_y = screenSpaceCorners[i].y;
}
}
return Rect.MinMaxRect( min_x, min_y, max_x, max_y );
}