Мы с другом надеемся создать игру в Unity, в которой вы летите через бесконечную 3D-пещеру, которая может крутиться и крутиться в любом направлении (хотя, очевидно, не до такой степени, что развороты сделать невозможно). Мы думали о создании ряда туннельных «кусочков», каждый из которых искривлял определенное количество, и порождал каждый в конце того, что было раньше.
Но мы понятия не имеем, как сделать так, чтобы горловина одной части туннеля всегда идеально совмещалась (и в положении, и в повороте) с концом предыдущего. Может кто-нибудь предложить какой-либо совет, как этого добиться?
Правильно ли мы идем по этому пути, или есть лучший способ процедурно создать пещеру? Бонусные очки: Было бы замечательно, если бы пещера могла измениться в диаметре и / или форме, хотя это было бы просто соусом.
Когда дело доходит до игрового дизайна, редко бывает «правильный путь» или «неправильный путь». Есть много способов решить эту проблему, но вот несколько возможных подходов для исследования:
Заставьте части туннеля начинаться и заканчиваться только в определенных направлениях; например только по осям. Тогда вам просто нужно отслеживать смещение от начала до конца сегмента вместе с перечислениями, описывающими направление движения в начале и конце сегмента. Таким образом, вам не нужно беспокоиться о вращении ваших туннельных сеток, если вы всегда выбираете следующую так, чтобы она начиналась в том же направлении, в котором заканчивалась последняя.
Сделайте так, чтобы каждый сегмент начинался с начала локального пространства модели с туннелем, проходящим вдоль определенной оси (+ X, + Z или -Z было бы наиболее логичным выбором, но все модели должны использовать один и тот же), затем Сохраните положение конца туннеля и конечное направление движения таким образом, чтобы следующая сетка могла быть правильно преобразована. (Матрица преобразования, вероятно, является самым простым способом хранения этой информации, но вы также можете использовать вектор смещения + кватернион, двойной кватернион, смещение + новые базисные векторы, смещение + повороты Эйлера и т. д.)
Процедурно сгенерируйте свою пещеру, передавая новые данные вершин в несколько ячеек. Вы можете сделать это с помощью Meshкласса . При создании новых данных вершин, возможно, самый простой способ - выбрать точку где-то примерно в том же направлении, что и предыдущий сегмент пещеры, а затем позволить центру пещеры двигаться к этой точке. Затем вы можете использовать цилиндрические координаты, чтобы процедурно создать детали на стенах пещеры. Думайте об этом как о выдавливании конца цилиндра, а затем индивидуальном перемещении каждой вершины ближе или дальше от центра этого цилиндра.
Любое решение, использующее готовые сегменты, потребует от вас убедиться, что все сетки имеют одинаковую форму и диаметр вокруг центра туннеля, но вы можете обойти это несколько, если сегменты перекрываются в некоторой степени, и каждый сегмент расширяется на концах. Если все сделано правильно, для игрока не должно быть слишком очевидно, что есть шов.
С другой стороны, если вы будете использовать полностью процедурно-сгенерированную геометрию, у вас будет больше работы, чтобы гарантировать, что вы не создадите участки, которые невозможно пройти, и у вас могут возникнуть проблемы с обнаружением столкновений.
Имейте в виду, что в любой «бесконечной» игре вы должны знать об ограничениях представлений с плавающей запятой. Если игрок слишком далеко отходит от мирового происхождения, становится легко потерять точность в вычислениях с плавающей запятой (например, когда два больших значения вычитаются друг из друга). Чтобы избежать этого, вы можете сделать так, чтобы мир двигался вокруг игрока, а не игрок двигался по миру, но обычно легче просто проверять положение игрока время от времени, и, если они находятся слишком далеко от начала координат, восстанавливать мир с игроком в или около начала.
+1 специально для комментария «нет правильного пути» (хотя мне придется немного не согласиться: есть много, много неправильных путей ...)
Стивен Стадницки
Спасибо! Мы закончили тем, что использовали несколько различных туннельных фрагментов с заранее известными конечными местоположениями и направлениями, установили маркеры в этих местах / углах и поместили каждый новый кусок относительно маркера предыдущего фрагмента. Было бы круто сделать что-то более сложное, но на данный момент более законное процедурное поколение выходило за пределы нашего диапазона навыков и временных ограничений. Еще раз спасибо!
Ричардмхерндон
3
Вот техника, с которой я экспериментировал недавно. Мой прототип RenderMonkey показывает часть каньона в стиле бесплодных земель, но тот же принцип должен работать в пещерах.
Идея состоит в том, чтобы начать с плиток, которые являются общими, совершенно скучными, с простыми предсказуемыми краями, чтобы их было легко выстраивать без швов или разрывов:
Эти начальные плитки могут быть формами, которые вы смоделировали, или процедурно сгенерированными макаронными трубками цилиндрической геометрии (эта форма является вариантом по предложению bcrist и Steven Stadnicki). Использование созданных моделей упрощает обработку произвольной топологии, например путей ветвления, или точек интереса, таких как открытые каверны. Это все еще возможно с чисто процедурной (см. Предложение Gyroninja о методах metaball), но сложно.
Как только плитка помещена в мир, сместите ее вершины, используя шумовые функции, применяемые в мировом пространстве. Это сохраняет связь и бесшовность между плитками (поскольку совпадающие вершины имеют одинаковый входной сигнал в мировом пространстве и получают одинаковый выходной сигнал смещения), но при этом каждая плитка выглядит уникальной и органичной:
Текстура и нормали также применяются в мировом пространстве - здесь используется трипланарное картирование - так, чтобы соседние плитки были полностью бесшовными, без хитрых ограничений развертывания в УФ.
Надежда состоит в том, что подобная техника дает вам простоту планирования и управления дизайном уровня мозаичной карты без видимого повторения или механически выглядящей структуры в воспроизводимом результате.
Вы можете использовать сетку с низким разрешением, используя только компоненты низкочастотного шума, применяемые для создания представления столкновения. Как отмечает bcrist, вам необходимо контролировать максимальную амплитуду шума относительно радиуса и резкости поворотов туннеля, чтобы он никогда не сжимался полностью.
Еще одно замечание: если ваша пещера действительно бесконечна, вам, возможно, придется периодически «пересекать» ее, когда игрок продвигается все дальше и дальше от начала координат. Поскольку числа с плавающей запятой теряют точность при больших величинах, физические объекты и артефакты рендеринга могут заползать на огромные расстояния. Если вы сделаете это, вы захотите, чтобы ваш шум в мировом пространстве был периодическим в больших масштабах, с периодом, точно соответствующим вашему центрирующему смещению, чтобы вы не встретили швы после повторного центрирования.
Вы можете смоделировать свою пещеру как последовательность точек, каждая из которых имеет соответствующий размер, с линиями, соединяющими их. Затем обработайте каждую точку и линию как Metaballs и metacylinders. Это дает вам базовую форму для вашей пещеры, к которой вы можете начать добавлять вариации, например, путем случайного смещения вершин.
Вот еще один подход к процессуальной генерации, который еще не был явно упомянут: скины сплайнов. Вы можете использовать версию Hermite Splines(которые обеспечивают кривую, интерполирующую позиции и касательные) для определения кривых: когда пришло время генерировать новый сегмент, просто выберите позицию (примерно в направлении предыдущего сегмента, как говорит bcrist) и направление (примерно в том же направление - например, в некотором четко определенном конусе предыдущего направления), затем используйте новую позицию + направление и ваше предыдущее положение + направление, чтобы построить новый «позвоночник» для вашей пещеры. Получив эту основную цепь, вы можете придать ей цилиндрическую форму: определите положения и касательные (например) 10 точек вдоль кривой, используйте эти положения / касательные, чтобы найти ортогональную «рамку», а затем используйте эти рамки для строить цилиндрические сегменты. Одна маленькая предосторожность с этим, что пещера не может кривая слишком много, иначе вы можете столкнуться с проблемами самопересечения.
РЕДАКТИРОВАТЬ: Вот грубая разбивка псевдокода алгоритма:
Parameters:
L =(average) segment length,
V = segment length variation,
R = cylinder radius,
T = segment angular variation
S = number of 'rings' per segmentSetup:Choose an initial point P_0 and direction D_0 (for concreteness' sake, these can be
the origin and the X axis).Set P_prev and D_prev to these values.Initialize u_prev to be the Y axis and v_prev to be the Y and Z axes.(Note that (D_prev, u_prev, v_prev) form a mutually-orthogonal 'coordinate frame')Generate a segment (dothisas many times as you want):{Choose a (temporary) direction D within a cone of size T around the previous direction D_prevChoose a segment length L_cur = at random from within the range [L-V, L+V].Set the current terminal point P_cur to P_prev+D*L_cur -thisis the position
we'll interpolate toSet the current terminal direction D_cur to a direction chosen at random from
within a cone of size T around the previous direction.(There are good ways
of doing this-if you look back through gamedev.SE you should find some)'Build' the Hermite spline H that goes from(P_prev, D_prev) to (P_cur, D_cur)Now, skin that spline:for( i =1; i <= S; i++){
find the position P of the hermite spline H at t=i/S
find the direction D of the spline at t (this will be just the derivative)'transport' the orthogonal frame to the new spot:for instance,
v_new = D x u_prev
u_new = v_new x D(note that this keeps u_new, v_new close to their old values, and orthogonal
to each other and to D)Use the previous and current frames and positions to build a cylindrical 'ring':For theta from0 to 2pi{
find the points (P+(u_new, v_new, D)*(cos theta, sin theta,0))
and connect them to their counterparts from the previous ring(note that that multiplication is a matrix-vector multiply)}
update u_prev and v_prev to u_new and v_new}
update the other prev variables to their 'new' values}
Это явно очень грубый псевдокод; если есть какая-то неясная часть, просто дайте мне знать, и я постараюсь объяснить, но будет сложно охватить все детали без огромного дампа кода ...
(Между прочим, если вы хотите использовать псевдокод для этого подхода, дайте мне знать; мне нужно было сделать что-то похожее на предыдущей работе, поэтому я решил проработать все мелкие детали.)
Стивен Стадницки
Мне было бы любопытно увидеть вашу реализацию; Я тоже когда-то делал нечто похожее, но вместо этого использовал трехмерные кубические кривые Безье.
Вот техника, с которой я экспериментировал недавно. Мой прототип RenderMonkey показывает часть каньона в стиле бесплодных земель, но тот же принцип должен работать в пещерах.
Идея состоит в том, чтобы начать с плиток, которые являются общими, совершенно скучными, с простыми предсказуемыми краями, чтобы их было легко выстраивать без швов или разрывов:
Эти начальные плитки могут быть формами, которые вы смоделировали, или процедурно сгенерированными макаронными трубками цилиндрической геометрии (эта форма является вариантом по предложению bcrist и Steven Stadnicki). Использование созданных моделей упрощает обработку произвольной топологии, например путей ветвления, или точек интереса, таких как открытые каверны. Это все еще возможно с чисто процедурной (см. Предложение Gyroninja о методах metaball), но сложно.
Как только плитка помещена в мир, сместите ее вершины, используя шумовые функции, применяемые в мировом пространстве. Это сохраняет связь и бесшовность между плитками (поскольку совпадающие вершины имеют одинаковый входной сигнал в мировом пространстве и получают одинаковый выходной сигнал смещения), но при этом каждая плитка выглядит уникальной и органичной:
Текстура и нормали также применяются в мировом пространстве - здесь используется трипланарное картирование - так, чтобы соседние плитки были полностью бесшовными, без хитрых ограничений развертывания в УФ.
Надежда состоит в том, что подобная техника дает вам простоту планирования и управления дизайном уровня мозаичной карты без видимого повторения или механически выглядящей структуры в воспроизводимом результате.
Вы можете использовать сетку с низким разрешением, используя только компоненты низкочастотного шума, применяемые для создания представления столкновения. Как отмечает bcrist, вам необходимо контролировать максимальную амплитуду шума относительно радиуса и резкости поворотов туннеля, чтобы он никогда не сжимался полностью.
Еще одно замечание: если ваша пещера действительно бесконечна, вам, возможно, придется периодически «пересекать» ее, когда игрок продвигается все дальше и дальше от начала координат. Поскольку числа с плавающей запятой теряют точность при больших величинах, физические объекты и артефакты рендеринга могут заползать на огромные расстояния. Если вы сделаете это, вы захотите, чтобы ваш шум в мировом пространстве был периодическим в больших масштабах, с периодом, точно соответствующим вашему центрирующему смещению, чтобы вы не встретили швы после повторного центрирования.
источник
Вы можете смоделировать свою пещеру как последовательность точек, каждая из которых имеет соответствующий размер, с линиями, соединяющими их. Затем обработайте каждую точку и линию как Metaballs и metacylinders. Это дает вам базовую форму для вашей пещеры, к которой вы можете начать добавлять вариации, например, путем случайного смещения вершин.
источник
Вот еще один подход к процессуальной генерации, который еще не был явно упомянут: скины сплайнов. Вы можете использовать версию Hermite Splines(которые обеспечивают кривую, интерполирующую позиции и касательные) для определения кривых: когда пришло время генерировать новый сегмент, просто выберите позицию (примерно в направлении предыдущего сегмента, как говорит bcrist) и направление (примерно в том же направление - например, в некотором четко определенном конусе предыдущего направления), затем используйте новую позицию + направление и ваше предыдущее положение + направление, чтобы построить новый «позвоночник» для вашей пещеры. Получив эту основную цепь, вы можете придать ей цилиндрическую форму: определите положения и касательные (например) 10 точек вдоль кривой, используйте эти положения / касательные, чтобы найти ортогональную «рамку», а затем используйте эти рамки для строить цилиндрические сегменты. Одна маленькая предосторожность с этим, что пещера не может кривая слишком много, иначе вы можете столкнуться с проблемами самопересечения.
РЕДАКТИРОВАТЬ: Вот грубая разбивка псевдокода алгоритма:
Это явно очень грубый псевдокод; если есть какая-то неясная часть, просто дайте мне знать, и я постараюсь объяснить, но будет сложно охватить все детали без огромного дампа кода ...
источник