Я створив систему, подібну до тієї, яку ви шукаєте в 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()
Основні правила розширення потоку, де (упорядковано за пріоритетом):
- Якщо в кубіку нижче води менше, стікайте вниз
- Якщо сусідній куб на одному рівні має менше води, стікайте бічно.
- Якщо в кубі вище води менше І вихідний куб вище куба вище, підведіть вгору.
Я знаю, це досить високий рівень. Але це важко , щоб отримати більш докладно , не отримуючи шлях в деталі.
Ця система працює досить добре. Я можу легко заповнити ями води, які переповнюються, щоб продовжуватися назовні. Я можу заповнити U-образні тунелі, як ви бачите в gif-файлі вище. Однак, як я вже сказав, система неповна, і я ще не все розробив. Я довго не працював над потоковою системою (я вирішив, що вона не потрібна альфа, і я перестав її утримувати). Однак проблеми, з якими я мав справу, коли я зупинив її:
Басейни . Коли ви отримуєте великий басейн з водою, вказівники від дитини до батька - це як шалений безлад будь-якого випадкового куба, обраного для течії в будь-якому напрямку. Як би наповнити ванну дурною струною. Коли ви хочете злити ванну, чи слід йти шляхом глупої струни до її джерела? Або просто слід взяти все, що знаходиться найближче? Тож у ситуаціях, коли кубики знаходяться у великому басейні, вони, швидше за все, повинні просто ігнорувати потоки своїх батьків і витягувати з того, що знаходиться над ними. Я придумав базовий робочий код для цього, але ніколи не мав елегантного рішення, яким би міг бути задоволений.
Багато батьків . Дочірній потік легко може живитись більш ніж одним батьківським потоком. Але дитина, яка має вказівник на одного батька, цього не дозволила б. Це можна виправити, використовуючи достатню кількість бітів, щоб забезпечити біт для кожного можливого батьківського напрямку. І, ймовірно, зміна алгоритму для випадкового вибору шляху у випадку кількох батьків. Але я ніколи не обійшовся цим, щоб перевірити і подивитися, які ще проблеми, які можуть виникнути.