Một cách để lưu trữ dữ liệu bản đồ 2D có khả năng vô hạn?


29

Tôi có một nền tảng 2D hiện có thể xử lý các khối với 100 x 100 ô, với các tọa độ khối được lưu trữ dưới dạng dài, vì vậy đây là giới hạn duy nhất của bản đồ (maxlong * maxlong). Tất cả các vị trí thực thể, vv vv đều có liên quan và do đó không có giới hạn ở đó.

Vấn đề tôi gặp phải là làm thế nào để lưu trữ và truy cập các khối này mà không có hàng ngàn tệp. Bạn có ý tưởng nào cho định dạng lưu trữ chi phí HD nhanh và thấp mà không cần phải mở mọi thứ cùng một lúc không?


2
Một số cấu trúc dữ liệu mà bạn có thể xem xét để có thêm cảm hứng là ma trận thưa thớtbảng trang (đa cấp) .
Andrew Russell

Mức độ ưu tiên thấp: Bạn có thể làm rõ nếu loại dữ liệu "dài" là 32 hoặc 64 bit?
Randolf Richardson

2
@Randolf cho rằng đây là C #, có lẽ anh ta có nghĩa là C # longlà 64 bit (vì vậy maxlong là Int64.MaxValue).
Andrew Russell

Notch có một số điều thú vị để nói về các bản đồ vô hạn trong Minecraft trong blog của anh ấy tại đây: notch.tumblr.com/post/3746989361/terrain-generation-part-1
dlras2

Câu trả lời:


17

Tạo một định dạng bản đồ tùy chỉnh cho trò chơi của bạn. Nó dễ hơn bạn nghĩ. Chỉ cần sử dụng lớp BinaryWriter. Đầu tiên viết tiêu đề trong một vài ints hoặc uint. Thông tin cần bao gồm trong tiêu đề:

  • Chuỗi ma thuật / số ma thuật của định dạng tệp của bạn.
  • Bắt đầu / kết thúc / kích thước của các đoạn được mô tả trong tệp này

và cũng (và đây là phần quan trọng về hiệu suất

  • ints mô tả vị trí bắt đầu bên trong tập tin. Vì vậy, bạn không phải tìm kiếm các khối cụ thể.

Với methode ở trên, bạn có thể (và nên) tạo một chỉ mục cho nội dung tệp của mình, chứa một số loại mô tả (tên do người dùng chỉ định cho vùng / chunk hoặc chỉ tọa độ) và làm giá trị thứ hai cho vị trí trong tệp.

Sau đó, khi bạn muốn tải một đoạn cụ thể, bạn sẽ chỉ phải tìm kiếm bên trong chỉ mục. Khi bạn có vị trí, chỉ cần đặt fileStream.Pocation = PositionOfChunkFrom Index và bạn có thể tải nó.

Đó là tất cả về thiết kế của tệp định dạng với tiêu đề mô tả nội dung của tệp một cách hiệu quả nhất.

Chỉ cần lưu các tệp với một phần mở rộng tùy chỉnh bạn tạo ra và bạn đi.

THƯỞNG: Thêm nén BZip2 vào các vùng cụ thể của tệp / toàn bộ nội dung (không phải tiêu đề !!), để bạn có thể giải nén các đoạn cụ thể khỏi tệp, cho dấu chân bộ nhớ rất nhỏ.


12
Thật đáng để chỉ ra rằng, nếu bạn đang sửa đổi tệp này một cách nhanh chóng, bạn sẽ muốn một tiêu đề / chỉ mục bên ngoài có kích thước cố định, để bạn có thể thêm các đoạn vào tệp mà không phải viết lại toàn bộ tập tin (do sự thay đổi bù đắp).
Andrew Russell

Tại thời điểm đó, không phải bạn chỉ đang thực hiện một cơ sở dữ liệu phẳng?
Ape-inago

13

Tôi gặp phải một vấn đề tương tự và quyết định tạo cấu trúc của riêng mình để xử lý dữ liệu. Nó dựa một cách lỏng lẻo trên một góc phần tư, nhưng có khả năng mở rộng vô hạn (ít nhất là lớn bằng Int) theo mọi hướng. Nó được thiết kế để xử lý dữ liệu dựa trên lưới được mở rộng từ một điểm trung tâm, giống như Minecraft hiện nay. Đó là không gian hiệu quả trong bộ nhớ, và rất nhanh.

Bạn có thể chỉ định cường độ tối thiểu cho mỗi nút (cường độ 7 sẽ là 128x128) và một khi bất kỳ nút nào có tỷ lệ phần trăm của các mã con được chỉ định, nó sẽ tự động làm phẳng thành một mảng hai chiều. Điều này có nghĩa là một phần dân cư rất dày đặc (ví dụ, một lục địa hoàn toàn được khám phá) sẽ có hiệu suất của một mảng (rất nhanh) nhưng một phần dân cư thưa thớt (ví dụ, một bờ biển ai đó lang thang lên xuống nhưng không khám phá trong đất liền) sẽ có hiệu suất tốt và sử dụng bộ nhớ thấp.

Mã của tôi có thể được tìm thấy ở đây . Mã này đã hoàn tất, đã được kiểm tra (đơn vị và kiểm tra tải) và khá tối ưu. Tuy nhiên, các hoạt động bên trong chưa được ghi chép quá rõ, nhưng tất cả các phương pháp công khai đều có thể sử dụng được. Nếu bất cứ ai quyết định dùng thử, hãy liên hệ với tôi với câu hỏi hoặc nhận xét.

Tôi chưa sử dụng nó để lưu trữ dữ liệu vào một tệp, nhưng đó là một vấn đề thú vị và tôi có thể giải quyết vấn đề đó tiếp theo.


Vì vậy, đây về cơ bản là một cây có thể mở rộng, phải không? Tôi đang thiếu gì?
kaoD

2
Cải tiến lớn nhất đối với một cây có thể mở rộng là nó 'làm phẳng' các nút nhất định của cây được dân cư đông đúc (mặc định 70%) thành các mảng 2D, thay vì giữ chúng có cấu trúc như cây. Điều này cung cấp cho bạn tốc độ của một tra cứu mảng, không có kích thước (vô hạn) của một mảng vô hạn.
dlras2

Cả lá và nút bên trong, phải không? Ý tưởng thú vị, có thể cho kết quả tốt, tôi sẽ thử nếu tôi cần. Btw, +1 để đưa ra mã và câu trả lời nhanh! Ồ, và thử nghiệm đơn vị cũng vậy, tôi (thật đáng buồn) không bao giờ làm điều đó trong các dự án cá nhân của mình :)
kaoD

Chúng tôi không thực hiện bất kỳ thử nghiệm đơn vị nào trong công việc của tôi, thật đáng buồn, đó là cách nổi loạn của tôi .. Tôi đã tạo ra một ứng dụng demo cho nó, cho thấy cách nó cư trú và làm phẳng, vì vậy nếu tôi có thể dọn sạch nó trong vài lần tiếp theo ngày để nó có thể trình bày, tôi cũng sẽ đăng nó ở đây. Nó có ý nghĩa hơn rất nhiều khi bạn nhìn thấy nó.
dlras2

1
Tôi đã đánh mất nó, xin lỗi! Tôi vẫn muốn dọn dẹp nó, nhưng tôi đang dần làm lại một số mã giữa lớp và bài tập về nhà, vì vậy sẽ không lâu nữa. Hiện tại, bản demo cũ, không đẹp đang ở đây: j.mp/qIwKYt Bởi không đẹp, tôi một phần có nghĩa là nó đòi hỏi rất nhiều lời giải thích, vì vậy đừng quên đọc README và thoải mái đặt câu hỏi tại đây hoặc qua email.
dlras2

3

Thay vào đó, bạn có thể sử dụng cơ sở dữ liệu - PostgreSQL có một số khả năng lập chỉ mục đặc biệt được tối ưu hóa cho loại dữ liệu này được đặt theo tọa độ X và Y. Bạn cũng có thể chỉ định rằng dữ liệu được trả về nằm trong một bán kính nhất định thay vì trong một khu vực hình vuông hoặc hình thuôn.

  PostgreSQL (miễn phí và mã nguồn mở)
  http://www.postgresql.org/

Ngoài ra còn có các cơ sở dữ liệu khác và đối với phía khách hàng, bạn có thể thấy một số loại nhất định phù hợp hơn với điều này vì chúng có thể chạy độc lập (do ứng dụng khách trò chơi của bạn khởi tạo) hoặc có thể được đưa vào như một phần của thư viện mã mà bạn có thể "chỉ sử dụng." Ưu điểm là bạn không phải thiết kế sơ đồ lập chỉ mục vì hầu hết các công cụ cơ sở dữ liệu SQL đã làm điều này khá tốt.

Một lợi thế với cách tiếp cận cơ sở dữ liệu là bạn có thể làm cho các khối của mình nhỏ hơn (hoặc loại bỏ hoàn toàn các khối và chỉ sử dụng gạch trực tiếp, nhưng việc sử dụng ít nhất các khối / nhóm nhiều gạch có thể hiệu quả hơn tùy thuộc vào thiết kế của bạn), và sau đó sử dụng truy vấn SQL để đưa vào một khu vực lớn hơn có thể xem được. Bằng cách tải trước để chồng lên các khu vực không thể xem gần đó, các ô có thể được chuẩn bị trước khi người chơi di chuyển nhân vật của họ, mang lại trải nghiệm chơi game tốt hơn (hy vọng mượt mà hơn).

Tôi đã nhận thấy rằng một số trò chơi giữ "bộ nhớ cache" dữ liệu bản đồ trên ổ cứng cục bộ sau khi có được nó lần đầu tiên (điều này chắc chắn là để giảm I / O mạng), chẳng hạn như Ashen Empires:

  Ashen Empires (chơi miễn phí, triển khai 2D đẹp mắt)
  http://www.ashenempires.com/

Theo dõi dấu thời gian "được cập nhật lần cuối" với mỗi khối / ô cũng sẽ hữu ích vì, khi có sẵn dữ liệu được lưu trữ cục bộ, truy vấn SQL có thể bao gồm một mệnh đề "WHERE timestamp_column> $ local_timestamp" để chỉ các khối / ô được cập nhật mới có được được tải xuống (hai lợi ích của việc tiết kiệm băng thông như thế này là chi phí kết nối thấp hơn và độ trễ cho người chơi của bạn ít hơn, điều này sẽ trở nên rõ ràng hơn khi trò chơi của bạn trở nên phổ biến).

Ảnh chụp màn hình từ Ashen Empires (một vài nhân vật đang ở ngân hàng địa phương, và bởi vẻ ngoài của những cục xương trên sàn, trông giống như một vài quái vật bộ xương phải lang thang và có khả năng bị tàn sát bởi những người bảo vệ của thị trấn địa phương):

nhập mô tả hình ảnh ở đây


2

Không lưu trữ và truy cập chúng, chỉ lưu trữ các hạt ngẫu nhiên cần thiết cũng như các thay đổi của người chơi trên bản đồ. Sau đó tạo các phần cần thiết vào thời gian chạy (chạy thuật toán tạo thế hệ của bạn, sau đó áp dụng các thay đổi của người chơi). Với quy trình tạo chính xác và nhất quán, bản đồ kết quả sẽ luôn giống nhau cho cùng một hạt giống bắt đầu.

Về mặt lý thuyết bạn có thể thực hiện bản đồ vô hạn theo nghĩa đen sẽ lưu vào một tệp rất nhỏ theo cách này.


@Josh Petrie cảm ơn vì những chỉnh sửa ngôn ngữ / phong cách quan trọng và tuyệt vời cho bài viết của tôi. đáng tiếc tôi không thể nâng cấp bản chỉnh sửa :-D
mã sh

1

Có cách nào bạn có thể phân vùng các khối (một số loại 'tiểu lục địa / quốc gia' trong thế giới của bạn) không? Vì vậy, có lẽ bạn có thể có một số loại tệp chỉ mục cho phép bạn nhanh chóng tìm thấy tệp phụ / phần nào của tệp lớn hơn mà bạn cần tải để có một đoạn trong bộ nhớ ...


luôn luôn có một cách bạn có thể phân vùng chunk. LUÔN LUÔN. cho dù người chơi có thể nhìn thấy / có liên quan đến phần còn lại của hệ thống hay không, luôn có cách phân vùng dữ liệu thế giới thành các khối, thường theo nhiều cách khác nhau.
mã sh

0

Bạn có thể lấy ý tưởng từ Minecraft. Ban đầu họ có một tập tin cho mỗi đoạn. Bây giờ họ sử dụng định dạng MCRegion, nhóm này chia thành các khu vực 32x32 và lưu trữ một trong những khu vực trên mỗi tệp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.