Tôi đã tạo một hệ thống tương tự như hệ thống mà bạn theo đuổi trong 3D. Tôi có một đoạn video ngắn trình bày các cơ chế đơn giản của nó ở đây và một bài đăng blog ở đây .
Đây là một gif nhỏ tôi làm bằng cơ học áp lực đằng sau một bức tường vô hình (chơi ở tốc độ cao):
Hãy để tôi giải thích các dữ liệu liên quan, để đưa ra ý tưởng về một số tính năng của hệ thống. Trong hệ thống hiện tại, mỗi khối nước chứa 2 byte sau:
//Data2 Data
//______________________________ _____________________________________
//|0 |0 |000 |000 | |0 |0 |000 |000 |
//|Extra|FlowOut|Active|Largest| |HasSource|IsSource|Direction|Height|
//------------------------------ -------------------------------------
Height
là lượng nước trong khối lập phương, tương tự như áp lực của bạn, nhưng hệ thống của tôi chỉ có 8 cấp độ.
Direction
là hướng của dòng chảy. Khi quyết định nơi nước sẽ chảy tiếp theo, nhiều khả năng nó sẽ tiếp tục theo hướng hiện tại. Điều này cũng được sử dụng để nhanh chóng theo dõi một dòng chảy ngược lên khối nguồn của nó khi cần thiết.
IsSource
cho biết nếu khối này là một khối nguồn, có nghĩa là nó không bao giờ hết nước. Được sử dụng cho nguồn của sông, suối, v.v ... Khối bên trái trong gif ở trên là một khối nguồn chẳng hạn.
HasSource
cho biết nếu khối này được kết nối với một khối nguồn. Khi được kết nối với một nguồn, các khối sẽ cố gắng khai thác nguồn để có thêm nước trước khi tìm các khối không nguồn "đầy đủ" khác.
Largest
cho khối này biết dòng chảy lớn nhất giữa nó và khối nguồn của nó là gì. Điều này có nghĩa là nếu nước chảy qua một khe hẹp, nó sẽ hạn chế dòng chảy vào khối này.
Active
là một truy cập. Khi khối này có dòng hoạt động đi qua nó, đến nó hoặc từ nó, hoạt động được tăng lên. Nếu không hoạt động được giảm ngẫu nhiên. Khi hoạt động chạm 0 (nghĩa là không hoạt động), lượng nước sẽ bắt đầu giảm trong khối này. Loại hành động này như bốc hơi hoặc ngấm xuống đất. ( Nếu bạn có dòng chảy, bạn nên có ebb! )
FlowOut
cho biết nếu khối này được kết nối với một khối lập phương ở rìa thế giới. Khi một con đường đến rìa thế giới được tạo ra, nước có xu hướng chọn con đường đó hơn bất kỳ con đường nào khác.
Extra
là một chút thêm cho việc sử dụng trong tương lai.
Bây giờ chúng ta đã biết dữ liệu, hãy xem xét tổng quan cấp cao của thuật toán. Ý tưởng cơ bản của hệ thống là ưu tiên chảy xuống và ra ngoài. Như tôi giải thích trong video, tôi làm việc từ dưới lên. Mỗi lớp nước được xử lý một cấp tại một thời điểm trong trục y. Các khối cho mỗi cấp được xử lý ngẫu nhiên, mỗi khối sẽ cố gắng kéo nước từ nguồn của nó trên mỗi lần lặp.
Các khối dòng chảy kéo nước từ nguồn của chúng bằng cách theo hướng dòng chảy của chúng trở lại cho đến khi chúng đạt đến một khối nguồn hoặc một khối dòng chảy không có cha mẹ. Lưu trữ hướng dòng chảy trong mỗi khối làm cho việc đi theo đường dẫn đến nguồn dễ dàng như duyệt qua danh sách được liên kết.
Mã giả cho thuật toán như sau:
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()
Các quy tắc cơ bản để mở rộng luồng trong đó (được ưu tiên theo thứ tự):
- Nếu khối bên dưới có ít nước, chảy xuống
- Nếu khối lập phương liền kề trên cùng mức có ít nước hơn, hãy chảy bên.
- Nếu khối trên có ít nước VÀ khối nguồn cao hơn khối ở trên, hãy chảy lên.
Tôi biết, đó là mức khá cao. Nhưng thật khó để đi vào chi tiết hơn mà không đi vào chi tiết.
Hệ thống này hoạt động khá tốt. Tôi có thể dễ dàng lấp đầy các hố nước, tràn ra ngoài để tiếp tục ra ngoài. Tôi có thể lấp đầy các đường hầm hình chữ U như bạn thấy trong gif ở trên. Tuy nhiên, như tôi đã nói, hệ thống chưa hoàn thiện và tôi chưa làm việc hết. Tôi đã không làm việc trên hệ thống dòng chảy trong một thời gian dài (tôi quyết định rằng nó không cần thiết cho alpha và tôi đã giữ nó ở trạng thái chờ). Tuy nhiên, các vấn đề tôi đã giải quyết khi tôi giữ nó ở đâu:
Bể bơi . Khi nhận được một vũng nước lớn, các con trỏ từ con đến cha mẹ giống như một mớ hỗn độn điên rồ của bất kỳ khối lập phương ngẫu nhiên nào được chọn để chảy theo bất kỳ hướng nào. Giống như làm đầy một bồn tắm với chuỗi ngớ ngẩn. Khi bạn muốn tháo bồn, bạn có nên đi theo con đường của chuỗi ngớ ngẩn trở về nguồn của nó không? Hay bạn chỉ nên lấy bất cứ thứ gì gần nhất? Vì vậy, trong các tình huống mà các hình khối nằm trong một bể lớn, chúng có thể nên bỏ qua dòng chảy bố mẹ và kéo từ bất cứ thứ gì lên trên chúng. Tôi đã đưa ra một số mã làm việc cơ bản cho việc này, nhưng chưa bao giờ có một giải pháp tao nhã mà tôi có thể hài lòng.
Nhiều cha mẹ . Một luồng con có thể dễ dàng được nuôi bởi nhiều hơn một luồng cha. Nhưng đứa trẻ có một con trỏ đến một cha mẹ duy nhất sẽ không cho phép điều đó. Điều này có thể được khắc phục bằng cách sử dụng đủ bit để cho phép một bit cho mỗi hướng cha có thể. Và có khả năng thay đổi thuật toán để chọn ngẫu nhiên một đường dẫn trong trường hợp có nhiều cha mẹ. Nhưng, tôi chưa bao giờ đến đó để kiểm tra và xem những vấn đề khác có thể phơi bày.