Làm cách nào tôi có thể tối ưu hóa một thế giới voxel Minecraft-esque?


76

Tôi thấy các thế giới rộng lớn kỳ diệu của Minecraft rất chậm để điều hướng, ngay cả với lõi đồ họa lõi tứ và đồ họa.

Tôi cho rằng sự chậm chạp của Minecraft đến từ:

  • Java, vì phân vùng không gian và quản lý bộ nhớ nhanh hơn trong C ++ bản địa.
  • Phân vùng thế giới yếu.

Tôi có thể sai trên cả hai giả định. Tuy nhiên, điều này khiến tôi suy nghĩ về cách tốt nhất để quản lý các thế giới voxel lớn. Vì nó là một thế giới 3D thật sự, nơi một khối có thể tồn tại trong bất kỳ phần nào của thế giới, nó cơ bản là một mảng lớn 3D [x][y][z], trong đó mỗi khối trên thế giới có một loại (ví dụ BlockType.Empty = 0, BlockType.Dirt = 1vv)

Tôi cho rằng để làm cho loại thế giới này hoạt động tốt, bạn sẽ cần phải:

  • Sử dụng một cây của một số loại ( oct / kd / bsp ) để tách tất cả các hình khối ra; có vẻ như một oct / kd sẽ là lựa chọn tốt hơn, vì bạn chỉ có thể phân vùng trên một cấp độ hình khối chứ không phải mỗi cấp độ tam giác.
  • Sử dụng một số thuật toán để tìm ra các khối hiện có thể nhìn thấy, vì các khối gần hơn với người dùng có thể làm xáo trộn các khối phía sau, khiến cho việc hiển thị chúng trở nên vô nghĩa.
  • Giữ cho đối tượng khối tự nhẹ, vì vậy thật nhanh chóng để thêm và loại bỏ chúng khỏi cây.

Tôi đoán không có câu trả lời đúng cho vấn đề này, nhưng tôi sẽ thấy thú vị khi xem ý kiến ​​của mọi người về chủ đề này. Làm thế nào bạn sẽ cải thiện hiệu suất trong một thế giới dựa trên voxel lớn?



2
Vì vậy, những gì bạn thực sự yêu cầu? Bạn đang yêu cầu cách tiếp cận tốt để quản lý thế giới rộng lớn, hoặc phản hồi về cách tiếp cận cụ thể của bạn, hoặc ý kiến ​​về chủ đề quản lý thế giới rộng lớn?
doppelgreener

1
Những thứ tốt cho đến nay, nó là về cách tiếp cận phổ biến nhất đối với những thứ này. Tôi không đặc biệt sau phản hồi về cách tiếp cận của tôi vì tất cả những gì tôi đã đề xuất là những gì tôi mong đợi một cách hợp lý sẽ xảy ra. Tôi chỉ muốn biết thêm thông tin về chủ đề này thực sự và không có quá nhiều thứ xuất hiện sau một vài tìm kiếm. Tôi đoán câu hỏi của tôi không chỉ là về hiệu suất kết xuất mà còn về cách quản lý một khối lượng dữ liệu lớn như vậy ... chẳng hạn như phân chia các khu vực, v.v.
Một sốXXChump

2
Vì vậy, hãy rõ ràng và thêm một câu hỏi vào bài viết của bạn để chúng tôi biết câu hỏi nào chúng tôi đang trả lời. ;)
doppelgreener

3
Bạn có ý nghĩa gì bởi "cực kỳ chậm để điều hướng"? Chắc chắn có một số chậm khi trò chơi tạo địa hình mới, nhưng sau đó, minecraft có xu hướng xử lý địa hình khá tốt.
thedaian

Câu trả lời:


106

Đá động cơ Voxel

Cỏ động cơ Voxel

Liên quan đến Java vs C ++, tôi đã viết một công cụ voxel ở cả hai (phiên bản C ++ được hiển thị ở trên). Tôi cũng đã viết động cơ voxel từ năm 2004 (khi chúng không thịnh hành). :) Tôi có thể nói với một chút do dự rằng hiệu năng C ++ vượt trội hơn nhiều (nhưng nó cũng khó mã hóa hơn). Nó ít hơn về tốc độ tính toán, và nhiều hơn về quản lý bộ nhớ. Thật tuyệt vời, khi bạn đang phân bổ / xử lý nhiều dữ liệu như những gì trong thế giới voxel, C (++) là ngôn ngữ để đánh bại. Tuy nhiên, bạn nên nghĩ về mục tiêu của mình. Nếu hiệu suất là ưu tiên cao nhất của bạn, hãy đi với C ++. Nếu bạn chỉ muốn viết một trò chơi mà không có hiệu năng vượt trội, Java hoàn toàn có thể chấp nhận được (bằng chứng là Minecraft). Có nhiều trường hợp tầm thường / cạnh, nhưng nói chung, bạn có thể mong đợi Java chạy chậm hơn khoảng 1,75-2,0 lần so với (được viết tốt) C ++. Bạn có thể thấy một phiên bản cũ hơn, tối ưu hóa cho động cơ của tôi đang hoạt động ở đây (EDIT: phiên bản mới hơn tại đây ). Mặc dù thế hệ chunk có vẻ chậm, nhưng hãy nhớ rằng nó đang tạo ra các sơ đồ voronoi 3D, tính toán các quy tắc bề mặt, ánh sáng, AO và bóng trên CPU bằng các phương pháp vũ phu. Tôi đã thử các kỹ thuật khác nhau và tôi có thể có được thế hệ chunk nhanh hơn khoảng 100 lần bằng cách sử dụng các kỹ thuật lưu trữ và lưu trữ khác nhau.

Để trả lời phần còn lại của câu hỏi của bạn, có rất nhiều điều bạn có thể làm để cải thiện hiệu suất.

  1. Bộ nhớ đệm. Bất cứ nơi nào bạn có thể, bạn nên tính toán dữ liệu một lần. Ví dụ, tôi nướng ánh sáng vào khung cảnh. Nó có thể sử dụng ánh sáng động (trong không gian màn hình, như một quá trình hậu kỳ), nhưng nướng trong ánh sáng có nghĩa là tôi không phải vượt qua các quy tắc cho các hình tam giác, có nghĩa là ....
  2. Truyền càng ít dữ liệu vào card màn hình càng tốt. Một điều mọi người có xu hướng quên là bạn truyền càng nhiều dữ liệu vào GPU thì càng mất nhiều thời gian. Tôi vượt qua trong một màu duy nhất và một vị trí đỉnh. Nếu tôi muốn thực hiện chu kỳ ngày / đêm, tôi chỉ có thể thực hiện phân loại màu hoặc tôi có thể tính toán lại cảnh khi mặt trời dần thay đổi.

  3. Vì việc truyền dữ liệu tới GPU rất tốn kém, có thể viết một công cụ trong phần mềm nhanh hơn ở một số khía cạnh. Ưu điểm của phần mềm là nó có thể thực hiện tất cả các loại thao tác dữ liệu / truy cập bộ nhớ mà đơn giản là không thể có trên GPU.

  4. Chơi với kích thước lô. Nếu bạn đang sử dụng GPU, hiệu suất có thể thay đổi đáng kể dựa trên mức độ lớn của từng mảng đỉnh bạn vượt qua. Theo đó, chơi xung quanh với kích thước của các khối (nếu bạn sử dụng khối). Tôi đã thấy rằng các khối 64x64x64 hoạt động khá tốt. Không có vấn đề gì, giữ cho khối của bạn khối (không có lăng kính hình chữ nhật). Điều này sẽ làm cho mã hóa và các hoạt động khác nhau (như biến đổi) dễ dàng hơn, và trong một số trường hợp, hiệu suất cao hơn. Nếu bạn chỉ lưu trữ một giá trị cho độ dài của mọi thứ nguyên, hãy nhớ rằng hai thanh ghi ít bị tráo đổi trong quá trình tính toán.

  5. Xem xét danh sách hiển thị (đối với OpenGL). Mặc dù chúng là cách "cũ", chúng có thể nhanh hơn. Bạn phải nướng một danh sách hiển thị thành một biến ... nếu bạn gọi các hoạt động tạo danh sách hiển thị trong thời gian thực, nó sẽ chậm một cách vô duyên. Làm thế nào là một danh sách hiển thị nhanh hơn? Nó chỉ cập nhật trạng thái, so với các thuộc tính trên mỗi đỉnh. Điều này có nghĩa là tôi có thể vượt qua tối đa sáu mặt, sau đó một màu (so với một màu cho mỗi đỉnh của voxel). Nếu bạn đang sử dụng GL_QUADS và voxel khối, điều này có thể tiết kiệm tới 20 byte (160 bit) cho mỗi voxel! (15 byte không có alpha, mặc dù thông thường bạn muốn giữ mọi thứ được căn chỉnh 4 byte.)

  6. Tôi sử dụng phương pháp brute-force để hiển thị "khối" hoặc các trang dữ liệu, đây là một kỹ thuật phổ biến. Không giống như octrees, việc đọc / xử lý dữ liệu dễ dàng hơn / nhanh hơn nhiều, mặc dù ít thân thiện với bộ nhớ hơn (tuy nhiên, ngày nay bạn có thể nhận được 64 gigabyte bộ nhớ với giá 200 - 300 đô la) ... không phải người dùng trung bình có điều đó. Rõ ràng, bạn không thể phân bổ một mảng lớn cho toàn thế giới (một bộ voxels 1024x1024x1024 là 4 gigabyte bộ nhớ, giả sử int 32 bit được sử dụng cho mỗi voxel). Vì vậy, bạn phân bổ / dealloc nhiều mảng nhỏ, dựa trên sự gần gũi của chúng với người xem. Bạn cũng có thể phân bổ dữ liệu, lấy danh sách hiển thị cần thiết, sau đó kết xuất dữ liệu để tiết kiệm bộ nhớ. Tôi nghĩ rằng sự kết hợp lý tưởng có thể là sử dụng cách tiếp cận hỗn hợp giữa octrees và mảng - lưu trữ dữ liệu trong một mảng khi thực hiện quá trình tạo thế giới, chiếu sáng, v.v.

  7. Kết xuất gần đến xa ... một pixel bị cắt là tiết kiệm thời gian. Gpu sẽ ném một pixel nếu nó không vượt qua bài kiểm tra bộ đệm sâu.

  8. Chỉ hiển thị các đoạn / trang trong chế độ xem (tự giải thích). Ngay cả khi gpu biết cách cắt các polgyons bên ngoài khung nhìn, việc truyền dữ liệu này vẫn mất thời gian. Tôi không biết cấu trúc hiệu quả nhất cho việc này sẽ là gì ("đáng xấu hổ", tôi chưa bao giờ viết cây BSP), nhưng ngay cả một chương trình phát sóng đơn giản trên cơ sở từng đoạn có thể cải thiện hiệu suất và rõ ràng việc kiểm tra chống lại sự thất vọng khi xem tiết kiệm thời gian.

  9. Thông tin rõ ràng, nhưng đối với người mới: xóa mọi đa giác không có trên bề mặt - tức là nếu một voxel bao gồm sáu mặt, hãy xóa các mặt không bao giờ được hiển thị (đang chạm vào một voxel khác).

  10. Như một quy tắc chung của tất cả mọi thứ bạn làm trong lập trình: CACHE ĐỊA PHƯƠNG! Nếu bạn có thể giữ mọi thứ trong bộ nhớ cache (ngay cả trong một khoảng thời gian nhỏ, nó sẽ tạo ra sự khác biệt lớn. Điều này có nghĩa là giữ cho dữ liệu của bạn đồng nhất (trong cùng một vùng bộ nhớ) và không chuyển đổi các vùng bộ nhớ để xử lý quá thường xuyên. , lý tưởng, làm việc trên một khối trên mỗi luồng và giữ bộ nhớ đó dành riêng cho luồng. Điều này không chỉ áp dụng cho bộ đệm CPU. Hãy nghĩ về hệ thống phân cấp bộ đệm như thế này (chậm nhất đến nhanh nhất): mạng (đám mây / cơ sở dữ liệu / v.v.) -> ổ cứng (có ổ SSD nếu bạn chưa có), ram (lấy kênh gấp ba hoặc RAM lớn hơn nếu bạn chưa có), bộ đệm CPU, đăng ký. Hãy cố gắng giữ dữ liệu của bạn kết thúc sau, và không trao đổi nó nhiều hơn bạn phải.

  11. Luồng. Làm đi. Thế giới Voxel rất phù hợp để phân luồng, vì mỗi phần có thể được tính toán (phần lớn) độc lập với các phần khác ... Tôi đã thấy một sự cải tiến gần gấp 4 lần (trên Core i7 4 lõi, trong thế hệ thủ tục khi tôi viết thói quen cho luồng.

  12. Không sử dụng các kiểu dữ liệu char / byte. Hoặc quần short. Người tiêu dùng trung bình của bạn sẽ có bộ xử lý AMD hoặc Intel hiện đại (có thể như bạn). Các bộ xử lý này không có thanh ghi 8 bit. Họ tính toán byte bằng cách đặt chúng vào khe 32 bit, sau đó chuyển đổi chúng trở lại (có thể) trong bộ nhớ. Trình biên dịch của bạn có thể thực hiện tất cả các loại voodoo, nhưng sử dụng số 32 hoặc 64 bit sẽ mang lại cho bạn kết quả dễ đoán nhất (và nhanh nhất). Tương tự, giá trị "bool" không mất 1 bit; trình biên dịch thường sẽ sử dụng 32 bit đầy đủ cho một bool. Nó có thể hấp dẫn để thực hiện một số loại nén trên dữ liệu của bạn. Ví dụ: bạn có thể lưu trữ 8 voxels dưới dạng một số (2 ^ 8 = 256 kết hợp) nếu tất cả chúng đều cùng loại / màu. Tuy nhiên, bạn phải suy nghĩ về sự phân nhánh của việc này - nó có thể tiết kiệm rất nhiều bộ nhớ, nhưng nó cũng có thể cản trở hiệu suất, ngay cả với thời gian giải nén nhỏ, bởi vì ngay cả lượng nhỏ thời gian thêm đó cũng cân đối với kích thước của thế giới của bạn. Hãy tưởng tượng tính toán một raycast; đối với mỗi bước của raycast, bạn sẽ phải chạy thuật toán giải nén (trừ khi bạn đưa ra một cách thông minh để khái quát hóa phép tính cho 8 voxels trong một bước tia).

  13. Như Jose Chavez đã đề cập, mẫu thiết kế fly trọng có thể hữu ích. Giống như bạn sẽ sử dụng một bitmap để thể hiện một ô trong trò chơi 2D, bạn có thể xây dựng thế giới của mình từ một số loại hình xếp (hoặc khối) 3D. Nhược điểm của điều này là sự lặp lại của kết cấu, nhưng bạn có thể cải thiện điều này bằng cách sử dụng kết cấu phương sai phù hợp với nhau. Như một quy tắc tự nhiên, bạn muốn sử dụng sự vận động bất cứ nơi nào bạn có thể.

  14. Tránh xử lý đỉnh và pixel trong shader khi xuất hình học. Trong một công cụ voxel chắc chắn bạn sẽ có nhiều hình tam giác, do đó, ngay cả một trình đổ bóng pixel đơn giản cũng có thể giảm đáng kể thời gian kết xuất của bạn. Tốt hơn là kết xuất vào bộ đệm, sau đó bạn tạo pixel shader làm hậu xử lý. Nếu bạn không thể làm điều đó, hãy thử thực hiện các phép tính trong trình tạo bóng đỉnh của bạn. Các tính toán khác nên được đưa vào dữ liệu đỉnh nếu có thể. Các đường chuyền bổ sung trở nên rất tốn kém nếu bạn phải kết xuất lại tất cả các hình dạng (như ánh xạ bóng hoặc ánh xạ môi trường). Đôi khi tốt hơn là từ bỏ một cảnh năng động để ủng hộ các chi tiết phong phú hơn. Nếu trò chơi của bạn có các cảnh có thể sửa đổi (tức là địa hình có thể phá hủy), bạn luôn có thể tính toán lại cảnh đó khi mọi thứ bị phá hủy. Việc biên dịch lại không tốn kém và sẽ mất dưới một giây.

  15. Hủy bỏ các vòng lặp của bạn và giữ cho mảng phẳng! Đừng làm điều này:

    for (i = 0; i < chunkLength; i++) {
     for (j = 0; j < chunkLength; j++) {
      for (k = 0; k < chunkLength; k++) {
       MyData[i][j][k] = newVal;
      }
     }
    }
    //Instead, do this:
    for (i = 0; i < chunkLengthCubed; i++) {
     //figure out x, y, z index of chunk using modulus and div operators on i
     //myData should have chunkLengthCubed number of indices, obviously
     myData[i] = newVal;
    }
    

    EDIT: Thông qua thử nghiệm rộng rãi hơn, tôi đã thấy điều này có thể sai. Sử dụng trường hợp hoạt động tốt nhất cho kịch bản của bạn. Nói chung, các mảng phải bằng phẳng, nhưng sử dụng các vòng lặp đa chỉ số thường có thể nhanh hơn tùy theo trường hợp

EDIT 2: khi sử dụng các vòng lặp đa chỉ mục, tốt nhất là lặp theo thứ tự z, y, x thay vì ngược lại. Trình biên dịch của bạn có thể tối ưu hóa điều này, nhưng tôi sẽ ngạc nhiên nếu có. Điều này tối đa hóa hiệu quả trong việc truy cập bộ nhớ và địa phương.

for (k < 0; k < volumePitch; k++) {
    for (j = 0; j < volumePitch; j++) {
        for (i = 0; i < volumePitch; i++) {
            myIndex = k*volumePitch*volumePitch + j*volumePitch + i;
        }
    }
}
  1. Đôi khi bạn phải đưa ra các giả định, khái quát hóa và hy sinh. Điều tốt nhất bạn có thể làm là giả định rằng hầu hết thế giới của bạn hoàn toàn tĩnh và chỉ thay đổi mỗi vài nghìn khung hình. Đối với các phần hoạt hình của thế giới, những phần đó có thể được thực hiện trong một đường chuyền riêng. Cũng cho rằng hầu hết thế giới của bạn là hoàn toàn mờ đục. Các đối tượng trong suốt có thể được hiển thị trong một vượt qua riêng biệt. Giả sử rằng kết cấu chỉ thay đổi mỗi đơn vị x hoặc các đối tượng chỉ có thể được đặt theo gia số x. Giả sử kích thước thế giới cố định ... hấp dẫn như một thế giới vô tận, nó có thể dẫn đến các yêu cầu hệ thống không thể đoán trước. Ví dụ, để đơn giản hóa việc tạo mẫu voronoi trong các tảng đá ở trên, tôi giả định rằng mọi điểm trung tâm voronoi đều nằm trong một lưới đồng nhất, với một độ lệch nhẹ (nói cách khác, ngụ ý băm hình học). Giả sử một thế giới không bao bọc (có các cạnh). Điều này có thể đơn giản hóa nhiều phức tạp được giới thiệu bởi một hệ thống tọa độ gói, với chi phí tối thiểu cho trải nghiệm người dùng.

Bạn có thể đọc thêm về việc triển khai của tôi tại trang web của tôi


9
+1. Liên lạc tốt đẹp bao gồm cả hình ảnh ở đầu như một động lực để đọc bài luận. Bây giờ tôi đã đọc bài luận tôi có thể nói rằng họ không cần thiết và nó đáng giá. ;)
George Duckett

Cảm ơn - một bức tranh đáng giá ngàn lời nói, như họ nói. :) Ngoài việc làm cho bức tường văn bản của tôi bớt đáng sợ, tôi muốn cung cấp cho độc giả một ý tưởng về việc có bao nhiêu voxels có thể hiển thị ở mức hợp lý bằng cách sử dụng các kỹ thuật được mô tả.
Gavan Woolery

14
Tôi vẫn muốn SE sẽ cho phép câu trả lời cụ thể yêu thích.
joltmode

2
@PatrickMoriarty # 15 là một mẹo khá phổ biến. Giả sử rằng trình biên dịch của bạn không thực hiện tối ưu hóa này (nó có thể hủy bỏ vòng lặp của bạn, nhưng nó có thể sẽ không nén một mảng nhiều chiều). Bạn muốn giữ tất cả dữ liệu của mình trong cùng một không gian bộ nhớ liền kề để lưu vào bộ đệm. Một mảng nhiều chiều có thể (có khả năng) được phân bổ trên nhiều không gian, vì nó là một mảng các con trỏ. Đối với việc bỏ kiểm soát vòng lặp, hãy nghĩ về mã được biên dịch trông như thế nào. Để đăng ký ít nhất và hoán đổi bộ đệm, bạn muốn tạo ra các vars / hướng dẫn ít nhất. Mà bạn nghĩ biên dịch thành nhiều hơn?
Gavan Woolery

2
Mặc dù một số điểm ở đây là tốt, đặc biệt là liên quan đến bộ nhớ đệm, luồng và giảm thiểu chuyển GPU, một số điểm không chính xác khủng khiếp. 5: LUÔN LUÔN sử dụng VBO / VAO thay vì danh sách hiển thị. 6: Nhiều RAM hơn chỉ cần thêm băng thông. Với dẫn đến 12: Ngược lại chính xác là đúng với bộ nhớ hiện đại, với mỗi byte được lưu sẽ tăng cơ hội phù hợp với nhiều dữ liệu hơn vào bộ đệm. 14: Trong Minecraft, có nhiều đỉnh hơn pixel (tất cả các khối ở xa), vì vậy hãy di chuyển tính toán sang trình đổ bóng pixel, không phải TỪ nó, tốt nhất là với bóng mờ bị trì hoãn.

7

Có rất nhiều thứ mà Minecraft có thể làm hiệu quả hơn. Ví dụ, Minecraft tải toàn bộ các cột dọc có kích thước khoảng 16x16 và hiển thị chúng. Tôi cảm thấy rằng việc gửi và kết xuất nhiều gạch không cần thiết là rất không hiệu quả. Nhưng tôi không cảm thấy sự lựa chọn ngôn ngữ là một điều quan trọng.

Java có thể khá nhanh nhưng đối với một cái gì đó theo định hướng dữ liệu này, C ++ có một lợi thế lớn với chi phí thấp hơn đáng kể để truy cập các mảng và làm việc trong các byte. Mặt khác, việc thực hiện luồng trên tất cả các nền tảng trong Java sẽ dễ dàng hơn nhiều. Trừ khi bạn có kế hoạch sử dụng OpenMP hoặc OpenCL, bạn sẽ không tìm thấy sự tiện lợi đó trong C ++.

Hệ thống lý tưởng của tôi sẽ là một hệ thống phân cấp phức tạp hơn một chút.

Ngói là một đơn vị, có khả năng khoảng 4 byte để giữ thông tin như loại vật liệu và ánh sáng.

Phân khúc sẽ là một khối gạch 32x32x32.

  1. Cờ sẽ được đặt cho mỗi sáu bên, nếu toàn bộ bên đó là các khối vững chắc. Điều đó sẽ cho phép trình kết xuất chặn các phân đoạn phía sau phân khúc đó. Minecraft hiện không xuất hiện để thực hiện kiểm tra tắc. Nhưng đã có đề cập đến việc loại bỏ tắc nghẽn phần cứng có sẵn có thể tốn kém nhưng tốt hơn so với việc hiển thị số lượng lớn đa giác trên thẻ cấp thấp.
  2. Các phân đoạn sẽ chỉ được tải vào bộ nhớ trong khi hoạt động (người chơi, NPC, vật lý nước, sinh trưởng của cây, vân vân). Nếu không, chúng sẽ được gửi trực tiếp vẫn được nén từ đĩa cho khách hàng.

Các ngành sẽ là một khối 16x16x8 của các phân khúc.

  1. Các ngành sẽ theo dõi phân khúc cao nhất cho mỗi cột dọc để các phân đoạn cao hơn đó có thể nhanh chóng được xác định trống.
  2. Nó cũng sẽ theo dõi phân đoạn bị che khuất phía dưới, để mọi phân đoạn cần được hiển thị từ bề mặt có thể nhanh chóng được lấy.
  3. Các ngành cũng sẽ theo dõi thời gian tiếp theo mỗi phân đoạn cần được cập nhật (vật lý nước, sinh trưởng của cây, vân vân). Cách tải này trong mỗi lĩnh vực sẽ đủ để giữ cho thế giới tồn tại và chỉ tải trong các phân đoạn đủ dài để hoàn thành nhiệm vụ của họ.
  4. Tất cả các vị trí thực thể sẽ được theo dõi liên quan đến ngành. Điều này sẽ ngăn các lỗi dấu phẩy động xuất hiện trong Minecraft khi đi rất xa từ trung tâm bản đồ.

Thế giới sẽ là một bản đồ vô tận của các lĩnh vực.

  1. Thế giới sẽ chịu trách nhiệm quản lý các lĩnh vực và cập nhật tiếp theo của họ.
  2. Thế giới sẽ ưu tiên gửi các phân khúc cho người chơi dọc theo con đường tiềm năng của họ. Minecraft phản ứng gửi các phân đoạn mà khách hàng yêu cầu, gây ra sự chậm trễ.

Tôi thường thích ý tưởng này, nhưng bên trong bạn sẽ lập bản đồ các lĩnh vực của Thế giới như thế nào?
Clashsoft

Mặc dù một mảng sẽ là giải pháp tốt nhất cho Gạch trong Phân đoạn và Phân đoạn trong Khu vực, Các ngành trong Thế giới sẽ cần một cái gì đó khác biệt để cho phép kích thước bản đồ vô hạn. Đề xuất của tôi sẽ là sử dụng bảng băm (từ điển giả <Vector2i, ngành>), sử dụng tọa độ XY cho hàm băm. Sau đó, Thế giới có thể chỉ cần tra cứu một khu vực tại các tọa độ nhất định.
Josh Brown

6

Minecraft khá nhanh, ngay cả trên 2 nhân của tôi. Java dường như không phải là một yếu tố hạn chế, ở đây, mặc dù có một chút độ trễ của máy chủ. Các trò chơi địa phương dường như làm tốt hơn, vì vậy tôi sẽ giả sử một số sự thiếu hiệu quả ở đó.

Đối với câu hỏi của bạn, Notch (tác giả Minecraft) đã viết blog ở một số chiều dài về công nghệ. Cụ thể, thế giới được lưu trữ trong "khối" (đôi khi bạn nhìn thấy những thứ này, đặc biệt là khi một thế giới bị mất vì thế giới vẫn chưa được lấp đầy.), Vì vậy, tối ưu hóa đầu tiên là quyết định xem có thể nhìn thấy một khối hay không .

Trong một đoạn, như bạn đã đoán, ứng dụng phải quyết định xem có thể nhìn thấy một khối hay không, dựa trên việc có bị che khuất bởi các khối khác hay không.

Cũng lưu ý rằng có các khối FACES, có thể được coi là không nhìn thấy, do bị che khuất (nghĩa là một khối khác che mặt) hoặc theo hướng mà camera hướng (nếu camera quay về hướng Bắc, bạn có thể Tôi không thấy mặt phía Bắc của BẤT K blocks khối nào!)

Các kỹ thuật phổ biến cũng sẽ bao gồm không giữ các đối tượng khối riêng biệt mà thay vào đó là một "khối" các loại khối, với một khối nguyên mẫu duy nhất cho mỗi loại, cùng với một số dữ liệu tối thiểu để mô tả cách khối này có thể được tùy chỉnh. Ví dụ, không có bất kỳ khối đá granit tùy chỉnh nào (mà tôi biết), nhưng nước có dữ liệu để cho biết độ sâu của nó dọc theo mỗi mặt, từ đó người ta có thể tính toán hướng chảy của nó.

Câu hỏi của bạn không rõ ràng nếu bạn đang tìm cách tối ưu hóa tốc độ kết xuất, kích thước dữ liệu hoặc những gì. Làm rõ sẽ có ích.


4
"cụm" thường được đặt tên chunk.
Marco

Bắt tốt (+1); Trả lời cập nhật. (Đã làm cho bộ nhớ ban đầu và quên từ đúng.)
Olie

Sự không hiệu quả mà bạn đề cập đến còn được gọi là "mạng", không bao giờ hoàn toàn hoạt động theo cùng một cách hai lần, ngay cả với các điểm cuối cùng giao tiếp.
Edwin Buck

4

Đây chỉ là một vài từ về thông tin và lời khuyên chung chung mà tôi có thể cung cấp với tư cách là một modder Minecraft có kinh nghiệm quá mức (mà ít nhất có thể cung cấp cho bạn một số hướng dẫn.)

Lý do Minecraft chậm có rất nhiều vấn đề với một số quyết định thiết kế cấp thấp, đáng nghi ngờ - ví dụ, mỗi khi một khối được tham chiếu bằng định vị, trò chơi sẽ xác nhận tọa độ với khoảng 7 nếu các câu lệnh để đảm bảo nó không nằm ngoài giới hạn . Hơn nữa, không có cách nào để lấy một "khối" (một đơn vị khối 16x16x256 mà trò chơi hoạt động cùng) sau đó tham chiếu các khối trong đó, để bỏ qua việc tra cứu bộ đệm và, các vấn đề xác thực ngớ ngẩn (iow, mỗi tham chiếu khối cũng liên quan đến một tra cứu chunk, trong số những thứ khác.) Trong mod của tôi, tôi đã tạo ra một cách để lấy và thay đổi mảng khối trực tiếp, điều này đã thúc đẩy thế hệ ngục tối khổng lồ từ độ trễ không thể phát hiện đến nhanh chóng đáng chú ý.

EDIT: Đã xóa yêu cầu khai báo các biến ở một phạm vi khác dẫn đến tăng hiệu suất, điều này thực sự không xảy ra. Tôi tin vào thời điểm mà tôi kết hợp kết quả này với một thứ khác mà tôi đang thử nghiệm (cụ thể là loại bỏ các phôi giữa đôi và phao trong mã liên quan đến vụ nổ bằng cách hợp nhất thành đôi ... có thể hiểu được, điều này có tác động rất lớn!)

Ngoài ra, mặc dù đó không phải là khu vực tôi dành nhiều thời gian, nhưng phần lớn hiệu suất bị nghẹt trong Minecraft là một vấn đề với kết xuất (khoảng 75% thời gian trò chơi dành riêng cho hệ thống của tôi). Rõ ràng là bạn không quan tâm lắm nếu mối quan tâm hỗ trợ nhiều người chơi hơn trong nhiều người chơi (máy chủ không kết xuất bất cứ thứ gì), nhưng vấn đề là máy của mọi người thậm chí có thể chơi được.

Vì vậy, dù bạn chọn ngôn ngữ nào, hãy cố gắng thực hiện thật thân mật với các chi tiết triển khai / mức độ thấp, bởi vì ngay cả một chi tiết nhỏ trong dự án như thế này cũng có thể tạo ra sự khác biệt (một ví dụ đối với tôi trong C ++ là "Trình biên dịch có thể thực hiện chức năng tĩnh không con trỏ? "Có chứ! Có thể tạo ra sự khác biệt đáng kinh ngạc trong một trong những dự án tôi đang thực hiện, vì tôi có ít mã hơn và lợi thế của nội tuyến.)

Tôi thực sự không thích câu trả lời đó vì nó làm cho thiết kế cấp cao trở nên khó khăn, nhưng đó là sự thật đau lòng nếu hiệu suất là một mối quan tâm. Hy vọng bạn tìm thấy điều này hữu ích!

Ngoài ra, câu trả lời của Gavin bao gồm một số chi tiết tôi không muốn nhắc lại (và nhiều hơn nữa! Anh ấy rõ ràng hiểu biết về chủ đề này hơn tôi) và tôi đồng ý với anh ấy phần lớn. Tôi sẽ phải thử nghiệm nhận xét của anh ấy về bộ xử lý và kích thước biến ngắn hơn, tôi chưa bao giờ nghe về điều đó-- Tôi muốn chứng minh với bản thân mình rằng đó là sự thật!


2

Vấn đề là suy nghĩ về cách trước tiên bạn sẽ tải dữ liệu. Nếu bạn truyền dữ liệu bản đồ của mình vào bộ nhớ khi cần, chắc chắn sẽ có giới hạn tự nhiên cho những gì bạn có thể kết xuất, đây đã là một bản nâng cấp hiệu suất kết xuất.

Những gì bạn làm với dữ liệu này là tùy thuộc vào bạn. Đối với hiệu suất GFX, sau đó bạn có thể sử dụng Clipping để cắt các đối tượng ẩn, các đối tượng quá nhỏ để có thể nhìn thấy, v.v.

Nếu bạn đang tìm kiếm các kỹ thuật Hiệu suất Đồ họa thì tôi chắc chắn bạn có thể tìm thấy hàng núi thứ trên mạng.


1

Một cái gì đó để xem xét là các mẫu thiết kế Flykg . Tôi tin rằng hầu hết các câu trả lời ở đây tham khảo mẫu thiết kế này theo cách này hay cách khác.

Mặc dù tôi không biết phương pháp chính xác mà Minecraft đang sử dụng để giảm thiểu bộ nhớ cho từng loại khối, đây là một con đường có thể sử dụng trong trò chơi của bạn. Ý tưởng là chỉ có một đối tượng, giống như một đối tượng nguyên mẫu, chứa thông tin về tất cả các khối. Sự khác biệt duy nhất sẽ là vị trí của mỗi khối.

Nhưng ngay cả vị trí cũng có thể được giảm thiểu: nếu bạn biết một khối đất là một loại, tại sao không lưu trữ kích thước của vùng đất đó dưới dạng một khối khổng lồ, với một bộ dữ liệu vị trí?

Rõ ràng cách duy nhất để biết là bắt đầu thực hiện của riêng bạn, và thực hiện một số bài kiểm tra bộ nhớ cho hiệu suất. Hãy cho chúng tôi biết làm thế nào nó đi!

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.