У меня в системе XNA есть система водоснабжения на основе 2D-сетки, у нас есть метод, использующий клеточные автоматы для симуляции падения и распространения воды.
Пример воды, стекающей по склону:
Каждая плитка может содержать массу от 0 до 255 значений жидкости, хранящихся в байтах. Я не использую floats
старую систему водоснабжения, которую я использовал, однако это добавило осложнений и привело к снижению производительности.
Каждая плитка для воды обновляется с помощью простого набора правил:
- Если на плитке внизу есть место, переместите как можно больше от текущей плитки к нижней (Flow Down)
- Если две стороны не одинаковы и не равны нулю, и обе являются проходимыми, мы получаем сумму 3 плиток (левая + текущая + правая) и делим ее на 3, оставляя остальные на средней (текущей) плитке
- Если вышеупомянутое правило дало число 2 как сумму, мы должны разделить плитки на две стороны (1, 0, 1)
- Если правило 2 дало 1 в качестве суммы, выберите случайную сторону для
- Если правило 2 не выполнено, мы должны проверить, является ли одна сторона проходимой, а другая - нет. Если это правда, мы разделяем текущую плитку пополам для 2 плиток
Как я могу расширить эту логику, чтобы включить давление? Давление заставит жидкости подниматься над U-образными изгибами и заполнять воздушные карманы.
Пример того, как это в настоящее время не удается:
Вода должна течь и выравниваться на каждой стороне U-Bend. Кроме того, я создал методы, чтобы узнать, как далеко находится водоблок и, следовательно, какое давление он испытывает. Теперь мне нужно взять эти цифры и применить их к другим областям, чтобы выровнять давление.
Ответы:
Обратите внимание, что я никогда не делал этого; это только идеи, которые могут помочь. Или может быть полностью поддельным. Я давно хотел заняться этой проблемой со времен Terraria, но в настоящее время я не работаю над такой игрой.
Способ, который я рассмотрел, пытаясь дать каждому поверхностному водному блоку (любому блоку с водой в нем и без водяного блока над ним) начальное значение давления, равное (или функция) его высоты от дна мира. Неявное значение давления для любой непроходимой плитки равно
MAX_PRESSURE
(скажем, 255), а для плитки под открытым небом -MIN_PRESSURE
(0).Затем давление распространяется вверх / вниз / в сторону от любой плитки с более высоким давлением на плитки с более низким давлением в течение каждого тика, как в клеточном автомате. Я должен был бы получить фактическую симуляцию, чтобы выяснить, что именно нужно уравнять. Давление в блоке должно быть равным его неявному давлению плюс «избыточное» давление примерно равно уравненному (так что вам нужно будет хранить только это избыточное давление, а не неявное давление).
Если поверхностная плитка имеет давление, превышающее его неявное основанное на высоте давление, и если плитка выше имеет свободное пространство для воды, небольшая часть воды перемещается вверх. Вода стекает только в том случае, если у плитки есть место, так как давление ниже, чем ожидалось.
Это примерно имитирует идею о том, что чем глубже «точка» воды, тем большее давление она имеет, хотя значения давления представляют больше высоту, чем фактическое давление (поскольку ожидается, что более высокие плитки будут иметь более высокое «давление»). Это делает давление своего рода подобным
h
термину в уравнении (но не совсем):В результате, если давление воды выше, чем должно быть для ее глубины, оно будет выталкиваться вверх. Это должно означать, что уровни воды в закрытых системах со временем будут выравнивать давление на всех уровнях высоты.
Я не уверен, как бороться или нужно ли вообще иметь дело с «воздушными пузырями», которые могут быть созданы (где неповерхностная плитка будет иметь неполное количество воды, когда вода выталкивается вверх). Я также все еще не уверен, как бы вы избежали неравномерного давления воды с одной стороны, а затем после неравномерного тиканья с другой стороны, назад и вперед.
источник
Я создал систему, похожую на ту, что вам нужна в 3D. У меня есть короткое видео, демонстрирующее простую механику этого здесь и сообщение в блоге здесь .
Вот небольшой подарок, который я сделал из механики давления за невидимой стеной (играли на высокой скорости):
Позвольте мне объяснить соответствующие данные, чтобы дать представление о некоторых особенностях системы. В существующей системе каждый блок воды содержит следующие данные в 2 байта:
Height
это количество воды в кубе, подобное вашему давлению, но моя система имеет всего 8 уровней.Direction
это направление потока. При принятии решения о том, куда будет течь вода, вероятнее всего, она продолжит движение в текущем направлении. Это также используется для быстрого обратного отслеживания потока до его исходного куба, когда это необходимо.IsSource
указывает, является ли этот куб исходным кубом, что означает, что в нем никогда не заканчивается вода. Используется для источника рек, родников и т. Д. Куб слева в GIF выше, например, является исходным кубом.HasSource
указывает, подключен ли этот куб к исходному кубу. При подключении к источнику кубы будут пытаться использовать источник для получения большего количества воды, прежде чем искать другие «более полные» кубы, не являющиеся источниками.Largest
говорит этому кубу, каков самый большой поток между ним и его исходным кубом. Это означает, что если вода течет через узкий зазор, это ограничивает поток в этот куб.Active
это счетчик. Когда этот куб имеет активный поток, проходящий через него, в него или из него, активный увеличивается. В противном случае активный случайным образом уменьшается. Как только активность достигнет нуля (имеется в виду не активна), количество воды в этом кубе начнет уменьшаться. Этот вид действует как испарение или впитывание в землю. ( Если у вас есть поток, вы должны иметь отлив! )FlowOut
указывает, связан ли этот куб с кубом, который находится на краю мира. Как только путь к краю света проложен, вода стремится выбрать этот путь перед любым другим.Extra
это дополнительный бит для будущего использования.Теперь, когда мы знаем данные, давайте посмотрим на общий обзор алгоритма. Основная идея системы состоит в том, чтобы расставить приоритеты в направлении потока вниз и наружу. Как я объясняю в видео, я работаю снизу вверх. Каждый слой воды обрабатывается по одному уровню по оси Y. Кубы для каждого уровня обрабатываются случайным образом, каждый куб будет пытаться вытягивать воду из своего источника на каждой итерации.
Кубы потока вытягивают воду из источника, следуя направлению потока обратно вверх, пока они не достигнут куба источника или куба потока без родителя. Сохранение направления потока в каждом кубе упрощает отслеживание пути к источнику и обход связанного списка.
Псевдокод для алгоритма выглядит следующим образом:
Основные правила расширения потока, где (упорядочено по приоритету):
Я знаю, это довольно высокий уровень. Но это трудно , чтобы получить более подробно , не получая путь в детали.
Эта система работает довольно хорошо. Я могу легко заполнить ямы водой, которые переливаются, чтобы продолжаться наружу. Я могу заполнить U-образные туннели, как вы видите в GIF выше. Однако, как я уже сказал, система неполна, и я еще не все решил. Я давно не работал над системой потоков (я решил, что она не нужна для альфы, и отложил ее). Тем не менее, проблемы, с которыми я столкнулся, когда я отложил их, где:
Бассейны . При получении большого объёма воды указатели от ребенка к родителю похожи на безумный беспорядок любого случайного куба, выбранного для направления в любом направлении. Как заполнить ванну глупой нитью. Если вы хотите слить ванну, следует ли вам идти по пути глупой струны обратно к ее источнику? Или просто взять то, что ближе? Поэтому в ситуациях, когда кубы находятся в большом пуле, они, скорее всего, должны просто игнорировать свои родительские потоки и извлекать все, что находится над ними. Я придумал некоторый основной рабочий код для этого, но у меня никогда не было элегантного решения, которым я мог бы быть доволен.
Несколько родителей . Дочерний поток может легко обслуживаться несколькими родительскими потоками. Но ребенок, имеющий указатель на одного из родителей, не допустит этого. Это можно исправить, используя достаточное количество битов, чтобы учесть бит для каждого возможного родительского направления. И, вероятно, изменение алгоритма для случайного выбора пути в случае нескольких родителей. Но я так и не нашел время, чтобы проверить и выяснить, какие другие проблемы могут это выявить.
источник
Я согласен с Шоном, но сделал бы это немного по-другому:
Блок генерирует давление, равное его собственному весу (сколько воды в нем) и применяет его к блокам ниже и рядом с ним. Я не вижу причин, по которым его позиция в мире актуальна.
На каждом тике перемещайте воду из высокого давления в низкое давление, но перемещайте только часть воды, необходимой для выравнивания. Вода также может быть поднята вверх, если давление в блоке слишком велико для давления, подаваемого на квадрат.
Вы получите петли, в которых давление воды течет слишком далеко в одном направлении, а затем должно быть исправлено, но, поскольку вы не перемещаете все количество воды за тик, они будут демпфированы. Я думаю, что на самом деле это хорошо, так как вы получите эффект от перенапряжения, когда вода затопит область, как вы бы это сделали на самом деле.
источник
Вы можете добавить правило, которое пытается пройти влево или вправо (через стены) с плитками, пока не найдете свободное место, начиная со слоев внизу. Если вы не можете найти, то плитка остается на текущей позиции. Если вы обнаружите, то другие правила гарантируют замену перемещенной плитки (при необходимости).
источник
почему вы не можете определить другой тип блока, который действует как неподвижное количество давления? Поэтому, когда вы используете свой способ обычного перемещения водяных блоков и проверки, может ли он двигаться вверх, он не может.
Еще лучше было бы добавить другое определение к тем блокам, которое позволяет пользователю вводить количество давления на блок, увеличивая давление в соответствии с количеством добавляемых к нему водяных блоков.
источник