Mensimulasikan tekanan dalam simulasi cairan berbasis grid

30

Saya memiliki sistem air berbasis jaringan 2D dalam game XNA saya, kami memiliki metode menggunakan automata seluler untuk mensimulasikan jatuh dan penyebaran air.

Contoh air yang mengalir menuruni lereng:

Fisika Air

Setiap ubin dapat berisi massa 0 hingga 255 nilai cairan, disimpan dalam byte. Saya tidak menggunakan floats, sistem air lama yang saya lakukan, tetapi menambah komplikasi dan memiliki kinerja yang baik.

Setiap ubin air memperbarui dirinya dengan seperangkat aturan sederhana:

  1. Jika ubin di bawah ini memiliki ruang di dalamnya, pindahkan sebanyak mungkin dari ubin saat ini ke ubin bawah (Aliran Turun)
  2. Jika 2 sisi tidak sama dan tidak nol dan keduanya bisa dilewati, kita mendapatkan jumlah dari 3 ubin (kiri + sekarang + kanan) dan membaginya dengan 3 meninggalkan sisanya di ubin tengah (saat ini)
  3. Jika aturan di atas memberikan angka 2 sebagai jumlah, kita harus membagi ubin menjadi dua sisi (1, 0, 1)
  4. Jika aturan 2 memberi 1 sebagai jumlah, pilih sisi acak untuk mengalir
  5. Jika aturan 2 gagal, kita harus memeriksa apakah satu sisi bisa dilewati dan yang lainnya tidak. Jika itu benar, kami membagi ubin saat ini menjadi dua untuk 2 ubin

Bagaimana saya bisa memperluas logika ini untuk memasukkan tekanan? Tekanan akan membuat cairan naik di atas "U-Bends" dan mengisi kantong udara.

Contoh tentang bagaimana ini gagal saat ini:

Tekanan Gagal

Air harus mengalir dan menyamakan di setiap sisi U-Bend. Selain itu, saya telah menciptakan metode untuk mengetahui seberapa jauh blok air itu, dan oleh karena itu seberapa banyak tekanan yang dialami. Sekarang saya harus bisa mengambil angka-angka ini dan menerapkannya ke area lain untuk menyamakan tekanan.

Cyral
sumber
Masalahnya adalah sulit untuk menyimpannya sebagai automata seluler. Karena sekarang setiap blok perlu tahu lebih dari sekadar apa yang ada di sebelahnya. Saya membuat sistem yang mirip dengan yang Anda inginkan dalam 3D. Ini sistem yang cukup kompleks, tapi saya pikir ini akan lebih bisa dilakukan dalam 2D.
MichaelHouse
@ Byte56 Yah kita tidak perlu itu untuk automata seluler, selama kita bisa tetap berjalan pada kecepatan yang masuk akal.
Cyral
3
Saya akan membuat jawaban lengkap jika saya menemukan waktu malam ini. Namun, sederhananya, saya pada dasarnya menciptakan jalan untuk air. Blok ingin menemukan suatu tempat dengan sedikit tekanan untuk pergi. Mereka menemukan jalur melalui air lain mencari tempat yang memiliki lebih sedikit air daripada mereka (termasuk udara di sebelah air). Ini memecahkan sebagian besar kasus penggunaan.
MichaelHouse
Terima kasih, itu akan dihargai. Saya membaca beberapa wawancara dengan pembuat Benteng Kerdil dan dia melakukan ini saya percaya, tapi saya tidak yakin bagaimana mengatasi beberapa masalah yang dia hadapi, jadi saya tidak pernah benar-benar mencoba.
Cyral
1
Perhatikan bahwa, begitu Anda menambahkan tekanan udara, dua contoh kantong udara berpotensi sepenuhnya valid (ruang bertekanan tertutup). Saya berasumsi Anda tidak menggunakan 255 byte , melainkan nilai 0-255; dalam hal apa pun, Anda mungkin tidak ingin menggunakan jangkauan penuh dengan cara itu. Saya mungkin akan membatasi, hmm, 0-15 untuk tekanan '1 atmosfer' (tidak ada tekanan 'negatif', bukan?), Memungkinkan tekanan lebih tinggi, yang saat ini kurang Anda miliki. Setelah Anda memasukkan blok 'udara' dalam sim, 'bobot' blok air yang lebih tinggi secara alami akan membuatnya mengalir di sekitar tikungan.
Clockwork-Muse

Jawaban:

6

Perhatikan bahwa saya belum pernah melakukan ini; ini hanya ide yang dapat membantu. Atau mungkin benar-benar palsu. Saya sudah ingin mengatasi masalah ini sejak Terraria tetapi saat ini saya tidak bekerja pada permainan seperti itu.

Salah satu cara yang saya pertimbangkan untuk mencoba adalah memberikan setiap blok air permukaan (setiap blok dengan air di dalamnya dan tanpa blok air di atasnya) nilai tekanan awal sama dengan (atau fungsi) ketinggiannya dari bagian bawah dunia. Nilai tekanan implisit dari setiap ubin yang tidak dapat dilewati adalah MAX_PRESSURE(katakanlah 255), dan untuk ubin udara terbuka adalah MIN_PRESSURE(0).

Tekanan kemudian menyebar ke atas / ke bawah / ke samping dari ubin mana pun dengan tekanan lebih tinggi ke ubin dengan tekanan lebih rendah selama setiap centang, gaya automata seluler. Saya harus mendapatkan simulasi yang sebenarnya untuk mencari tahu persis apa yang harus disamakan. Tekanan blok harus sama dengan tekanan implisit ditambah tekanan "kelebihan" dari sekitar yang disamakan (jadi Anda hanya perlu menyimpan tekanan berlebih ini, bukan tekanan implisit).

Jika ubin permukaan memiliki tekanan lebih besar daripada tekanan berbasis ketinggian implisitnya dan jika ubin di atas memiliki ruang bebas untuk air, sebagian kecil air dipindahkan ke atas. Air hanya mengalir turun jika ubin keduanya memiliki ruang karena memiliki tekanan lebih rendah dari yang diharapkan.

Ini secara kasar mensimulasikan gagasan bahwa semakin dalam "titik" air semakin banyak tekanan yang dimilikinya, meskipun nilai tekanan mewakili lebih tinggi daripada tekanan aktual (karena ubin yang lebih tinggi diharapkan memiliki "tekanan" yang lebih tinggi). Ini membuat tekanan agak seperti histilah dalam persamaan (tapi tidak terlalu):

P' = P + qgh

Hasilnya adalah bahwa jika tekanan air lebih tinggi dari yang seharusnya untuk kedalamannya, itu akan didorong ke atas. Itu harus berarti bahwa level air dalam sistem tertutup akan menyamakan tekanan di semua level ketinggian seiring waktu.

Saya tidak yakin bagaimana cara menghadapinya atau apakah seseorang bahkan perlu berurusan dengan "gelembung udara" yang akan dibuat (di mana ubin non-permukaan akan memiliki jumlah air yang tidak penuh saat air didorong ke atas). Saya juga masih tidak yakin bagaimana Anda akan menghindari loop tekanan air yang tidak setara di satu sisi dan kemudian setelah berdetak tidak sama di sisi lain, bolak-balik.

Sean Middleditch
sumber
20

Saya membuat sistem yang mirip dengan yang Anda kejar dalam 3D. Saya punya video pendek yang menunjukkan mekanisme sederhana di sini dan posting blog di sini .

Inilah sedikit gif yang saya buat dari mekanik tekanan di balik dinding yang tidak terlihat (dimainkan dengan kecepatan tinggi):

masukkan deskripsi gambar di sini

Biarkan saya menjelaskan data yang terlibat, untuk memberikan gambaran tentang beberapa fitur sistem. Dalam sistem saat ini, setiap blok air berisi yang berikut dalam 2 Bytes:

//Data2                          Data
//______________________________  _____________________________________
//|0    |0      |000   |000    |  |0        |0       |000      |000   |
//|Extra|FlowOut|Active|Largest|  |HasSource|IsSource|Direction|Height|
//------------------------------  -------------------------------------
  • Height adalah jumlah air dalam kubus, mirip dengan tekanan Anda, tetapi sistem saya hanya memiliki 8 level.
  • Directionadalah arah aliran menuju. Ketika memutuskan di mana air akan mengalir selanjutnya, itu lebih cenderung untuk melanjutkan ke arah saat ini. Ini juga digunakan untuk dengan cepat melacak kembali aliran kembali ke kubus sumbernya ketika dibutuhkan.
  • IsSourcemenunjukkan jika kubus ini adalah kubus sumber, artinya kubus tidak pernah kehabisan air. Digunakan untuk sumber sungai, mata air, dll. Kubus di sebelah kiri dalam gif di atas adalah kubus sumber, misalnya.
  • HasSourcemenunjukkan apakah kubus ini terhubung ke kubus sumber. Ketika terhubung ke sumber, kubus akan mencoba untuk menyadap sumber air lebih banyak sebelum mencari kubus non-sumber "lebih penuh" lainnya.
  • Largestmemberi tahu kubus ini apa aliran terbesar antara itu dan kubus sumbernya. Ini berarti jika air mengalir melalui celah sempit, itu membatasi aliran ke kubus ini.
  • Activeadalah sebuah counter. Ketika kubus ini memiliki aliran aktif melewatinya, ke sana, atau dari situ, aktif akan bertambah. Jika tidak aktif akan dikurangi secara acak. Setelah aktif mencapai nol (artinya tidak aktif), jumlah air akan mulai berkurang di kubus ini. Tindakan semacam ini seperti penguapan atau meresap ke dalam tanah. ( Jika Anda memiliki aliran, Anda harus mengalami pasang surut! )
  • FlowOutmenunjukkan jika kubus ini terhubung ke kubus yang ada di ujung dunia. Begitu jalan menuju ujung dunia dibuat, air cenderung memilih jalan itu daripada yang lain.
  • Extra adalah bit ekstra untuk penggunaan di masa depan.

Sekarang kita tahu datanya, mari kita lihat ikhtisar algoritma tingkat tinggi. Ide dasar dari sistem ini adalah memprioritaskan mengalir turun dan keluar. Seperti yang saya jelaskan di video, saya bekerja dari bawah ke atas. Setiap lapisan air diproses satu tingkat pada satu waktu dalam sumbu y. Kubus untuk setiap level diproses secara acak, setiap kubus akan berusaha menarik air dari sumbernya pada setiap iterasi.

Kubus aliran menarik air dari sumbernya dengan mengikuti arah alirannya kembali hingga mencapai kubus sumber atau kubus aliran tanpa induk. Menyimpan arah aliran di setiap kubus membuat mengikuti jalur ke sumber semudah melintasi daftar tertaut.

Kode pseudo untuk algoritma adalah sebagai berikut:

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()

Aturan dasar untuk memperluas aliran di mana (dipesan berdasarkan prioritas):

  1. Jika kubus di bawah memiliki lebih sedikit air, mengalirlah ke bawah
  2. Jika kubus yang berdekatan pada tingkat yang sama memiliki lebih sedikit air, mengalirlah secara lateral.
  3. Jika kubus di atas memiliki lebih sedikit air DAN kubus sumber lebih tinggi dari kubus di atas, mengalirlah ke atas.

Saya tahu, itu level yang cukup tinggi. Tapi sulit untuk masuk ke detail lebih tanpa mendapatkan cara ke detail.

Sistem ini bekerja dengan cukup baik. Saya dapat dengan mudah mengisi lubang-lubang air, yang meluap untuk terus keluar. Saya bisa mengisi terowongan berbentuk U seperti yang Anda lihat di gif di atas. Namun, seperti yang saya katakan, sistemnya tidak lengkap dan saya belum menyelesaikan semuanya. Saya belum bekerja pada sistem aliran dalam waktu yang lama (saya memutuskan itu tidak diperlukan untuk alpha dan saya akan menahannya). Namun, masalah yang saya hadapi ketika saya tunda dimana:

  • Kolam renang . Ketika mendapatkan genangan air yang besar, petunjuk dari anak ke orang tua seperti kekacauan gila dari kubus acak apa pun yang dipilih untuk mengalir ke arah mana pun. Seperti mengisi bathtub dengan tali konyol. Saat Anda ingin mengalirkan bak mandi, haruskah Anda mengikuti jalur benang konyol kembali ke sumbernya? Atau haruskah Anda mengambil apa pun yang terdekat? Jadi dalam situasi di mana kubus berada di kolam besar, mereka mungkin seharusnya mengabaikan arus induknya dan menarik dari apa pun yang ada di atas mereka. Saya datang dengan beberapa kode kerja dasar untuk ini, tetapi tidak pernah memiliki solusi elegan yang bisa saya senangi.

  • Banyak orang tua . Aliran anak dapat dengan mudah diberi makan oleh lebih dari satu aliran induk. Tetapi anak yang memiliki pointer ke orang tua tunggal tidak akan membiarkan itu. Ini dapat diperbaiki dengan menggunakan bit yang cukup untuk memungkinkan bit untuk setiap arah induk yang mungkin. Dan kemungkinan mengubah algoritma untuk secara acak memilih jalur dalam kasus beberapa orang tua. Tapi, saya tidak pernah sempat untuk menguji dan melihat masalah apa yang mungkin terungkap.

MichaelHouse
sumber
Terima kasih! Sangat informatif! Saya akan mulai mengerjakan ini segera dan menerimanya jika semuanya berjalan dengan baik.
Cyral
Tentu saja. Saya membayangkan hibrida dari sistem Anda dan yang ini akan sangat efektif untuk dunia 2D. Ping saya dalam obrolan (dengan @ byte56) jika Anda ingin membahas detail.
MichaelHouse
Baiklah, mungkin sehari atau lebih sebelum saya mendapatkan kesempatan untuk mencoba ini.
Cyral
3
Maklum. Saya mungkin menghabiskan waktu berbulan-bulan untuk mengerjakannya (dan mengerjakannya kembali). Saya akan berada di sekitar untuk sementara waktu :)
MichaelHouse
2

Saya agak setuju dengan Sean tapi saya akan melakukannya sedikit berbeda:

Blok menghasilkan tekanan yang sama dengan beratnya sendiri (berapa banyak air di dalamnya) dan menerapkannya pada blok di bawah dan di sebelahnya. Saya tidak melihat alasan mengapa posisinya di dunia relevan.

Pada setiap kutu, gerakkan air dari tekanan tinggi ke tekanan rendah, tetapi gerakkan hanya sebagian kecil dari air yang dibutuhkan untuk menyamakan kedudukan. Air juga dapat didorong ke atas jika tekanan di blok terlalu besar untuk tekanan yang diterapkan di alun-alun.

Anda akan mendapatkan loop di mana tekanan air mengalir terlalu jauh satu arah dan kemudian harus memperbaiki tetapi karena Anda tidak memindahkan seluruh jumlah air per kutu ini akan teredam. Saya pikir itu sebenarnya hal yang baik karena Anda akan mendapatkan efek gelombang ketika air membanjiri area seperti yang Anda bayangkan.

Loren Pechtel
sumber
Jika air naik ketika tekanan yang diterapkan dari atas terlalu besar, itu tidak akan bergerak ke blok tekanan yang lebih rendah. Agar tekanan di atas terlalu besar, itu harus lebih besar daripada blok di bawah ini. Selain itu, tekanan harus bergerak ke atas serta ke bawah dan ke kiri / kanan.
MichaelHouse
@ Byte56 Anda salah menafsirkan apa yang saya katakan. Saya mengatakan air naik ketika tekanan di blok yang Anda analisis terlalu tinggi untuk tekanan yang diterapkan dari atas, bukan karena tekanan dari atas terlalu besar!
Loren Pechtel
OK, jadi izinkan saya ulangi apa yang Anda katakan, jadi saya mengerti: "air naik ketika tekanan di blok yang Anda analisis lebih besar daripada tekanan yang diterapkan dari atas". Apakah itu benar?
MichaelHouse
@ Byte56 Ya. Tekanan di blok harus berat air di atasnya atau diterapkan ke samping ketika kita memiliki permukaan padat di suatu tempat di atas. Terlalu sedikit tekanan berarti tidak ada cukup air di atas, naikkan air.
Loren Pechtel
Saya hanya ingin menambahkan, bahwa jika Anda berurusan dengan air yang mengalir ini tidak akan cukup dan Anda juga harus mempertimbangkan inersia atau air akan bergerak terlalu lambat.
kubus
1

Anda dapat menambahkan aturan yang mencoba untuk pergi ke kiri atau kanan (melalui dinding) dengan ubin sampai Anda menemukan tempat gratis, dimulai dengan lapisan di bagian bawah. Jika Anda tidak dapat menemukan, maka ubin tetap pada posisi saat ini. Jika Anda menemukan, maka aturan lain akan menjamin penggantian ubin yang dipindahkan (jika perlu).

almanegra
sumber
Ini juga merupakan ide yang baik, tidak yakin apakah itu akan berhasil dalam semua kasus tetapi saya akan mempertimbangkannya.
Cyral
Baik! Biarkan saya tahu apakah itu berhasil atau tidak. salam
almanegra
Saya akan, sedikit sibuk belakangan ini.
Cyral
-2

mengapa Anda tidak bisa mendefinisikan jenis blok lain yang bertindak sebagai jumlah tekanan tidak bergerak? Karena itu ketika Anda menggunakan cara Anda biasanya memindahkan balok air dan memeriksa apakah itu bisa naik, itu tidak bisa.

Yang lebih baik adalah menambahkan definisi lain pada blok-blok yang memungkinkan pengguna untuk memasukkan jumlah tekanan per blok, meningkatkan tekanan sesuai dengan jumlah blok air yang menambahkannya.

SD1990
sumber
1
"karenanya ketika kamu menggunakan cara kamu biasanya memindahkan balok air dan memeriksa apakah itu bisa naik, itu tidak bisa." Ya ... Itu sudah tidak bisa. Itulah masalahnya, saya tidak mencari cara untuk membuatnya tetap sama.
Cyral