Нарисуйте ограничивающий прямоугольник (экранное пространство) вокруг игрового объекта с помощью средства визуализации (мировое пространство) (Unity)

У меня есть игровой объект, который живет в мировом пространстве в моей сцене. Я хотел бы получить координаты углов ограничивающего прямоугольника для средства визуализации этого игрового объекта в пространстве экрана, потому что у меня есть элементы пользовательского интерфейса, которые нужно расположить вокруг этого поля.

Контекст: я делаю туториал и использую панели, чтобы затемнить все, кроме игрового объекта, который останется незатемненным. Я могу легко сделать это с помощью кнопок, которые уже находятся в пространстве экрана и имеют прямоугольные преобразования на них, но я не могу понять, как это сделать вокруг игрового объекта в мировом пространстве. Мы используем камеру с орфографической проекцией и используем Версия 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;
    }

Я думаю, что это неправильно, потому что то, что я делаю с границами рендерера для вычисления половинного размера и центра, не дает мне ограничивающий прямоугольник. Я надеялся, что будет простой встроенный способ сделать это, но пока ничего не смог найти.

Спасибо за помощь!

Вы пробовали использовать Vector2 вместо Vector3? WorldToGUIPoint() возвращает Vector2, поэтому что-то может происходить во время преобразования типа.

NicknEma 23.12.2020 12:04

@Kirlian, в этом случае все, что происходит, это то, что компонент z будет 0

derHugo 23.12.2020 12:33

@derHugo, а разве мы не этого хотим, раз уж мы говорим о пользовательском интерфейсе? И они в любом случае будут 0, так как halfSize исходит из метода Vector2, и у него будет собственный z, установленный на 0.

NicknEma 23.12.2020 16:56

@Kirlian ну да ... я только что сослался на ваш комментарий something may be happening during the type conversion -> Нет, на самом деле ничего особенного не происходит во время преобразования типа, за исключением того, что z будет 0 .. это то, что нужно иметь в виду, а не то, что на самом деле «происходит» ;)

derHugo 23.12.2020 16:59

@derHugo о, извини, тогда я неправильно понял

NicknEma 23.12.2020 20:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
5 688
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я нашел ответ (с видео-предложением от @SparrowsNest на форуме Unity)! Вот видео, начинающееся с соответствующей отметки времени: https://thewikihow.com/video_2Tgqr1_ajqE?t=1061

Шаги:

  1. получить углы ограничивающей рамки из границ рендерера
  2. преобразовать эти углы в экранное пространство
  3. получить минимальное и максимальное значения x и y
  4. установить позиции моих панелей, используя эти минимальные и максимальные значения 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/

Ввод функции: любой отображаемый на экране.

Вывод: границы 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;
    }

Другие вопросы по теме