Izgara tabanlı bir sıvı simülasyonda basınç simülasyonu


30

XNA oyunumda 2B ızgara tabanlı bir su sistemine sahibim, suyun düşmesini ve yayılmasını simüle etmek için hücresel otomatları kullanan bir yöntemimiz var.

Bir yokuş aşağı akan su örneği:

Su Fiziği

Her karo bir baytta depolanan 0 ila 255 değerinde sıvı kütlesini içerebilir. Yapmadığım floatseski su sistemini kullanmıyorum, ancak bu komplikasyonlar ekledi ve performans açısından bir darbe aldı.

Her su kiremiti basit bir kurallar dizisiyle kendini günceller:

  1. Aşağıdaki döşemenin içinde boşluk varsa, geçerli döşemeden alta doğru mümkün olduğunca hareket edin (Aşağı Akış)
  2. 2 taraf aynı değilse ve sıfır değilse ve her ikisi de fena değilse, 3 fayansın (sol + akım + sağ) toplamını alırız ve kalanını ortadaki (mevcut) fayans üzerinde bırakarak 3'e böleriz
  3. Yukarıdaki kural toplam olarak 2 sayısı verdiyse, döşemeleri iki tarafa bölmeliyiz (1, 0, 1)
  4. Kural 2 toplam olarak 1 verirse, içine akacak rastgele bir taraf seçin
  5. Kural 2 başarısız olursa, bir tarafın pasif olup olmadığını, diğerinin pasif olup olmadığını kontrol etmeliyiz. Bu doğruysa, geçerli döşemeyi 2 döşemenin yarısına böleriz

Bu mantığı baskı içerecek şekilde nasıl genişletebilirim? Basınç, sıvıları "U-Bends" üzerine yükseltir ve hava ceplerini doldurur.

Bunun şu anda nasıl başarısız olduğuna dair bir örnek:

Basınç Arızası

Su, U-Bend'in her iki tarafında akmalı ve eşitlenmelidir. Ek olarak, bir su bloğunun ne kadar aşağı olduğunu ve bu nedenle ne kadar baskı yaşadığını bulmak için yöntemler geliştirdim. Şimdi bu sayıları alıp basıncı eşitlemek için diğer alanlara uygulayabilmeliyim.


Sorun, hücresel otomatları tutmak zor. O zamandan beri her bloğun yanında olandan daha fazlasını bilmesi gerekiyor. 3D olarak istediğinize benzer bir sistem yarattım. Oldukça karmaşık bir sistem, ancak 2D'de daha uygulanabilir olacağını düşünüyorum.
Michaelhouse

@ Byte56 Peki , makul hızda çalışmaya devam edebildiğimiz sürece hücresel otomatlar olmasına ihtiyacımız yok .
Cyral

3
Bu akşam biraz zaman bulursam, tam bir cevap oluşturacağım. Ancak, basitçe söylemek gerekirse, esasen su için yol bulmayı yarattım. Bloklar, daha az baskısı olan bir yer bulmak istiyor. Sudan daha az suya sahip başka bir yer arıyorlar (suyun yanında hava var). Kullanım durumlarının büyük çoğunluğunu çözer.
Michaelhouse

Teşekkürler, çok memnun olurum. Cüce Kalesi'nin yapımcısıyla bazı röportajlar okudum ve inandığımı yaptı, ancak karşılaştığı sorunların bazılarının nasıl üstesinden geleceğinden emin değildim, bu yüzden asla denemedim.
Cyral

1
Hava basıncı eklendikten sonra, iki hava cebi örneğinin potansiyel olarak tamamen geçerli olduğunu unutmayın (kapalı basınç odaları). 255 bayt kullanmadığınızı , bunun yerine 0-255 değerlerini kullandığınızı farz ediyorum ; Her durumda, muhtemelen tüm aralığı bu şekilde kullanmak istemeyeceksiniz. Muhtemelen, şu anda eksik olan yüksek basınçlara izin vererek, '1 atmosfer' basınç için (hmm, 0-15) ('negatif' basınç gibi bir şey yoktur) sınırlandırırdım. Sim'e 'hava' bloklarını ekledikten sonra, su bloklarının doğal olarak daha yüksek 'ağırlığı' virajların etrafında akmasına neden olmalıdır.
Clockwork-Muse,

Yanıtlar:


6

Bunu asla yapmadığımı unutmayın; bunlar sadece yardımcı olabilecek fikirler. Ya da tamamen sahte olabilir. Terraria’dan beri bu sorunu çözmek istiyordum ama şu anda böyle bir oyun üzerinde çalışmıyorum.

Denemeyi düşündüğüm bir yol, her yüzey su bloğunu (içinde su olan ve üstünde su bloğu olmayan herhangi bir blok), dünyanın tabanından yüksekliğine eşit (veya bir işlevi) olan ilk basınç değerini vermektir . Geçilemez bir karonun örtük basınç değeri MAX_PRESSURE(diyelim 255) ve bir açık hava karosu için MIN_PRESSURE(0).

Daha sonra basınç, her kene, hücresel otomat tarzında her kene sırasında daha düşük basınçlı olan karolara daha yüksek basınçlı bir karodan yukarı / aşağı / yanlara doğru yayılır. Tam olarak neye eşitleneceğini bulmak için gerçek bir simülasyon yaptırmalıydım. Bir bloğun basıncı, örtülü basıncına ve etrafındaki dengelenmiş "aşırı" basınca eşit olmalıdır (bu nedenle örtülü basıncı değil, yalnızca bu aşırı basıncı saklamanız gerekir).

Bir yüzey döşemesi, örtülü yüksekliğe dayalı basınca göre daha büyük bir basınca sahipse ve yukarıdaki döşeme, su için boş alana sahipse, küçük bir kısım su yukarı doğru hareket ettirilir. Su, her iki karo da beklenenden daha düşük bir basınca sahipse odadan aşağı akar.

Bu, kabaca, suyun, daha yüksek basınca sahip olmasına rağmen, basınç değerlerinin gerçek basınçtan daha yüksek olduğunu belirttiği fikrini simüle eder (çünkü daha yüksek karoların daha yüksek "basınca sahip olması beklenir). Bu, basıncı h, denklemdeki terim gibi sorta eder (ama gerçekte değil):

P' = P + qgh

Sonuç olarak, eğer suyun basıncı derinlik için olması gerekenden daha yüksekse, yukarı itilir. Kapalı sistemlerdeki su seviyelerinin zaman içindeki tüm yükseklik seviyelerinde basıncı eşitleyeceği anlamına gelmelidir.

Nasıl başa çıkılacağından veya birinin yaratılacak olan "hava kabarcıkları" ile başa çıkması gerekip gerekmediğinden emin değilim (burada yüzey yukarı çini, su yukarı doğru itildiğinde dolu olmayan su miktarına sahip olacaktır). Aynı zamanda, bir yandan su basınçları döngülerinin bir tarafta eşit olmadığından ve diğer tarafta eşit olmayan bir şekilde ileri geri tıklamayı nasıl önleyeceğinizden hala emin değilim.


20

3D olarak takip ettiğinize benzer bir sistem yarattım. Burada basit mekaniğini gösteren kısa bir videom ve burada bir blog yazısı var .

İşte görünmez bir duvarın arkasındaki basınç mekaniğinden (yüksek hızda oynandı) yaptığım küçük bir gif:

görüntü tanımını buraya girin

Sistemin bazı özellikleri hakkında fikir vermek için ilgili verileri açıklamama izin verin. Mevcut sistemde, her su bloğu 2 Bayt'ta aşağıdakileri içerir:

//Data2                          Data
//______________________________  _____________________________________
//|0    |0      |000   |000    |  |0        |0       |000      |000   |
//|Extra|FlowOut|Active|Largest|  |HasSource|IsSource|Direction|Height|
//------------------------------  -------------------------------------
  • Height basınçtaki gibi, küpteki su miktarıdır, ancak sistemimin sadece 8 seviyesi vardır.
  • Directionakışın gittiği yöndür. Suyun bundan sonra nereye akacağına karar verirken, mevcut yönünde devam etmesi daha olasıdır. Bu, gerektiğinde kaynak küpüne giden bir akışı hızla izlemek için de kullanılır.
  • IsSourcebu küpün bir kaynak küp olup olmadığını gösterir, yani hiç su tükenmez. Nehirlerin, kaynakların, vb. Kaynakları için kullanılır. Yukarıdaki gifte soldaki küp, örneğin bir kaynak küpüdür.
  • HasSourcebu küpün bir kaynak küpüne bağlı olup olmadığını gösterir. Bir kaynağa bağlandığınızda, küpler, diğer "dolgun" kaynak olmayan küpleri aramadan önce kaynağa daha fazla su için dokunmaya çalışacaktır.
  • LargestBu küpe, kendisiyle kaynak küp arasındaki en büyük akışın ne olduğunu söyler. Bu, suyun dar bir boşluktan akması durumunda, bu küpün akışını sınırlar.
  • Activebir sayaçtır. Bu küp, içinden, içinden veya içinden geçen aktif bir akışa sahip olduğunda, aktif artar. Aksi halde aktif rastgele azalır. Aktif sıfıra ulaştığında (yani aktif değil), bu küpte su miktarı azalmaya başlayacaktır. Bu tür buharlaşma veya toprağa batırma gibi davranır. ( Akışınız varsa, ebb olmalıydı! )
  • FlowOutbu küpün dünyanın kenarında bir küpe bağlı olup olmadığını gösterir. Dünyanın kenarına giden bir yol yapıldıktan sonra, su bu yolu diğerlerinden seçmeye meyillidir.
  • Extra gelecekteki kullanım için ekstra bir bit.

Şimdi verileri bildiğimize göre, algoritmaya üst düzey bir bakış atalım. Sistemin temel fikri, aşağı ve dışarı akan önceliği belirlemektir. Videoda açıkladığım gibi, aşağıdan yukarıya doğru çalışıyorum. Her su katmanı, y ekseninde bir seferde bir seviye işlenir. Her seviye için küpler rastgele işlenir, her küp her bir iterasyonda kaynağından su çekmeye çalışır.

Akış küpleri, akış yönlerini izleyerek bir kaynak küpüne veya ebeveynsiz bir akış küpüne erişinceye kadar suyu kaynağından alır. Akış yönünü her bir küpte saklamak, kaynağa giden yolu izlemeyi bağlantılı bir listeyi dolaşmak kadar kolay hale getirir.

Algoritmanın sözde kodu aşağıdaki gibidir:

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

Bir akışı genişletmek için temel kurallar (öncelik sırasına göre sıralanmıştır):

  1. Alttaki küpün suyu daha azsa, aşağı akın
  2. Aynı seviyedeki bitişik küp daha az su içeriyorsa, yanal olarak akın.
  3. Yukarıdaki küp daha az su içeriyorsa ve kaynak küp yukarıdaki küpten daha yüksekse, yukarı akın.

Biliyorum, bu oldukça yüksek bir seviye. Ama almadan daha detaya almak zor yolu detaya.

Bu sistem oldukça iyi çalışıyor. Dışarıya doğru devam etmek için taşan su birikintilerini kolayca doldurabilirim. Yukarıdaki gifte gördüğünüz gibi U şeklindeki tünelleri doldurabilirim. Ancak dediğim gibi, sistem eksik ve henüz her şeyi çözmedim. Akış sistemi üzerinde uzun zamandır çalışmadım (alfa için gerek olmadığına karar verdim ve beklemeye aldım). Ancak, beklettiğimde uğraştığım konular nerede kaldı:

  • Havuzlar . Büyük bir su havuzu elde edildiğinde, çocuktan ebeveyne kadar olan işaretçiler, hangi yöne akacak olursa olsun, rastgele bir küpün seçildiği çılgın bir karmaşa gibidir. Aptal ip ile bir küvet doldurmak gibi. Küveti boşaltmak istediğinizde, saçma ipin yolunu kaynağına geri mi götürmelisiniz? Yoksa en yakın olanı mı almalısın? Bu nedenle, küplerin büyük bir havuzda olduğu durumlarda, ebeveynlerin akışlarını görmezden gelmeleri ve üstlerindeki her şeyden çekmeleri gerekir. Bunun için bazı temel çalışma kodları buldum, ancak hiçbir zaman mutlu olabileceğim zarif bir çözüm olmadı.

  • Birden ebeveynler . Bir çocuk akışı, birden fazla ana akım tarafından kolayca beslenebilir. Ancak, tek bir ebeveyne imleci olan çocuk buna izin vermez. Bu, her olası ebeveyn yönü için bir bit bırakacak kadar bit kullanarak düzeltilebilir. Muhtemelen çoklu ebeveynler durumunda rastgele bir yol seçmek için algoritma değiştirme. Ancak, başka hangi sorunları ortaya çıkarabileceğini test etmek ve görmek için hiç bir zaman bu işe girmedim.


Teşekkürler! Çok bilgilendirici! Yakında bunun üzerinde çalışmaya başlayacağım ve her şey yolunda giderse kabul edeceğim.
Cyral

Tabi ki. Sisteminizin bir melezini hayal ediyorum ve bu bir 2D dünyası için çok etkili olurdu. Ayrıntıları tartışmak istiyorsanız, beni sohbete (@ byte56 ile) ekleyin.
MichaelHouse

Tamam, bunu denemek için bir şansım olmadan bir gün kadar önce olabilir.
Cyral

3
Anlaşılır. Muhtemelen aylarca onu çalıştırarak geçirdim (ve tekrar çalıştırarak). Bir süre olsa buralarda olacağım :)
MichaelHouse

2

Sean ile aynı fikirdeyim ama biraz daha farklı yapardım:

Bir blok, kendi ağırlığına eşit bir basınç oluşturur (içinde ne kadar su vardır) ve altındaki bloklara uygular. Dünyadaki konumunun alakalı olmasının bir nedeni olmadığını görüyorum.

Her kene üzerinde suyu yüksek basınçtan düşük basınca kaydırın, ancak eşitlemek için gereken suyun sadece bir kısmını hareket ettirin. Eğer bloktaki basınç, kare üzerine uygulanan basınç için çok büyükse, su da arttırılabilir.

Su basıncının bir yönden çok fazla aktığı ve ardından düzeltilmesi gereken döngüler elde edersiniz, ancak kene başına tüm su miktarını hareket ettirmediğiniz için bunlar sönecektir. Su, gerçekte olduğu gibi bir alana taşırken dalgalanma etkisi alacağınız için bence iyi bir şey.


Yukarıdan uygulanan basınç çok büyük olduğunda su yükselecek olsaydı, daha düşük bir basınç bloğuna girmezdi. Yukarıdaki baskının çok büyük olması için, aşağıdaki bloktan daha büyük olması gerekir. Ek olarak, basıncın aşağı, yukarı ve sola / sağa doğru hareket etmesi gerekir.
MichaelHouse

@ Byte56 Söylediklerimi yanlış yorumluyorsunuz. Analiz ettiğiniz bloktaki basınç yukarıdan uygulanan basınç için çok yüksek olduğunda suyun yukarı çıktığını söylüyorum, yukarıdan gelen basınç çok fazla değil!
Loren Pechtel

Tamam, öyleyse söylediklerini tekrar ifade etmeme izin ver, anlıyorum: "analiz ettiğin bloktaki basınç yukarıdan uygulanan basınçtan fazla olduğunda su yükseliyor ". Bu doğru mu?
MichaelHouse

@ Byte56 Evet. Bloktaki basınç, üstündeki suyun ağırlığı veya yukarıda bir yerde katı bir yüzeye sahipken yanlara uygulanmalıdır. Üzerine çok az baskı yapılması, üzerinde yeterli su bulunmadığı ve suyun yukarı çekildiği anlamına gelir.
Loren Pechtel

Eklemek isterim ki, eğer akan su ile uğraşıyorsanız, bu yeterli olmayacak ve ayrıca ataleti göz önünde bulundurmanız gerekecek ya da su çok yavaş hareket edecektir.
küp

1

Boş bir nokta bulana kadar karolarla sola veya sağa (duvarlardan geçerek), altta bulunan katmanlardan başlayarak bir nokta ekleyebilirsiniz. Bulamazsanız, döşeme geçerli konumda kalır. Eğer bulursanız, başka kurallar taşınmış döşemenin değiştirilmesini garanti edecektir (gerekirse).


Bu aynı zamanda iyi bir fikir, her durumda işe yarayıp yaramayacağından emin değilim, ama bunu düşüneceğim.
Cyral

Tamam! İşe yarayıp yaramadığını bana bildir. Saygılarımızla
almanegra

Son zamanlarda biraz meşguldüm.
Cyral

-2

Neden taşınmaz bir basınç olarak hareket eden başka bir blok tipi tanımlayamıyorsunuz? Bu nedenle, su bloklarını normal olarak hareket ettirmek ve hareket edip etmeyeceğini kontrol etmek için kullandığınız zaman, kullanamaz.

Daha da iyisi, kullanıcının blok başına basınç miktarını girmesine olanak sağlayan bloklara başka bir tanım eklemek olabilir, buna ilave olarak basıncı su bloklarının miktarına göre artırabilir.


1
"Bu nedenle, su bloklarını normal olarak hareket ettirmek ve hareket edip etmeyeceğini kontrol etmek için kullandığınız zaman, kullanamazsınız." Evet ... Zaten yapamaz. Sorun bu, aynı kalmasını sağlayacak bir yol aramıyorum.
Cyral
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.