W mojej grze XNA mam system wodny oparty na siatce 2D, mamy metodę wykorzystującą automaty komórkowe do symulacji spadania i rozprzestrzeniania się wody.
Przykład wody spływającej ze zbocza:
Każda płytka może zawierać masę od 0 do 255 wartości cieczy, zapisanych w bajcie. Nie używam floats
, starego systemu wodnego, który miałem, ale to komplikowało i miało hit wydajności.
Każdy kafelek wody aktualizuje się za pomocą prostego zestawu zasad:
- Jeśli na kafelku poniżej jest miejsce, przenieś jak najwięcej z bieżącego kafelka na dolny (Flow Down)
- Jeśli 2 strony nie są takie same i nie są zerowe, a obie są przejezdne, otrzymujemy sumę 3 płytek (lewa + prąd + prawa) i dzielimy ją przez 3, pozostawiając resztę na środkowej (bieżącej) płytce
- Jeśli powyższa reguła podała liczbę 2 jako sumę, powinniśmy podzielić płytki na dwie strony (1, 0, 1)
- Jeśli reguła 2 podała 1 jako sumę, wybierz losową stronę, do której wpłynie
- Jeśli reguła 2 zawiodła, powinniśmy sprawdzić, czy jedna strona jest przejezdna, a druga nie. Jeśli to prawda, dzielimy bieżącą płytkę na pół dla 2 płytek
Jak mogę rozwinąć tę logikę, aby uwzględnić presję? Ciśnienie spowoduje wzrost cieczy powyżej „zakrętów U” i wypełnienie kieszeni powietrznych.
Przykład, jak to się obecnie nie powiedzie:
Woda powinna płynąć i wyrównywać się po obu stronach U-Bend. Dodatkowo stworzyłem metody, aby dowiedzieć się, jak daleko jest blok wodny, a tym samym, jak duży nacisk on odczuwa. Teraz muszę móc wziąć te liczby i zastosować je w innych obszarach, aby wyrównać presję.
Odpowiedzi:
Zauważ, że nigdy tego nie zrobiłem; są to tylko pomysły, które mogą pomóc. Lub może być całkowicie fałszywy. Chciałem rozwiązać ten problem od czasu Terrarii, ale obecnie nie pracuję nad taką grą.
Sposobem, który rozważałem, jest nadanie każdemu blokowi wody powierzchniowej (każdemu blokowi z wodą i bez bloku wodnego) początkowej wartości ciśnienia równej (lub funkcji) jego wysokości z dna świata. Implikowana wartość ciśnienia dowolnej nieprzejezdnej płytki wynosi
MAX_PRESSURE
(powiedzmy 255), a dla płytki na wolnym powietrzu wynosiMIN_PRESSURE
(0).Nacisk jest następnie rozkładany w górę / w dół / na boki z dowolnej płytki o wyższym ciśnieniu na płytki o niższym ciśnieniu podczas każdego tyknięcia, w stylu automatów komórkowych. Musiałbym wykonać rzeczywistą symulację, aby dowiedzieć się dokładnie, do czego się wyrównać. Ciśnienie bloku powinno być równe jego ciśnieniu niejawnemu powiększonemu o „nadwyżkowe” ciśnienie z otoczenia wyrównywane (więc wystarczy przechowywać to nadciśnienie, a nie ciśnienie ukryte).
Jeśli płytka powierzchniowa ma ciśnienie większe niż jej domyślny nacisk oparty na wysokości, a jeśli płytka powyżej ma wolną przestrzeń dla wody, niewielka część wody jest przesuwana w górę. Woda spływa tylko wtedy, gdy na obu płytkach jest miejsce, ponieważ ciśnienie jest niższe niż oczekiwano.
To z grubsza symuluje ideę, że im głębszy „punkt” wody, tym większy jest nacisk, chociaż wartości ciśnienia reprezentują większą wysokość niż ciśnienie rzeczywiste (ponieważ oczekuje się, że wyższe płytki będą miały wyższe „ciśnienie”). To sprawia, że ciśnienie jest trochę takie jak
h
w równaniu (ale nie bardzo):Powoduje to, że jeśli ciśnienie wody jest wyższe niż powinno być dla jego głębokości, zostanie ono wypchnięte. Powinno to oznaczać, że poziomy wody w zamkniętych systemach wyrównają ciśnienie na wszystkich poziomach wysokości w czasie.
Nie jestem pewien, jak sobie z tym poradzić ani czy w ogóle trzeba poradzić sobie z „pęcherzykami powietrza”, które powstałyby (gdzie płytka niepowierzchniowa będzie miała niepełne ilości wody, gdy woda zostanie wypchnięta w górę). Nadal nie jestem pewien, jak można uniknąć nierówności ciśnienia wody po jednej stronie, a następnie po zaznaczeniu nierówności po drugiej stronie, tam iz powrotem.
źródło
Stworzyłem system podobny do tego, którego szukasz w 3D. Mam krótki film wykazujący proste mechanikę nim tutaj i na blogu tutaj .
Oto mały gif, który zrobiłem z mechaniki nacisku za niewidzialną ścianą (graną z dużą prędkością):
Pozwól mi wyjaśnić związane z tym dane, aby dać wyobrażenie o niektórych funkcjach systemu. W obecnym systemie każdy blok wody zawiera następujące elementy w 2 bajtach:
Height
to ilość wody w kostce, podobna do twojego ciśnienia, ale mój system ma tylko 8 poziomów.Direction
jest kierunkiem przepływu. Przy podejmowaniu decyzji o tym, gdzie woda przepłynie w następnej kolejności, bardziej prawdopodobne jest kontynuowanie jej w bieżącym kierunku. Służy to również do szybkiego śledzenia przepływu z powrotem do kostki źródłowej, gdy jest to potrzebne.IsSource
wskazuje, czy ta kostka jest kostką źródłową, co oznacza, że nigdy nie zabraknie jej wody. Używany do źródła rzek, źródeł itp. Sześcian po lewej w powyższym gifie to na przykład sześcian źródłowy.HasSource
wskazuje, czy ta kostka jest podłączona do kostki źródłowej. Po podłączeniu do źródła kostki będą próbowały stukać źródło w celu uzyskania większej ilości wody, zanim zaczną szukać innych „pełniejszych” kostek niebędących źródłami.Largest
mówi tej kostce, jaki jest największy przepływ między nią a jej kostką źródłową. Oznacza to, że jeśli woda przepływa przez wąską szczelinę, ogranicza przepływ do tego sześcianu.Active
jest licznikiem. Kiedy ten sześcian ma aktywny przepływ przechodzący przez niego, do niego lub z niego, aktywny zostaje zwiększony. W przeciwnym razie aktywny jest losowo zmniejszany. Gdy aktywna osiągnie zero (czyli nieaktywna), ilość wody zacznie się zmniejszać w tej kostce. Ten rodzaj działa jak parowanie lub wnikanie w ziemię. ( Jeśli masz przepływ, powinieneś odpłynąć! )FlowOut
wskazuje, czy ta kostka jest połączona z kostką na skraju świata. Po utworzeniu ścieżki na skraj świata woda zwykle wybiera tę ścieżkę.Extra
to dodatkowy kawałek do wykorzystania w przyszłości.Teraz, gdy znamy dane, spójrzmy na ogólny przegląd algorytmu. Podstawową ideą systemu jest ustalenie priorytetów w przepływie i odpływie. Jak wyjaśniam na filmie, pracuję od podstaw. Każda warstwa wody jest przetwarzana jeden poziom na raz na osi y. Kostki dla każdego poziomu są przetwarzane losowo, każda kostka będzie próbowała wyciągać wodę ze źródła podczas każdej iteracji.
Kostki przepływowe wyciągają wodę ze źródła, podążając z powrotem ich kierunkiem przepływu, aż dotrą do kostki źródłowej lub kostki przepływowej bez elementu nadrzędnego. Zapamiętanie kierunku przepływu w każdej kostce sprawia, że podążanie ścieżką do źródła jest tak proste, jak przejście przez połączoną listę.
Pseudo-kod dla algorytmu jest następujący:
Podstawowe zasady rozszerzania przepływu gdzie (uporządkowane według priorytetów):
Wiem, to całkiem wysoki poziom. Ale trudno dostać się do bardziej szczegółowo, bez uzyskiwania drogę w szczegółach.
Ten system działa całkiem dobrze. Mogę łatwo napełnić doły wody, które przelewają się, aby kontynuować na zewnątrz. Mogę wypełnić tunele w kształcie litery U, jak widać na powyższym gifie. Jednak, jak powiedziałem, system jest niekompletny i jeszcze nie wszystko dopracowałem. Dawno nie pracowałem nad systemem przepływu (zdecydowałem, że nie jest on potrzebny w wersji alfa i zawiesiłem go). Jednak problemy, z którymi miałem do czynienia, gdy je odłożyłem, gdzie:
Baseny . Gdy zdobywamy dużą kałużę wody, wskaźniki od dziecka do rodzica są jak szalony bałagan z dowolnej losowej kostki wybranej do płynięcia w dowolnym kierunku. Jak wypełnienie wanny głupim sznurkiem. Kiedy chcesz opróżnić wannę, powinieneś podążać ścieżką głupiego sznurka z powrotem do źródła? A może powinieneś wziąć to, co jest najbliższe? Tak więc w sytuacjach, w których kostki znajdują się w dużej sadzawce, prawdopodobnie powinny po prostu zignorować przepływy rodziców i wyciągnąć wszystko, co jest nad nimi. Wymyśliłem do tego podstawowy działający kod, ale nigdy nie miałem eleganckiego rozwiązania, z którego mógłbym być zadowolony.
Wielu rodziców . Strumień potomny może być łatwo zasilany przez więcej niż jeden strumień macierzysty. Ale dziecko mające wskaźnik do samotnego rodzica nie pozwoli na to. Można to naprawić za pomocą wystarczającej liczby bitów, aby pozwolić na bit dla każdego możliwego kierunku rodzica. I prawdopodobnie zmiana algorytmu, aby losowo wybrać ścieżkę w przypadku wielu rodziców. Ale nigdy nie zabrałem się za to, aby przetestować i zobaczyć, jakie inne problemy mogą ujawnić.
źródło
W pewnym sensie zgadzam się z Seanem, ale zrobiłbym to trochę inaczej:
Blok generuje ciśnienie równe jego własnej masie (ile wody w nim jest) i przykłada go do bloków poniżej i obok niego. Nie widzę powodu, dla którego jego pozycja na świecie jest istotna.
Na każdym kleszczu przenieś wodę z wysokiego ciśnienia na niskie ciśnienie, ale przenieś tylko ułamek wody potrzebnej do wyrównania. Wodę można również wypchnąć, jeśli ciśnienie w bloku jest zbyt duże, aby wywierać nacisk na kwadrat.
Dostaniesz pętle, w których ciśnienie wody przepływa zbyt daleko w jedną stronę, a następnie trzeba je poprawić, ale ponieważ nie przesuniesz całej ilości wody na kleszcza, zostaną one wytłumione. Myślę, że to naprawdę dobra rzecz, ponieważ dostaniesz efekt przypływu, gdy woda zaleje obszar, tak jak w rzeczywistości.
źródło
Możesz dodać regułę, która próbuje przejść w lewo lub w prawo (przez ściany) z kafelkami, aż znajdziesz wolne miejsce, zaczynając od warstw na dole. Jeśli nie możesz znaleźć, płytka pozostaje na bieżącej pozycji. Jeśli znajdziesz, inne zasady zagwarantują wymianę przeniesionego kafelka (jeśli to konieczne).
źródło
dlaczego nie możesz zdefiniować innego rodzaju bloku, który działa jak nieznośny nacisk? Dlatego kiedy używasz swojego sposobu normalnego przemieszczania bloków wodnych i sprawdzania, czy może się przesunąć w górę, nie może.
Jeszcze lepiej byłoby dodać kolejną definicję do tych bloków, która pozwala użytkownikowi wprowadzić ciśnienie na blok, zwiększając ciśnienie zgodnie z ilością dodawanych bloków wodnych.
źródło