У меня есть игровой объект, который живет в мировом пространстве в моей сцене. Я хотел бы получить координаты углов ограничивающего прямоугольника для средства визуализации этого игрового объекта в пространстве экрана, потому что у меня есть элементы пользовательского интерфейса, которые нужно расположить вокруг этого поля.
Контекст: я делаю туториал и использую панели, чтобы затемнить все, кроме игрового объекта, который останется незатемненным. Я могу легко сделать это с помощью кнопок, которые уже находятся в пространстве экрана и имеют прямоугольные преобразования на них, но я не могу понять, как это сделать вокруг игрового объекта в мировом пространстве. Мы используем камеру с орфографической проекцией и используем Версия 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;
}
Я думаю, что это неправильно, потому что то, что я делаю с границами рендерера для вычисления половинного размера и центра, не дает мне ограничивающий прямоугольник. Я надеялся, что будет простой встроенный способ сделать это, но пока ничего не смог найти.
Спасибо за помощь!
@Kirlian, в этом случае все, что происходит, это то, что компонент z
будет 0
@derHugo, а разве мы не этого хотим, раз уж мы говорим о пользовательском интерфейсе? И они в любом случае будут 0
, так как halfSize
исходит из метода Vector2, и у него будет собственный z, установленный на 0
.
@Kirlian ну да ... я только что сослался на ваш комментарий something may be happening during the type conversion
-> Нет, на самом деле ничего особенного не происходит во время преобразования типа, за исключением того, что z
будет 0
.. это то, что нужно иметь в виду, а не то, что на самом деле «происходит» ;)
@derHugo о, извини, тогда я неправильно понял
Я нашел ответ (с видео-предложением от @SparrowsNest на форуме Unity)! Вот видео, начинающееся с соответствующей отметки времени: https://thewikihow.com/video_2Tgqr1_ajqE?t=1061
Шаги:
Вот мой код:
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/
Ввод функции: любой отображаемый на экране.
Вывод: границы 2D-прямоугольника, которые можно использовать на холсте.
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 );
}
Должен работать как для GUI Rect(), так и для Canvas RectTransform.
статический Vector3[] screenSpaceCorners;
public static Rect GetBoundsInScreenSpace(Collider collider, Camera camera)
{
// This is the space occupied by the object's visuals
// in WORLD space.
Bounds bigBounds = collider.bounds;
if (screenSpaceCorners == null)
screenSpaceCorners = new Vector3[8];
// For each of the 8 corners of our renderer's world space bounding box,
// convert those corners into screen space.
screenSpaceCorners[0] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x + bigBounds.extents.x,
bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z));
screenSpaceCorners[1] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x + bigBounds.extents.x,
bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z));
screenSpaceCorners[2] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x + bigBounds.extents.x,
bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z));
screenSpaceCorners[3] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x + bigBounds.extents.x,
bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z));
screenSpaceCorners[4] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x - bigBounds.extents.x,
bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z));
screenSpaceCorners[5] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x - bigBounds.extents.x,
bigBounds.center.y + bigBounds.extents.y, bigBounds.center.z - bigBounds.extents.z));
screenSpaceCorners[6] = camera.WorldToScreenPoint(new Vector3(bigBounds.center.x - bigBounds.extents.x,
bigBounds.center.y - bigBounds.extents.y, bigBounds.center.z + bigBounds.extents.z));
screenSpaceCorners[7] = camera.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;
}
}
var rect = Rect.MinMaxRect(min_x, min_y, max_x, max_y);
//=======For on-screen Rect() or texture ==============
// rect = rect.ToGUISpace(); //Changing Y axis from "Screen Space" to "GUI Space",
// rect.position = rect.position - new Vector2(0, rect.size.y);
//=====================================================
//----For Canvas RectTransform------
rect.position = rect.position + (rect.size / 2f);
return rect;
}
/**
Rect rect = SomeRect();
rect = rect.ToGUISpace();
https://hackingwithunity.com/coordinate-system-in-unity/
*/
public static Rect ToGUISpace(this Rect rect)
{
//Changing from "Screen Space" to "GUI Space" coordinate
rect.position = new Vector2(rect.position.x, Screen.height - rect.position.y);
return rect;
}
Вы пробовали использовать Vector2 вместо Vector3? WorldToGUIPoint() возвращает Vector2, поэтому что-то может происходить во время преобразования типа.