Làm việc với nhiều hình khối. Cải thiện hiệu suất?


12

Chỉnh sửa: Để tổng hợp câu hỏi, tôi có một thế giới dựa trên voxel (phong cách Minecraft (Cảm ơn vịt cộng sản)) đang phải chịu đựng hiệu suất kém. Tôi không tích cực về nguồn này nhưng muốn có bất kỳ lời khuyên nào có thể về cách thoát khỏi nó.

Tôi đang làm việc trong một dự án trong đó một thế giới bao gồm một số lượng lớn hình khối (tôi sẽ cung cấp cho bạn một số, nhưng đó là thế giới do người dùng định nghĩa). Bài kiểm tra của tôi là khoảng (48 x 32 x 48) khối.

Về cơ bản, các khối này không tự làm bất cứ điều gì. Họ chỉ ngồi đó.

Chúng bắt đầu được sử dụng khi nói đến tương tác người chơi.

Tôi cần kiểm tra những khối mà chuột người dùng tương tác với (chuột, nhấp, v.v.) và để phát hiện va chạm khi người chơi di chuyển.

Bây giờ tôi đã có một độ trễ lớn lúc đầu, lặp qua mọi khối.

Tôi đã cố gắng giảm độ trễ đó, bằng cách lặp qua tất cả các khối và tìm khối nào nằm trong một phạm vi cụ thể của nhân vật, và sau đó chỉ lặp qua các khối đó để phát hiện va chạm, v.v.

Tuy nhiên, tôi vẫn đang đi với tốc độ 2fps.

Có ai có bất kỳ ý tưởng nào khác về cách tôi có thể giảm độ trễ này không?

Btw, tôi đang sử dụng XNA (C #) và vâng, đó là 3d.


Ý bạn là giống Minecraft? Đó là, voxel?
Vịt Cộng sản

4
Bạn đã nhìn vào octrees? vi.wikipedia.org/wiki/Octree
bummzack

1
Bạn đã thử hồ sơ trò chơi của bạn? Nó có thể hiển thị một số lĩnh vực chính mà thời gian nhiều nhất đang được sử dụng. Nó có thể không phải là những gì bạn nghĩ rằng nó là.
giảm tốc

2
Thay vì vẽ tất cả 6 mặt của mỗi khối, bạn chỉ có thể vẽ các mặt không tiếp xúc với bất cứ thứ gì
David Ashmore

1
@David: Vâng, hoặc anh ấy có thể dừng thực hiện một cuộc gọi rút thăm trên mỗi khối trước, và sau đó lo lắng về các đa giác riêng lẻ sau đó.
Olhovsky

Câu trả lời:


20

Âm thanh như bạn đang muốn tìm hiểu về Cây!

Và tôi đang nghiêm túc, nếu bạn hiện đang lặp lại một mảng của tất cả các hình khối của bạn, thì bạn thực sự nên xem xét các cấu trúc dữ liệu không gian khác nhau. Trong trường hợp này, cách tốt nhất để tưởng tượng lại thế giới khối của bạn là một cái cây.

Trước khi chúng ta đi vào lý do là tại sao, hãy suy nghĩ về vấn đề của chúng ta. Chúng tôi đang tìm kiếm một giải pháp trong đó, với chi phí thấp nhất có thể, chúng tôi có thể truy xuất danh sách các hình khối gần đó mà người chơi có thể va chạm. Danh sách này nên nhỏ, nhưng chính xác nhất có thể.

Bây giờ để xác định vùng này, chúng ta cần ánh xạ không gian tọa độ của người chơi sang không gian tọa độ của bản đồ khối; nghĩa là, chúng ta cần ánh xạ vị trí dấu phẩy động của người chơi thành một chỉ số riêng biệt của mảng khối đa chiều (ký hiệu ví dụ có thể là world[31][31][31], tức là chính xác ở giữa cho mảng đa chiều 64 * 64 * 64).

Chúng ta có thể chỉ cần tính toán các khối xung quanh bằng cách lập chỉ mục rời rạc tương tự này, có thể chỉ lấy mẫu các khối gần đó, nhưng điều này vẫn đòi hỏi phải tính toán lại liên tục và không cho phép bất kỳ đối tượng nào không rời rạc trong vị trí (nghĩa là không thể ánh xạ vào khối bản đồ).

Tình huống lý tưởng là một nhóm các thùng chứa các bộ khối cho các phần cụ thể của bản đồ khối của chúng tôi, được chia đều cho nhau thay vì tính toán lại khu vực xung quanh, chúng tôi chỉ cần di chuyển vào và ra khỏi những khu vực . Đối với bất kỳ phép tính không tầm thường nào, việc giữ dữ liệu của chúng tôi như thế này có thể loại bỏ việc lặp lại tất cả các hình khối và chỉ những bộ riêng lẻ này ở gần đó.

Câu hỏi là: Làm thế nào để chúng ta thực hiện điều này?

Đối với thế giới 64 * 64 * 64, hãy tưởng tượng nó được chia thành 8 * 8 * 8 vùng . Điều này có nghĩa là trong thế giới của bạn, bạn sẽ có 8 vùng trên mỗi trục (X, Y, Z). Mỗi cái khu vực này sẽ chứa 8 khối, có thể dễ dàng truy xuất bằng chỉ mục đơn giản hóa mới này.

Nếu bạn cần thực hiện một thao tác trên một tập hợp các hình khối gần đó, thay vì lặp lại mọi khối lập phương trong thế giới của bạn, bạn có thể chỉ cần lặp lại các khu vực này , phá vỡ số lần lặp tối đa từ 64 * 64 * 64 (262144) ban đầu để chỉ cần 520 (8 * 8 * 8 + 8).

Bây giờ hãy thu nhỏ từ thế giới khu vực này và đặt các khu vực thành các siêu khu vực lớn hơn ; trong đó mỗi siêu vùng chứa 2 * 2 * 2 vùng thông thường . Khi thế giới của bạn hiện chứa 512 (8 * 8 * 8) khu , chúng ta có thể phá vỡ 8 * 8 * 8 khu thành 64 (4 * 4 * 4) siêu khu bằng cách chia 8 khu 2 khu mỗi siêu zone . Áp dụng logic tương tự từ phía trên, điều này sẽ phá vỡ các lần lặp tối đa từ 512 đến 8 để tìm siêu vùng ; và sau đó tối đa là 64 để tìm khu vực tố tụng (tổng tối đa 72)! Bạn có thể thấy điều này giúp bạn tiết kiệm rất nhiều lần lặp lại (262144: 72).

Tôi chắc chắn bạn có thể thấy bây giờ cây hữu ích như thế nào. Mỗi vùng là một nhánh trên cây, với mỗi siêu vùng là một nhánh trước. Bạn chỉ đơn giản là đi ngang qua cây để tìm thứ bạn cần; sử dụng các bộ dữ liệu nhỏ hơn để giảm thiểu chi phí tổng thể.

Sơ đồ dưới đây sẽ giúp bạn hình dung khái niệm. (hình ảnh từ Wikipedia: Octrees ): Tháng mười

Tuyên bố từ chối trách nhiệm:

Trong một thiết lập lý tưởng như trên, trong đó thế giới voxel của bạn đã được bố trí trong một mảng đa chiều có kích thước cố định, bạn có thể chỉ cần truy vấn vị trí người chơi, sau đó lập chỉ mục các khối xung quanh với chi phí O (1)! (Xem giải thích của Olhovskys) Nhưng điều này trở nên khó khăn hơn khi bạn bắt đầu xem xét rằng thế giới của bạn hiếm khi có kích thước cố định trong trò chơi voxel; và bạn có thể cần cấu trúc dữ liệu của mình để có thể tải toàn bộ siêu vùng từ ổ cứng vào bộ nhớ. Không giống như một mảng đa chiều có kích thước cố định, cây dễ dàng cho phép điều này mà không mất quá nhiều thời gian cho các thuật toán kết hợp.


Tôi sẽ nói tôi nhận được nó, nhưng tiếc là tôi không. Các hộp va chạm của các khối không di chuyển, chỉ có hộp va chạm của người chơi. Và tôi có một phương thức bây giờ (không lặp qua tất cả các khối) trả về tất cả các khối nằm trong bán kính 5 khối của người chơi. Xin lỗi vì sự rắc rối, nhưng có làm rõ? Nhân tiện, để đơn giản hơn, bạn có thể cho rằng thế giới là 64 x 64 x 64 không?
Joel

Và tôi vẫn nhận được khoảng 5fps :(
Joel

Tôi nói lại câu trả lời của mình, cho tôi biết nếu nó giúp.
giảm tốc

Vì vậy, tôi thu hẹp các khối mà người chơi có thể, bằng cách sử dụng kỹ thuật octree này. Tôi khá chắc chắn rằng tôi nhận được điều đó. Nhưng bạn đang đề nghị tôi sử dụng phát hiện va chạm của mình một khi tôi đã thu hẹp nó xuống chỉ còn một lựa chọn nhỏ cho các khối, hoặc bạn có một số phương pháp khác không?
Joel

2
Trừ khi người chơi rất lớn so với kích thước của các hình khối, thì kiểm tra va chạm xung quanh người chơi có lẽ là cách nhanh nhất. Nếu người chơi chiếm không gian nhiều hơn một khối chẳng hạn, thì anh ta chỉ cần kiểm tra tối đa 27 khối xung quanh, để tìm va chạm. Điều này không yêu cầu cây vì bạn có thể lập chỉ mục trực tiếp vào các vị trí khối đó, giả sử anh ta lưu trữ các khối trong một mảng mà anh ta có thể lập chỉ mục, với một vị trí được phân bổ cho mọi vị trí khối có thể.
Olhovsky

13

Tôi đồng ý với Daniels câu trả lời, trong iterating rằng thông qua một lượng lớn hộp là nguyên nhân rất có thể, và điều đó bằng cách sử dụng phân vùng spacial bạn có thể tăng tốc độ game lên rất nhiều - nhưng vấn đề có thể cũng được ở nơi khác, và bạn có thể lãng phí thời gian của bạn .

Để tăng tốc độ trò chơi của bạn một cách đáng kể, bạn cần phải lập hồ sơ mã của mình. Xác định nơi tắc nghẽn là gì, điều này sẽ cho phép bạn thực hiện những cải tiến lớn nhất.

Có rất nhiều cách để lập hồ sơ mã của bạn, bạn có thể cuộn lớp phân tích hiệu suất của riêng mình (có thể sử dụng lớp Đồng hồ bấm giờ (MSDN) ) hoặc bạn có thể sử dụng PIX để có ý tưởng chung về mức độ bận rộn của CPU / GPU .

Bạn cũng có thể đặt các dấu sự kiện PIX trong mã của mình, mã này sẽ hiển thị dưới dạng các vùng được tô màu trong phần đọc của PIX. Không có giao diện C # chính thức cho các chức năng này, nhưng chủ đề này cho thấy cách bạn có thể tự tạo giao diện C #.


2
+1, hoàn toàn đồng ý. Bạn không nên xem xét các thuật toán, bạn nên xem xét hồ sơ mã của bạn. Tôi đoán là không có gì để làm với thực tế là bạn đang lặp qua các khối (không phải là nhiều), đó là những gì bạn đang làm với mỗi khối. Cuối cùng, bạn cần phải có một phương pháp tốt hơn so với lực lượng vũ phu, nhưng nếu bạn không thể xử lý một phép lặp đơn giản trên các khối 48x32x48, bạn cần suy nghĩ lại về những gì bạn đang làm với mỗi khối, chứ không phải cách bạn đang lặp.
Tim Holt

4
@Tim: Trừ khi người chơi của anh ta đủ lớn để chiếm một không gian 48x32x48, thì anh ta không nên lặp đi lặp lại qua bất cứ nơi nào gần nhiều hình khối đó. Nếu anh ta lặp đi lặp lại qua 73000 khối trên mỗi khung, thì tôi có thể nói với bạn mà không cần thực hiện bất kỳ hồ sơ nào, rằng anh ta nên sửa nó, nếu không vì lý do nào khác ngoài việc học cách tránh lặp đi lặp lại hàng chục nghìn lần so với là cần thiết. Đây không phải là những gì tôi sẽ gọi là tối ưu hóa vi mô hoặc sớm.
Olhovsky

Trình phát của tôi nhỏ hơn kích thước của 1 khối, mặc dù có thể lớn hơn ở một số giai đoạn (nhưng không nhiều)
Joel

Randomman159: Sau đó, bạn chỉ cần thử nghiệm với 27 khối xung quanh để tìm va chạm. Xem câu trả lời của tôi.
Olhovsky

6

Nếu trình phát của bạn lớn tương đối với kích thước của các hình khối, thì bạn có thể muốn một cấu trúc phân vùng không gian hoặc octree khác, như những người khác đã đề xuất.

Tuy nhiên, nếu trình phát của bạn nhỏ so với kích thước của các hình khối, thì có lẽ cách nhanh nhất để phát hiện va chạm với các hình khối là thực hiện tìm kiếm tuyến tính đơn giản về khu vực xung quanh người chơi.

Vì trình phát của bạn nhỏ hơn 1 khối, nên bạn chỉ cần kiểm tra va chạm với 27 khối lân cận.

Điều này giả định rằng bạn lưu trữ các hình khối trong một mảng mà bạn có thể lập chỉ mục, với một vị trí trong mảng cho mỗi khối.

Như những người khác đã chỉ ra, bạn cần lập hồ sơ mã của bạn để xem điều gì thực sự làm bạn chậm lại.

Nếu tôi phải đoán mặc dù, tôi sẽ nói rằng có lẽ bạn đang thực hiện một cuộc gọi bốc thăm cho mọi khối, đó sẽ là nút cổ chai của bạn cho đến nay. Để khắc phục điều đó, bạn nên xem xét hình học.


Hoặc bạn có thể có một 'hộp giới hạn' bao quanh người chơi, và sau đó bạn chỉ cần kiểm tra những vật thể mà nó đang va chạm để xác định những vật thể mà người chơi nên va chạm. Một công cụ vật lý tốt sẽ có thể thực hiện tất cả các tối ưu hóa cho bạn; nó cũng sẽ cho phép nhiều hơn chỉ là 'khối' va chạm với nhau.
giảm tốc

Cá nhân, tôi không muốn dựa vào broadphase của động cơ vật lý để kiểm tra 73000 khối cho tôi, khi tôi chỉ có thể viết hai chục dòng mã để kiểm tra va chạm hiệu quả với tôi. Ngoài ra, anh ta có lẽ không có động cơ vật lý để khai thác vào lúc này.
Olhovsky

1

Thêm một gợi ý để tăng tốc mọi thứ: Các khối của bạn gần như cố định - điều đó có nghĩa là không có cách nào người chơi có thể va chạm với hầu hết chúng. Thêm một boolean vào các khối cho biết liệu chúng có bị lộ hay không. (Điều này có thể được tính toán lại bằng cách nhìn vào hàng xóm của họ.) Một khối không bị lộ không cần phải kiểm tra va chạm.

Minecraft rõ ràng làm điều gì đó tương tự như thế này - Tôi đã chạm vào một khối không tải một lần cho tôi một cái nhìn vào thế giới - Tôi có thể nhìn thấy ngay trên mặt đất vững chắc, tất cả những gì hiện lên là không gian mở (phía xa của chúng là một bề mặt lộ ra và do đó được hiển thị.)


-1

Tôi đã có vấn đề với động cơ voxel của tôi.

Giải pháp: (Đơn giản hơn nhiều so với octrees) Thay vì lặp qua tất cả các khối, chỉ cần sử dụng một phương trình để xác định vị trí của khối trong mảng khối.

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

Sau đó, nếu bạn muốn xem nếu một khối tồn tại:

Blocks[BlockIndex].Type > -1;

Hoặc tuy nhiên bạn xác định nếu khối tồn tại.


Vấn đề ở đây phức tạp hơn, vì bạn chỉ có 2 tọa độ cho chuột, để kiểm tra với thế giới 3D. Nếu chế độ xem từ trên xuống, bạn có thể tìm thấy 2 trong số 3 chỉ mục bên phải cho vị trí chuột và sau đó lặp theo chiều cao, bắt đầu từ trên xuống - gần camera hơn và tìm thấy sự xuất hiện đầu tiên của một khối.
Markus von Broady
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.