Имитация давления в жидкости на основе сетки

30

У меня в системе XNA есть система водоснабжения на основе 2D-сетки, у нас есть метод, использующий клеточные автоматы для симуляции падения и распространения воды.

Пример воды, стекающей по склону:

Физика воды

Каждая плитка может содержать массу от 0 до 255 значений жидкости, хранящихся в байтах. Я не использую floatsстарую систему водоснабжения, которую я использовал, однако это добавило осложнений и привело к снижению производительности.

Каждая плитка для воды обновляется с помощью простого набора правил:

  1. Если на плитке внизу есть место, переместите как можно больше от текущей плитки к нижней (Flow Down)
  2. Если две стороны не одинаковы и не равны нулю, и обе являются проходимыми, мы получаем сумму 3 плиток (левая + текущая + правая) и делим ее на 3, оставляя остальные на средней (текущей) плитке
  3. Если вышеупомянутое правило дало число 2 как сумму, мы должны разделить плитки на две стороны (1, 0, 1)
  4. Если правило 2 дало 1 в качестве суммы, выберите случайную сторону для
  5. Если правило 2 не выполнено, мы должны проверить, является ли одна сторона проходимой, а другая - нет. Если это правда, мы разделяем текущую плитку пополам для 2 плиток

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

Пример того, как это в настоящее время не удается:

Сбой давления

Вода должна течь и выравниваться на каждой стороне U-Bend. Кроме того, я создал методы, чтобы узнать, как далеко находится водоблок и, следовательно, какое давление он испытывает. Теперь мне нужно взять эти цифры и применить их к другим областям, чтобы выровнять давление.

Cyral
источник
Проблема в том, что сложно держать его в клеточном автомате. С тех пор каждый блок должен знать больше, чем просто то, что рядом с ним. Я создал систему, аналогичную той, что вам нужна в 3D. Это довольно сложная система, но я думаю, что это было бы более выполнимо в 2D.
MichaelHouse
@ Byte56 Ну, нам не нужно, чтобы это были клеточные автоматы, если мы можем поддерживать их на разумной скорости.
Cyral
3
Я создам полный ответ, если найду время вечером. Однако, проще говоря, я по сути создал поиск пути для воды. Блоки хотят найти где-то с меньшим давлением, чтобы идти. Они находят путь через другую воду в поисках места, где меньше воды, чем у них (включая воздух рядом с водой). Это решает подавляющее большинство случаев использования.
MichaelHouse
Спасибо, это было бы оценено. Я читал некоторые интервью с создателем Dwarf Fortress, и он сделал это, я верю, но я не был уверен, как преодолеть некоторые из проблем, с которыми он столкнулся, поэтому я никогда не пытался.
Cyral
1
Обратите внимание, что, как только вы добавите давление воздуха, два примера воздушных карманов могут быть полностью действительными (закрытые камеры давления). Я предполагаю, что вы используете не 255 байтов , а значения 0-255; в любом случае, вы, вероятно, не захотите использовать весь диапазон таким образом. Я бы, вероятно, ограничил бы это, хмм, 0-15 для «1 атмосферы» давления (нет такого понятия, как «отрицательное» давление, верно?), Позволяя более высоким давлениям, которых у вас сейчас нет. После того, как вы добавите «воздушные» блоки в сим, естественно более высокий «вес» водяных блоков должен заставить его обтекать изгибы.
Заводная муза

Ответы:

6

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

Способ, который я рассмотрел, пытаясь дать каждому поверхностному водному блоку (любому блоку с водой в нем и без водяного блока над ним) начальное значение давления, равное (или функция) его высоты от дна мира. Неявное значение давления для любой непроходимой плитки равно MAX_PRESSURE(скажем, 255), а для плитки под открытым небом - MIN_PRESSURE(0).

Затем давление распространяется вверх / вниз / в сторону от любой плитки с более высоким давлением на плитки с более низким давлением в течение каждого тика, как в клеточном автомате. Я должен был бы получить фактическую симуляцию, чтобы выяснить, что именно нужно уравнять. Давление в блоке должно быть равным его неявному давлению плюс «избыточное» давление примерно равно уравненному (так что вам нужно будет хранить только это избыточное давление, а не неявное давление).

Если поверхностная плитка имеет давление, превышающее его неявное основанное на высоте давление, и если плитка выше имеет свободное пространство для воды, небольшая часть воды перемещается вверх. Вода стекает только в том случае, если у плитки есть место, так как давление ниже, чем ожидалось.

Это примерно имитирует идею о том, что чем глубже «точка» воды, тем большее давление она имеет, хотя значения давления представляют больше высоту, чем фактическое давление (поскольку ожидается, что более высокие плитки будут иметь более высокое «давление»). Это делает давление своего рода подобным hтермину в уравнении (но не совсем):

P' = P + qgh

В результате, если давление воды выше, чем должно быть для ее глубины, оно будет выталкиваться вверх. Это должно означать, что уровни воды в закрытых системах со временем будут выравнивать давление на всех уровнях высоты.

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

Шон Миддледич
источник
20

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

Вот небольшой подарок, который я сделал из механики давления за невидимой стеной (играли на высокой скорости):

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

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

//Data2                          Data
//______________________________  _____________________________________
//|0    |0      |000   |000    |  |0        |0       |000      |000   |
//|Extra|FlowOut|Active|Largest|  |HasSource|IsSource|Direction|Height|
//------------------------------  -------------------------------------
  • Height это количество воды в кубе, подобное вашему давлению, но моя система имеет всего 8 уровней.
  • Directionэто направление потока. При принятии решения о том, куда будет течь вода, вероятнее всего, она продолжит движение в текущем направлении. Это также используется для быстрого обратного отслеживания потока до его исходного куба, когда это необходимо.
  • IsSourceуказывает, является ли этот куб исходным кубом, что означает, что в нем никогда не заканчивается вода. Используется для источника рек, родников и т. Д. Куб слева в GIF выше, например, является исходным кубом.
  • HasSourceуказывает, подключен ли этот куб к исходному кубу. При подключении к источнику кубы будут пытаться использовать источник для получения большего количества воды, прежде чем искать другие «более полные» кубы, не являющиеся источниками.
  • Largestговорит этому кубу, каков самый большой поток между ним и его исходным кубом. Это означает, что если вода течет через узкий зазор, это ограничивает поток в этот куб.
  • Activeэто счетчик. Когда этот куб имеет активный поток, проходящий через него, в него или из него, активный увеличивается. В противном случае активный случайным образом уменьшается. Как только активность достигнет нуля (имеется в виду не активна), количество воды в этом кубе начнет уменьшаться. Этот вид действует как испарение или впитывание в землю. ( Если у вас есть поток, вы должны иметь отлив! )
  • FlowOutуказывает, связан ли этот куб с кубом, который находится на краю мира. Как только путь к краю света проложен, вода стремится выбрать этот путь перед любым другим.
  • Extra это дополнительный бит для будущего использования.

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

Кубы потока вытягивают воду из источника, следуя направлению потока обратно вверх, пока они не достигнут куба источника или куба потока без родителя. Сохранение направления потока в каждом кубе упрощает отслеживание пути к источнику и обход связанного списка.

Псевдокод для алгоритма выглядит следующим образом:

for i = 0 to topOfWorld //from the bottom to the top
   while flowouts[i].hasitems() //while this layer has flow outs
       flowout = removeRandom(flowouts[i]) //select one randomly
       srcpath = getPathToParent(flowout) //get the path to its parent
       //set cubes as active and update their "largest" value
       //also removes flow from the source for this flow cycle
       srcpath.setActiveAndFlux() 

//now we deal with regular flow
for i = 0 to topOfWorld //from the bottom to the top
    while activeflows[i].hasitems() //while this layer has water
        flowcube = removeRandom(activeflows[i]) //select one randomly
        //if the current cube is already full, try to distribute to immediate neighbors
        flowamt = 0
        if flowcube.isfull 
           flowamt = flowcube.settleToSurrounding
        else
           srcpath = getPathToParent(flowcube) //get the path to its parent
           flowamt = srcpath.setActiveAndFlux()
           flowcube.addflow(flowamt)

        //if we didn't end up moving any flow this iteration, reduce the activity
        //if activity is 0 already, use a small random chance of removing flow
        if flowamt == 0
           flowcube.reduceActive()

 refillSourceCubes()

Основные правила расширения потока, где (упорядочено по приоритету):

  1. Если куб ниже имеет меньше воды, стекают
  2. Если в соседнем кубе на том же уровне меньше воды, течь в боковом направлении.
  3. Если у куба выше меньше воды И кубик источника выше, чем у куба выше, поднимитесь вверх.

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

Эта система работает довольно хорошо. Я могу легко заполнить ямы водой, которые переливаются, чтобы продолжаться наружу. Я могу заполнить U-образные туннели, как вы видите в GIF выше. Однако, как я уже сказал, система неполна, и я еще не все решил. Я давно не работал над системой потоков (я решил, что она не нужна для альфы, и отложил ее). Тем не менее, проблемы, с которыми я столкнулся, когда я отложил их, где:

  • Бассейны . При получении большого объёма воды указатели от ребенка к родителю похожи на безумный беспорядок любого случайного куба, выбранного для направления в любом направлении. Как заполнить ванну глупой нитью. Если вы хотите слить ванну, следует ли вам идти по пути глупой струны обратно к ее источнику? Или просто взять то, что ближе? Поэтому в ситуациях, когда кубы находятся в большом пуле, они, скорее всего, должны просто игнорировать свои родительские потоки и извлекать все, что находится над ними. Я придумал некоторый основной рабочий код для этого, но у меня никогда не было элегантного решения, которым я мог бы быть доволен.

  • Несколько родителей . Дочерний поток может легко обслуживаться несколькими родительскими потоками. Но ребенок, имеющий указатель на одного из родителей, не допустит этого. Это можно исправить, используя достаточное количество битов, чтобы учесть бит для каждого возможного родительского направления. И, вероятно, изменение алгоритма для случайного выбора пути в случае нескольких родителей. Но я так и не нашел время, чтобы проверить и выяснить, какие другие проблемы могут это выявить.

MichaelHouse
источник
Благодарность! Очень информативно! Я скоро начну работать над этим и приму это, если все пойдет хорошо.
Кирал
Конечно. Я представляю гибрид вашей системы, и этот будет очень эффективен для 2D-мира. Пингуйте меня в чате (с @ byte56), если хотите обсудить детали.
MichaelHouse
Хорошо, может быть, день или около того, прежде чем я получу шанс попробовать это.
Cyral
3
Вполне понятно. Я, вероятно, потратил месяцы на его разработку (и переработку). Хотя я буду некоторое время рядом :)
MichaelHouse
2

Я согласен с Шоном, но сделал бы это немного по-другому:

Блок генерирует давление, равное его собственному весу (сколько воды в нем) и применяет его к блокам ниже и рядом с ним. Я не вижу причин, по которым его позиция в мире актуальна.

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

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

Лорен Печтель
источник
Если бы вода двигалась вверх, когда давление, подаваемое сверху, было слишком велико, она не переместилась бы в блок более низкого давления. Чтобы давление выше было слишком велико, оно должно быть больше, чем в блоке ниже. Кроме того, давление должно двигаться вверх, а также вниз и влево / вправо.
MichaelHouse
@ Byte56 Ты неправильно понял, что я сказал. Я говорю, что вода поднимается, когда давление в блоке, который вы анализируете, слишком велико для давления, подаваемого сверху, а не то, что давление сверху слишком велико!
Лорен Печтел
Хорошо, позвольте мне перефразировать то, что вы сказали, чтобы я понял: «вода поднимается, когда давление в блоке, который вы анализируете, превышает давление, подаваемое сверху». Это верно?
MichaelHouse
@ Byte56 Да. Давление в блоке должно быть весом воды над ним или приложенным вбок, когда у нас есть твердая поверхность где-то выше. Слишком слабое давление означает, что воды недостаточно, поднимите воду вверх.
Лорен Печтел
Я просто хотел бы добавить, что если вы имеете дело с проточной водой, этого будет недостаточно, и вы также должны учитывать инерцию, иначе вода будет двигаться слишком медленно.
куб
1

Вы можете добавить правило, которое пытается пройти влево или вправо (через стены) с плитками, пока не найдете свободное место, начиная со слоев внизу. Если вы не можете найти, то плитка остается на текущей позиции. Если вы обнаружите, то другие правила гарантируют замену перемещенной плитки (при необходимости).

almanegra
источник
Это также хорошая идея, не уверен, что она будет работать во всех случаях, но я рассмотрю ее.
Cyral
Хорошо! Дайте мне знать, сработало ли это или нет. С
наилучшими
Я буду, просто был немного занят в последнее время.
Cyral
-2

почему вы не можете определить другой тип блока, который действует как неподвижное количество давления? Поэтому, когда вы используете свой способ обычного перемещения водяных блоков и проверки, может ли он двигаться вверх, он не может.

Еще лучше было бы добавить другое определение к тем блокам, которое позволяет пользователю вводить количество давления на блок, увеличивая давление в соответствии с количеством добавляемых к нему водяных блоков.

SD1990
источник
1
«поэтому, когда вы используете свой способ обычного перемещения водяных блоков и проверки, может ли он двигаться вверх, он не может». Да ... Это уже не может. Вот в чем проблема, я не ищу способ сделать так, чтобы он оставался прежним.
Кирал