
В разработке игр постоянно создаются и применяются пайплайны, алгоритмы и небольшие процессы, которые экономят кучу времени и сил. Часто эти решения — простые, но изящные.
Эта статья как раз про подобное. Автор придумал метод постройки виртуальных городов, который в перспективе хочет превратить в полноценный инструмент левел-дизайнера.
В этом материале я разберу базовую настройку инструмента City Builder в Houdini, который может строить города простыми кликами мышью. Обратите внимание, что он работает только внутри Houdini с использованием Python Viewer States.
Референс
Самым большим вдохновением для меня был Оскар Штольберг. Его работа впечатлила, и заставила задуматься, возможно ли повторить такое в Houdini. Мою попытку также можно считать за доказательство того, что какую-либо идею вполне реально воспроизвести и в другом софте.

Начальный сетап
Первое, что нужно сделать — это сетка или плоскость, на которой будут строиться дома. Теоретически, это может быть что угодно, например, треугольники, шестиугольники и так далее. Самый простой способ, который я нашел — использовать quad mesher.
Начните с circle node и разместите под ним instant mesh node (он из инструментов Labs, поэтому убедитесь, что они у вас установлены).

После этого разместите extrude node — позже он будет использоваться для выдавливания примитивов, выбранных мышью.

Сетап будет свернут в HDA, в нем вы создадите параметр для работы с кодом Python. В данном случае нужно управлять параметром group для вытягивания и заливки примитивов, по которым щелкает мышь.

Python Viewer States
Теперь можно перейти к коду Python. Если вы новичок во viewer states, то я ранее написал базовое интро к нему.
Создайте новый viewer state и начните с пустого шаблона. Это сгенерирует только тот фрагмент кода, который действительно нужен.

Есть несколько вещей, которые нужно добавить. Можно разделить их на 2 части:
- Получение геометрии для просмотра при щелчках мыши.
- Получение примитива при щелчке мышью.
Для начала в часть с init добавьте новую переменную для геометрии под названием self.geometry = None. Затем добавьте функцию OnEnter — этот метод вызывается, когда состояние активируется пользователем, создающим/выбирающим узел и нажимающим Enter во вьюпорте.
Здесь хранится текущая геометрия в самом self.geometry. Делается с помощью node.geometry().

Теперь есть связь с геометрией.
Следующая часть — это получение примитивного числа, основанного на положении мыши при щелчке. Для этого создайте функцию OnMouseEvent — это обработка событий мыши (таких как щелчки или положение мыши).
В начале события мыши создайте переменные для работы. Затем получите пересечение с геометрией с помощью функции GeometryIntersector и используйте self.geometry.
С этого пересечения вы получите нужную информацию, узнайте и напечатайте примитивное число.

Если все сделано правильно, то получится это:

Последняя настройка заключается в том, чтобы использовать примитивное число в параметре group, который вы сделали в начале. Замените строку на новую, вызовите параметр group и установите примитивное число в качестве значения. Обратите внимание, он также преобразуется в строку, потому что group — это параметр string.

Получится это:

Это основная идея, с которой я начал создавать инструмент. С этого момента вы будете хранить информацию, а не переписывать ее. Этот сетап даст базу для старта и дальнейших экспериментов.
Детализация
Как только у вас появятся эти кубики, вы сможете делать с ними что угодно. Если нужен город, то создайте генератор домов — в интернете есть много туториалов на эту тему. А пока — мой пример.
На каждой стороне куба скопирована модульная стена.

Граница, которая скошена и развернута с UV.

Здесь очень важна производительность. Чтобы инструмент работал быстро и при нажатии дом мгновенно появлялся — придется заняться оптимизацией. Для этого вы можете попытаться использовать минимальное количество loop nodes и boolean nodes в сети.
Будущее инструмента
Я хочу пойти дальше и сделать из него инструмент для создания уровней. Идея в том, что вы можете быстро прототипировать готовые уровни с помощью пары кликов. Меня на это вдохновил Hob.

Напоследок небольшое демо того, что у меня получилось в данный момент:
