Bản đồ với 20 triệu ô khiến trò chơi hết bộ nhớ, làm cách nào để tránh?


11

Trong khi tải thêm các bản đồ khổng lồ, phương thức tải sẽ loại bỏ ngoại lệ bộ nhớ trong đó một phiên bản mới của ô bản đồ được tạo. Tôi muốn có toàn bộ bản đồ được xử lý ít nhất trên ứng dụng máy chủ (và trên máy khách nếu có thể). Tôi nên giải quyết vấn đề này như thế nào?

CẬP NHẬT: câu hỏi ở đây là làm thế nào để trò chơi ngừng bị sập khi vẫn còn bộ nhớ trống để sử dụng. Đối với việc chia bản đồ thành nhiều phần, đó là một cách tiếp cận tốt, nhưng không phải là điều tôi muốn trong trường hợp của mình.

CẬP NHẬT2: Lúc đầu, tôi đã đi và gán một kết cấu cho mọi phiên bản mới của lớp gạch và đó là thứ chiếm rất nhiều bộ nhớ (cũng là thời gian tải). Bây giờ nó mất khoảng bốn lần không gian ít hơn. Cảm ơn tất cả mọi người, bây giờ tôi có thể chạy các bản đồ khổng lồ mà không nghĩ đến việc phá vỡ chúng thành từng khối.

CẬP NHẬT 3: Sau khi viết lại mã để xem các mảng thuộc tính gạch hoạt động nhanh hơn hay tiêu thụ ít bộ nhớ hơn (so với các thuộc tính được nói trong các ô tương ứng của chúng làm thuộc tính đối tượng), tôi phát hiện ra rằng không chỉ tôi mất nhiều thời gian để thử nó, nó đã không mang lại bất kỳ cải thiện hiệu suất nào, và làm cho trò chơi khó gỡ lỗi khủng khiếp.


8
Chỉ tải các khu vực xung quanh người chơi.
MichaelHouse

4
20 triệu ô ở 4 byte cho mỗi ô chỉ khoảng 80 MB - vì vậy có vẻ như các ô của bạn không quá nhỏ. Chúng tôi không thể cung cấp cho bạn nhiều bộ nhớ hơn vì vậy chúng tôi phải tìm ra cách làm cho dữ liệu của bạn nhỏ hơn. Vì vậy, cho chúng tôi thấy những gì trong những viên gạch này.
Kylotan

Phương pháp tải làm gì? Mã bài, bạn có sử dụng đường ống Nội dung không? Nó tải kết cấu vào bộ nhớ hay chỉ siêu dữ liệu? Siêu dữ liệu gì? Các loại nội dung XNA thường tham chiếu các công cụ DirectX không được quản lý nên các đối tượng được quản lý rất nhỏ, nhưng ở phía không được quản lý, có thể có vô số nội dung bạn không thấy trừ khi bạn cũng đang chạy trình biên dịch DirectX. stackoverflow.com/questions/3050188/ Mạnh
Oskar Duveborn

2
Nếu hệ thống loại bỏ ngoại lệ bộ nhớ, thì sẽ không có đủ bộ nhớ trống để sử dụng, bất chấp những gì bạn có thể nghĩ. Sẽ không có dòng mã bí mật nào chúng tôi có thể cung cấp cho bạn để kích hoạt thêm bộ nhớ. Nội dung của từng ô và cách bạn phân bổ chúng rất quan trọng.
Kylotan

3
Điều gì khiến bạn nghĩ rằng bạn có bộ nhớ miễn phí? Bạn có đang chạy quy trình 32 bit bên trong HĐH 64 bit không?
Dalin Seivewright

Câu trả lời:


58

ứng dụng gặp sự cố khi đạt 1,5Gb.

Điều này cho thấy rằng bạn không thể hiện chính xác các ô của mình, vì điều này có nghĩa là mỗi ô có kích thước ~ 80 byte.

Điều bạn cần hiểu là cần có sự tách biệt giữa khái niệm trò chơi của một ô và hình ảnh trực quan mà người dùng nhìn thấy. Hai khái niệm này không giống nhau .

Lấy Terraria làm ví dụ. Thế giới Terraria nhỏ nhất chiếm 4200x1200, tức là 5 triệu gạch. Bây giờ, cần bao nhiêu bộ nhớ để đại diện cho thế giới đó?

Chà, mỗi viên gạch có một lớp tiền cảnh, một lớp nền (các bức tường nền), một "lớp dây" nơi dây dẫn đi và một "lớp đồ nội thất" nơi các vật dụng nội thất đi. Mỗi viên gạch chiếm bao nhiêu bộ nhớ? Một lần nữa, chúng ta chỉ nói về khái niệm, không trực quan.

Một gạch nền trước có thể dễ dàng được lưu trữ trong một dấu ngắn không dấu. Không có hơn 65536 loại gạch nền trước, vì vậy không có điểm nào sử dụng nhiều bộ nhớ hơn loại này. Các ô nền có thể dễ dàng ở dạng byte không dấu, vì có ít hơn 256 loại gạch nền khác nhau. Lớp dây hoàn toàn là nhị phân: hoặc một lát có một dây trong đó hoặc không. Vì vậy, đó là một bit trên mỗi lát. Và lớp đồ nội thất một lần nữa có thể là một byte không dấu, tùy thuộc vào số lượng đồ nội thất khác nhau có thể có.

Tổng kích thước bộ nhớ cho mỗi ô: 2 byte + 1 byte + 1 bit + 1 byte: 4 byte + 1 bit. Do đó, tổng kích thước cho bản đồ Terraria nhỏ là 20790000 byte, hoặc ~ 20MB. (lưu ý: các tính toán này dựa trên Terraria 1.1. Trò chơi đã mở rộng rất nhiều kể từ đó, nhưng ngay cả Terraria hiện đại cũng có thể phù hợp trong phạm vi 8 byte cho mỗi vị trí ô, hoặc ~ 40 MB. Vẫn có thể chấp nhận được).

Bạn không bao giờ nên có đại diện này được lưu trữ dưới dạng mảng của các lớp C #. Chúng nên là các mảng số nguyên hoặc một cái gì đó tương tự. AC # struct cũng sẽ hoạt động.

Bây giờ, khi đến lúc vẽ một phần của bản đồ (lưu ý sự nhấn mạnh), Terraria cần chuyển đổi các ô khái niệm này thành các ô thực tế . Mỗi ô cần thực sự chọn một hình ảnh nền trước, hình nền, hình ảnh đồ nội thất tùy chọn và có hình ảnh dây. Đây là nơi XNA xuất hiện với các bảng sprite khác nhau và như vậy.

Những gì bạn cần làm là chuyển đổi phần hiển thị của bản đồ khái niệm của bạn thành các ô xếp hình sprite XNA thực tế. Bạn không nên cố gắng chuyển đổi toàn bộ mọi thứ cùng một lúc . Mỗi ô bạn lưu trữ chỉ nên là một chỉ mục nói rằng "Tôi là loại X," trong đó X là số nguyên. Bạn sử dụng chỉ số nguyên đó để tìm nạp sprite nào bạn sử dụng để hiển thị nó. Và bạn sử dụng các tấm sprite của XNA để thực hiện việc này nhanh hơn là chỉ vẽ các hình tứ giác riêng lẻ.

Bây giờ, vùng gạch có thể nhìn thấy cần được chia thành nhiều phần khác nhau, để bạn không liên tục xây dựng các tấm sprite mỗi khi máy ảnh di chuyển. Vì vậy, bạn có thể có 64x64 khối của thế giới dưới dạng các tấm sprite. Bất kỳ khối 64x64 nào trên thế giới đều có thể nhìn thấy từ vị trí máy ảnh hiện tại của người chơi là các khối bạn vẽ. Bất kỳ khối nào khác thậm chí không có tấm sprite; nếu một đoạn rơi ra khỏi màn hình, bạn ném tờ đó ra (lưu ý: bạn không thực sự xóa nó; bạn giữ nó xung quanh và bảo vệ nó cho một đoạn mới có thể nhìn thấy sau này).

Tôi muốn có toàn bộ bản đồ được xử lý ít nhất trên ứng dụng máy chủ (và trên máy khách nếu có thể).

Máy chủ của bạn không cần biết hoặc quan tâm đến biểu diễn trực quan của gạch. Tất cả những gì nó cần quan tâm là đại diện khái niệm. Người dùng thêm một ô ở đây, vì vậy nó thay đổi chỉ mục ô đó.


4
+1 Câu trả lời xuất sắc. Cần được bình chọn nhiều hơn của tôi.
MichaelHouse

22

Chia địa hình thành các khu vực hoặc khối. Sau đó, chỉ tải các phần mà người chơi có thể nhìn thấy và dỡ bỏ những phần không có. Bạn có thể nghĩ về nó giống như một băng chuyền, nơi bạn đang tải các đoạn ở một đầu và dỡ chúng ở đầu kia khi người chơi di chuyển dọc. Luôn đi trước người chơi.

Bạn cũng có thể sử dụng các thủ thuật như inst instaging. Trường hợp nếu tất cả các ô của một loại chỉ có một thể hiện trong bộ nhớ và được vẽ nhiều lần ở tất cả các vị trí cần thiết.

Bạn có thể tìm thêm ý tưởng như thế này ở đây:

Làm thế nào họ làm điều đó: Hàng triệu viên gạch ở Terraria

'Khoanh vùng' lên các khu vực trên bản đồ gạch lớn, cũng như ngục tối


Vấn đề là cách tiếp cận như vậy là tốt cho ứng dụng khách, nhưng không tốt cho máy chủ. Khi một số ô trên thế giới tắt và một phản ứng dây chuyền bắt đầu (và điều đó xảy ra rất nhiều), toàn bộ bản đồ sẽ nằm trong bộ nhớ cho điều đó và đó là một vấn đề khi nó bị ném ra khỏi bộ nhớ.
dùng1306322

6
Mỗi viên gạch chứa gì? Bao nhiêu bộ nhớ được sử dụng cho mỗi ô? Ngay cả ở mức 10 byte, mỗi bạn chỉ ở mức 190 megabyte. Máy chủ không nên tải kết cấu hoặc bất kỳ thông tin nào khác không cần thiết. Nếu bạn cần một mô phỏng cho 20 triệu ô, bạn cần loại bỏ càng nhiều càng tốt khỏi các ô đó để tạo thành một bộ mô phỏng chỉ có thông tin cần thiết cho các phản ứng dây chuyền của bạn.
MichaelHouse

5

Câu trả lời của Byte56 là tốt, và tôi đã bắt đầu viết bài này như một bình luận cho điều đó nhưng nó quá dài và chứa một số lời khuyên có thể hữu ích hơn trong một câu trả lời.

Cách tiếp cận hoàn toàn tốt cho máy chủ cũng như cho máy khách. Trong thực tế, nó có lẽ phù hợp hơn , với điều kiện là máy chủ sẽ được yêu cầu quan tâm đến nhiều lĩnh vực hơn so với khách hàng. Máy chủ có một ý tưởng khác về những gì cần được tải so với máy khách (nó quan tâm đến tất cả các khu vực mà tất cả các máy khách được kết nối quan tâm), nhưng nó có khả năng quản lý một tập hợp các vùng làm việc.

Ngay cả khi bạn có thể phù hợp với một bản đồ khổng lồ như vậy (và rất có thể bạn không thể) trong bộ nhớ, bạn không muốn . Hoàn toàn không có điểm nào trong việc tải dữ liệu mà bạn không phải sử dụng ngay lập tức, nó không hiệu quả và chậm. Ngay cả khi bạn có thể điều chỉnh nó trong bộ nhớ, rất có thể bạn sẽ không thể xử lý tất cả trong bất kỳ khoảng thời gian hợp lý nào.

Tôi nghi ngờ sự phản đối của bạn về việc một số khu vực nhất định không được tải là bởi vì bản cập nhật thế giới của bạn đang lặp đi lặp lại trên tất cả các ô và xử lý chúng? Điều đó chắc chắn hợp lệ, nhưng nó không loại trừ chỉ có một số phần nhất định được tải. Thay vào đó, khi một khu vực được tải, áp dụng bất kỳ bản cập nhật nào bị bỏ lỡ, để cập nhật nó. Đó cũng là cách xử lý hiệu quả hơn rất nhiều (tập trung một nỗ lực lớn vào một vùng nhớ nhỏ). Nếu có bất kỳ hiệu ứng xử lý nào có thể vượt qua ranh giới khu vực (ví dụ như dòng dung nham hoặc dòng nước sẽ chảy qua khu vực khác), thì liên kết hai khu vực đó lại với nhau để khi một khu vực được tải, thì khu vực kia cũng vậy. Lý tưởng nhất là những tình huống đó sẽ được giảm thiểu, nếu không, bạn sẽ nhanh chóng quay lại trường hợp 'tất cả các khu vực phải được tải mọi lúc'.


3

Một cách hiệu quả mà tôi đã sử dụng trong trò chơi XNA là:

  • sử dụng spritesheets, không phải hình ảnh cho mỗi ô / đối tượng và chỉ cần tải những gì bạn cần trên bản đồ đó;
  • chia bản đồ thành các phần nhỏ hơn, giả sử bản đồ chunk 500 x 200 nếu bản đồ của bạn có kích thước gấp 4 lần kích thước này, hoặc thứ gì đó bạn có thể haldle;
  • tải đoạn này vào bộ nhớ và chỉ vẽ các phần có thể nhìn thấy trên bản đồ (giả sử, các ô hiển thị cộng với 1 ô cho mỗi hướng người chơi đang di chuyển) trong bộ đệm ngoài màn hình;
  • xóa khung nhìn và sao chép các pixel từ bộ đệm qua (cái này được gọi là bộ đệm đôi và làm mịn chuyển động);
  • khi bạn di chuyển, theo dõi các bản đồ của bản đồ và tải đoạn tiếp theo khi nó đến gần nó và nối với bản đồ hiện tại;
  • một khi người chơi có toàn bộ trong bản đồ mới này, bạn có thể dỡ cái cuối cùng.

Bạn cũng có thể sử dụng một bản đồ lớn theo cách này nếu nó không lớn và sử dụng logic được trình bày.

Dĩ nhiên kích thước của kết cấu của bạn phải được cân bằng và độ phân giải phải trả giá rất cao trong việc này. Hãy thử làm việc ở độ phân giải thấp hơn và, nếu bạn cần, hãy tạo gói độ phân giải cao để tải nếu cài đặt cấu hình được đặt thành.

Bạn sẽ phải lặp lại chỉ các phần có liên quan của bản đồ này mỗi lần và chỉ hiển thị những gì cần thiết. Bằng cách này bạn sẽ cải thiện hiệu suất rất nhiều.

Một máy chủ có thể xử lý một bản đồ khổng lồ nhanh hơn vì nó không phải kết xuất (một trong những hoạt động chậm nhất) trò chơi, vì vậy logic cho nó có thể là việc sử dụng một khối lớn hơn hoặc thậm chí toàn bộ bản đồ, nhưng chỉ xử lý logic xung quanh người chơi, như trong 2 lần khung nhìn của người chơi khu vực xung quanh người chơi.

Một lời khuyên ở đây là tôi không phải là một chuyên gia trong trò chơi dev và trò chơi của tôi được tạo ra cho dự án cuối cùng tốt nghiệp của tôi (mà tôi có điểm cao nhất), vì vậy tôi có thể không đúng ở mọi điểm, nhưng tôi đã nghiên cứu trang web và các trang web như http://www.gamasutra.com và người tạo trang web XNA creators.xna.com (tại thời điểm này http://create.msdn.com ) để thu thập kiến ​​thức và kỹ năng và nó hoạt động tốt với tôi .


2

Hoặc

  • mua thêm bộ nhớ
  • biểu diễn cấu trúc dữ liệu của bạn gọn hơn
  • không giữ tất cả dữ liệu trong bộ nhớ cùng một lúc.

Tôi có ram 8 Gb trên win7 64 bit và ứng dụng gặp sự cố khi đạt 1,5Gb. Vì vậy, thực sự không có điểm nào để mua thêm ram trừ khi tôi thiếu thứ gì đó.
dùng1306322

1
@ user1306322: Nếu bạn có 20 triệu ô xếp và chiếm khoảng ~ 1,5 GB bộ nhớ, điều đó có nghĩa là các ô của bạn xấp xỉ 80 byte cho mỗi ô . Chính xác những gì bạn đang lưu trữ trong những thứ này?
Nicol Bolas

@NicolBolas như tôi đã nói, một vài int, byte và texture2d. Ngoài ra, tất cả họ không mất 1,5 GB, ứng dụng gặp sự cố khi đạt đến chi phí bộ nhớ này.
dùng1306322

2
@ user1306322: Điều đó vẫn còn nhiều hơn mức cần thiết. Làm thế nào lớn là một texture2d? Có phải mỗi ô có một cái duy nhất, hoặc chúng chia sẻ kết cấu? Ngoài ra, nơi có bạn nói điều này? Bạn chắc chắn đã không đặt nó trong câu hỏi của bạn, đó là nơi thông tin nên được. Giải thích hoàn cảnh cụ thể của bạn tốt hơn, xin vui lòng.
Nicol Bolas

@ user1306322: Thành thật mà nói, tôi không hiểu tại sao câu trả lời của tôi bị hạ thấp. Tôi đã liệt kê ba biện pháp khắc phục tình trạng hết bộ nhớ - tất nhiên, điều đó không có nghĩa là tất cả chúng đều nhất thiết phải áp dụng. Nhưng tôi vẫn nghĩ họ đúng. Tôi có lẽ nên viết "mở rộng bộ nhớ của bạn" thay vì "mua" để bao gồm cả trường hợp máy ảo. Tôi có bị hạ thấp vì câu trả lời của tôi ngắn gọn thay vì dài dòng không? Tôi nghĩ rằng điểm của họ đủ thẳng để được hiểu mà không cần giải thích thêm?!
Thomas

1

Câu hỏi ở đây là làm thế nào để trò chơi ngừng bị sập khi vẫn còn bộ nhớ trống để sử dụng. Đối với việc chia bản đồ thành nhiều phần, đó là một cách tiếp cận tốt, nhưng không phải là điều tôi muốn trong trường hợp của mình.

Trả lời cập nhật của bạn, bạn không thể phân bổ các mảng theo Int32.MaxValuekích thước. Bạn phải chia nó thành khối. Bạn thậm chí có thể gói nó trong một lớp bao bọc để lộ mặt tiền giống như mảng:

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.