Как нарисовать контур в Unity3d?

13

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

Я попытался с помощью MeshRenderer + Transparent Texture и LineRenderer обвести четыре точки прямоугольника. Ни один из них не очень удовлетворителен.

введите описание изображения здесь

(Линия рендерера включена в середине, масштабированный куб справа)

Как правильно это сделать? Я пытаюсь получить что-то вроде левого прямоугольника - простой периметр фиксированной ширины через четыре точки по моему выбору.

Ворон Мечтатель
источник

Ответы:

8

Использование GUI.Box().

Если вам нужен только двухмерный прямоугольник, лучше всего использовать GUI. Создайте новый GUIStyle, используя простой прямоугольник в качестве текстуры (конечно, внутренняя часть прямоугольника должна быть прозрачной), установите его Borderзначение так, чтобы оно не растягивалось, и вызовите GUI.Box(new Rect(...),"",myGuiStyle);.

Вы можете использовать Camera.WorldToScreenPointметод, если хотите нарисовать что-то в мировых координатах (например, в 3D), просто помните, что в мировых координатах Unity y движется снизу вверх, а в GUI y движется сверху вниз.

Пример кода:

void OnGUI()
{
    //top left point of rectangle
    Vector3 boxPosHiLeftWorld = new Vector3(0.5f, 12, 0);
    //bottom right point of rectangle
    Vector3 boxPosLowRightWorld = new Vector3(1.5f, 0, 0);

    Vector3 boxPosHiLeftCamera = Camera.main.WorldToScreenPoint(boxPosHiLeftWorld);
    Vector3 boxPosLowRightCamera = Camera.main.WorldToScreenPoint(boxPosLowRightWorld);

    float width = boxPosHiLeftCamera.x - boxPosLowRightCamera.x;
    float height = boxPosHiLeftCamera.y - boxPosLowRightCamera.y;


     GUI.Box(new Rect(boxPosHiLeftCamera.x, Screen.Height - boxPosHiLeftCamera.y, width, height),"", highlightBox);
}
Ничего
источник
Если я не ошибаюсь, GUI.Box () всегда будет рисоваться поверх любых объектов в сцене? Возможно ли иметь элемент GUI позади или частично позади игрового объекта?
Мечтатель Ворон
Да, GUI всегда рисуется после других объектов. Есть хак, который вы можете использовать, чтобы визуализировать отдельную камеру поверх графического интерфейса, но это довольно громоздко.
Nevermind
Этот ответ дал мне то, что мне было нужно, но я пока держу его открытым в надежде, что в общем случае есть лучший способ сделать это.
Raven Dreamer
Я принял ваш пример добавления кода редактирования и исправил в нем ошибку GUI-y-up-down.
Nevermind
1

Ниже приведен не шейдерный подход.

Представьте, что ваш 2-мерный прямоугольник представляет собой не более чем четыре линии, где каждая линия растянута только в одном измерении (два других измерения являются поперечным сечением края). Это очень похоже на то, как если бы вы собирали коробку в реальной жизни, где вы складываете куски различной длины, которые имеют одинаковый размер поперечного сечения.

Имея это в виду, вы можете разработать, скажем BoxBuilder, Компонент, который при подключении к GameObject создает и управляет четырьмя дочерними объектами GameObject. Каждый дочерний игровой объект является одним из ребер вашего бокса и может быть просто трехмерным кубом, растянутым только в одном измерении. С widthи heightопределенном BoxBuilderуровне, можно рассчитать необходимое позиционирование и неравномерную шкалу четырех детей края. Это будет много pos.x=w/2, pos.y=h/2, ..., scale.x=h,scale.y=w и т.д. вид кода.

Хотя я полагаю, что вы запрашиваете только BoxBuilderдвумерные изображения , обратите внимание, что эту же идею можно применить к трехмерным блокам, если это необходимо, где теперь необходимо создать и управлять 12 дочерними ребрами, но опять же только масштабировать каждое ребро в одном локальном измерении.

DuckMaestro
источник
Работоспособный, но Слишком сложный.
Nevermind
Если бы мне пришлось делать это таким образом, я бы просто использовал 4 линейных рендера ...
Raven Dreamer
Однако это правильно обрабатывает глубину.
DuckMaestro
1

Простой способ - использовать шейдер с двумя проходами: первый проход использует вершинный шейдер, чтобы немного увеличить масштаб объекта, и использует пиксельный шейдер, чтобы закрасить его до сплошного цвета, соответствующего цвету, который должен иметь контур, и затем второй проход делает обычный рендеринг.

UBSophung
источник
Не могли бы вы привести пример кода, чтобы лучше объяснить, что вы имеете в виду?
Raven Dreamer
Это будет работать только для выпуклых объектов (например, прямоугольника), источник которых находится в его геометрическом центре.
rootlocus
1

У меня была такая же проблема при создании контура, за исключением того, что мне нужно было создать «обводку» для 3d-куба, и я нашел новый способ сделать это, чего я не видел нигде в Интернете.

На изображении ниже есть две фигуры с очертаниями. Правый - это куб, построенный с помощью LineRenderer, который строит плоские грани, которые всегда обращены к пользователю. Я нашел этот метод супер глючным, со случайными «штрихами», которые имитируют треугольники, составляющие лицо.

Слева - мое «новшество» с 12-ю отдельными тощими кубиками, создающими то, что выглядит как контур. Чтобы изменить размер «обводки» в контуре, мне нужно увеличить / уменьшить размеры двух сторон каждого из 12 тощих кубов. Это также будет работать для двухмерных контуров. Просто примените материал, чтобы изменить цвет и вуаля!

два разных контура

На этом изображении вы можете увидеть детали структуры этого куба. Все это может быть создано во время выполнения, но я сделал это вручную и использовал его в качестве сборного.

подробно

ow3n
источник
0

В настоящее время я сталкиваюсь с той же проблемой, и мое решение - именно то, что предложили DuckMaestro и Raven Dreamer: иметь скрипт, создающий 4 дочерних объекта во время выполнения, каждый из которых представляет сторону границы, и прикреплять к ним средства визуализации линий.

В моем случае мне нужно было постоянно изменять размер границы, чтобы она оставалась вокруг моего объекта (текстовая сетка [которая использует средство визуализации сетки] для настраиваемого текстового поля), поэтому при каждом обновлении я делала это:


float width = Mathf.Max(renderer.bounds.size.x + paddingX * 2, minWidth);      
float x = renderer.bounds.center.x - width / 2;
float height = renderer.bounds.size.y + paddingY * 2;
float y = renderer.bounds.center.y - height / 2;

AlterBorder(0, new Vector3(x - thickness / 2, y, 0), new Vector3(x + width + thickness / 2, y, 0)); //Bottom edge going left to right
AlterBorder(1, new Vector3(x + width, y + thickness / 2, 0), new Vector3(x + width, y + height - thickness / 2, 0)); //Right edge going bottom to top
AlterBorder(2, new Vector3(x + width + thickness / 2, y + height, 0), new Vector3(x - thickness / 2, y + height, 0)); //Top edge going right to left
AlterBorder(3, new Vector3(x, y + height - thickness / 2, 0), new Vector3(x, y + thickness / 2, 0)); //Left edge going top to bottom

AlterBorder() просто обращается к соответствующему средству рендеринга строки (указанному первым параметром) и устанавливает его начало и конец для первого и второго вектора соответственно.

Обратите внимание, что я использовал в rendererкачестве ссылки для размера, но, очевидно, вы можете использовать любой прямоугольник, если х, у верхний левый угол.

Из того, что я могу сказать, это работает очень хорошо, отлично смотрится в игре, потому что я могу легко перемещать свой ограниченный объект вокруг по всем 3 осям (даже поворачивать его, и так как линейные рендеры всегда смотрят в камеру, это не выглядит странно), и это не сложно реализовать.

Роман
источник