Phân vùng không gian khi mọi thứ đang di chuyển


8

Lý lịch

Cùng với một người bạn tôi đang làm việc trong một trò chơi 2D được đặt trong không gian. Để làm cho nó trở nên nhập vai và tương tác nhất có thể, chúng tôi muốn có hàng ngàn vật thể tự do trôi nổi xung quanh, một số cụm lại với nhau, một số khác di chuyển trong không gian trống.

Thử thách

Để giải phóng công cụ kết xuất và vật lý, chúng ta cần thực hiện một số loại phân vùng không gian. Có hai thử thách chúng ta phải vượt qua. Thách thức đầu tiên là mọi thứ đều chuyển động nên việc xây dựng lại / cập nhật cấu trúc dữ liệu phải cực kỳ rẻ vì nó sẽ phải được thực hiện ở mọi khung hình. Thách thức thứ hai là phân phối các vật thể, như đã nói trước đó có thể có các cụm vật thể với nhau và các khoảng trống lớn và để làm cho nó thậm chí còn tệ hơn không có ranh giới với không gian.

Công nghệ hiện có

Tôi đã xem xét các kỹ thuật hiện có như BSP-Plants, QuadTrees, kd-Plants và thậm chí R-Plants nhưng theo tôi có thể nói các cấu trúc dữ liệu này không phù hợp hoàn hảo vì cập nhật nhiều đối tượng đã di chuyển sang các ô khác là tương đối đắt tiền.

Những gì tôi đã thử

Tôi đã đưa ra quyết định rằng tôi cần một cấu trúc dữ liệu hướng đến việc chèn / cập nhật nhanh hơn là trả lại số lần truy cập ít nhất có thể được cung cấp cho một truy vấn. Với mục đích đó, tôi đã tạo ra các ô ẩn để mỗi đối tượng, với vị trí của nó, có thể tính toán được các ô đó sẽ nằm ở đâu. Sau đó, tôi sử dụng một HashMapánh xạ tọa độ ô đến một ArrayList(nội dung của ô). Điều này hoạt động khá tốt vì không có bộ nhớ bị mất trên các ô 'trống' và dễ dàng tính toán các ô nào cần kiểm tra. Tuy nhiên, việc tạo ra tất cả những thứ đó ArrayList(trường hợp xấu nhất N) rất tốn kém và vì vậy nó đang tăng lên HashMaprất nhiều lần (mặc dù điều đó được giảm nhẹ bằng cách cho nó một công suất ban đầu lớn).

Vấn đề

OK để nó hoạt động nhưng vẫn không nhanh. Bây giờ tôi có thể cố gắng tối ưu hóa vi mã JAVA. Tuy nhiên tôi không mong đợi quá nhiều về điều đó vì trình hồ sơ cho tôi biết rằng phần lớn thời gian được dành để tạo ra tất cả những đối tượng mà tôi sử dụng để lưu trữ các tế bào. Tôi hy vọng rằng có một số thủ thuật / thuật toán khác hiện có giúp việc này nhanh hơn rất nhiều vì vậy đây là cấu trúc dữ liệu lý tưởng của tôi trông như sau:

  • Ưu tiên số một là cập nhật / tái cấu trúc nhanh toàn bộ cấu trúc dữ liệu
  • Việc phân chia các đối tượng thành các thùng có kích thước bằng nhau, điều quan trọng hơn là chúng ta có thể vẽ thêm một vài đối tượng và thực hiện thêm một số kiểm tra va chạm nếu điều đó có nghĩa là việc cập nhật nhanh hơn một chút
  • Bộ nhớ không thực sự quan trọng (trò chơi trên PC)

"[...] nó sẽ phải được thực hiện ở mọi khung hình." Tại sao? Bạn không thể dự đoán nếu một đối tượng sẽ rời khỏi tế bào của họ trong tương lai gần?
API-Beast

Bạn có thể bỏ qua việc cập nhật các đối tượng ở rất xa người chơi không? Hoặc ít nhất là cập nhật chúng ít thường xuyên hơn?
Liosan

@ Mr.Beast và Liosan, hai ý tưởng kết hợp có thể hoạt động nhưng các đối tượng sẽ phải tự tìm ra nếu có điều gì đó quan trọng xảy ra (như tăng tốc độ nhanh) v.v ... Bạn có bất kỳ ví dụ nào về ý tưởng này được sử dụng không?
Roy T.

Trình lược tả cho bạn biết rằng phần lớn thời gian được dành để tạo ArrayList hoặc khởi tạo các đối tượng được chứa? Bạn không thể preallocated và gộp các đối tượng này?
Fabien

@Fabien thực sự phân bổ và phát triển ArrayList là vấn đề lớn nhất, gộp chung có thể là một giải pháp. Tôi tự hỏi nếu tôi có thể tìm ra bằng cách dùng thử và lỗi lỗi nhóm nên lớn như thế nào và danh sách mảng trong nhóm nên lớn như thế nào.
Roy T.

Câu trả lời:


6

Kỹ thuật bạn đang sử dụng rất giống với kỹ thuật vật lý tính toán được gọi là động lực phân tử, trong đó quỹ đạo của các nguyên tử (thường là trong phạm vi hạt 100k đến 10M) được thực hiện theo các bước thời gian rất nhỏ. Vấn đề chính là để tính lực tác dụng lên một hạt, bạn phải so sánh vị trí của nó với vị trí của mọi hạt khác, có tỷ lệ rất kém (n bình phương).

Có một mẹo tôi có thể đề xuất, đó là yêu cầu bạn chọn một khoảng cách tối đa mà mọi thứ có thể tương tác. Là một điểm khởi đầu, tôi sẽ bắt đầu với thứ gì đó như 1/10 chiều dài của không gian của bạn và điều chỉnh theo sở thích (cắt dài hơn có nghĩa là chính xác hơn, nhưng tính toán nhiều hơn).

Phương pháp là lặp qua từng hạt (i). (I) nhận được một mảng trong đó tất cả các hạt trong phạm vi của i được thêm vào mảng. Những gì bạn nhận được cuối cùng là một mảng 2d, trong đó mục thứ i là một mảng của hạt trong phạm vi của i. Để tính toán các lực cho i, bạn chỉ phải kiểm tra các mục trong mảng của i.

Nghệ thuật của phương pháp này là chọn khoảng cách cắt và phần đệm thêm (ví dụ 20%). Tốc độ đạt được là bạn chỉ phải kiểm tra một vài tương tác cho mỗi hạt và bạn tính toán lại hàng xóm chỉ sau vài bước. Tôi khuyên bạn nên chọn tốc độ hơi nhanh và tìm hiểu xem sẽ mất bao nhiêu bước để vượt qua khu vực "đệm". Làm cho phần đệm lớn hơn (50% hoặc thậm chí 100% mức cắt) giúp bạn có thêm các bước giữa tính toán lại hàng xóm, nhưng làm cho mỗi bước chậm hơn một chút. Sự cân bằng của điều này là một sự đánh đổi.

Một mẹo khác trong việc tính toán khoảng cách là làm việc với d ^ 2 thay vì d, loại bỏ một loạt các lệnh gọi đến pow () và sqrt ().

Chỉnh sửa: Khó tìm thấy một liên kết ref không siêu kỹ thuật. Đây là người duy nhất tôi có thể tìm thấy.


Nghe có vẻ là một ý tưởng đầy hứa hẹn, tôi chắc chắn sẽ xem xét điều đó!
Roy T.

2

Giải pháp của riêng bạn nghe có vẻ khá tốt nếu bạn có thể đạt được việc xây dựng cấu trúc dữ liệu trong o (n) thì tôi sẽ nói việc tối ưu hóa phải được thực hiện trên sự lựa chọn cấu trúc dữ liệu thay vì trên thuật toán.

Tôi có một triển khai tương tự với một số khác biệt: Cấu trúc dữ liệu chính là một mảng có kích thước cố định (như ArrayList), là cách tốt nhất để truy cập trực tiếp vào một phần tử. Mỗi ô của mảng chứa một danh sách được liên kết, đây là danh sách tốt nhất để chèn và cũng như danh sách mảng cần lặp. Sau này chúng ta sẽ cần xóa các phần tử khỏi danh sách được liên kết, vì vậy để thực hiện thao tác này rất nhanh, một ý tưởng là lưu trữ trong mỗi phần tử của danh sách một trình vòng lặp trỏ đến chính nó (bạn nói bộ nhớ không phải là vấn đề, phải không?)

Để khởi tạo, mỗi "hạt" được chèn vào cuối danh sách được liên kết tương ứng với ô trong mảng khớp với vị trí của nó trong không gian, giả sử rằng không gian được phân vùng trong các ô có kích thước cố định. Vì vậy, chúng tôi vẫn với độ phức tạp o (n), nhưng chúng tôi tối ưu hóa toàn bộ bằng cách sử dụng các thùng chứa phù hợp hơn với việc sử dụng.

Mỗi "hạt" có một tham chiếu đến danh sách được liên kết chứa của nó để cung cấp quyền truy cập nhanh đến các vùng lân cận.

Ở mỗi khung, chúng ta có thể tạo sự tương tác giữa mỗi hạt với danh sách các lân cận của nó và tôi cũng sẽ nói với 8 ô xung quanh để tránh các hiệu ứng ngưỡng gần viền gạch.

Không cần phải tính toán lại toàn bộ phân vùng ở mỗi khung; chúng ta chỉ cần xóa và đặt lại một mục khi nó di chuyển nhiều hơn một khoảng cách nhất định hoặc, bằng bảo mật, mỗi khung X. Một ý tưởng có thể là lưu trữ vị trí của từng mục tại thời điểm nó được chèn vào danh sách được liên kết và tại mỗi khung so sánh vị trí hiện tại với vị trí cũ.



Tôi đã sử dụng một cái gì đó tương tự như thế này trong việc tính toán dữ liệu dựa trên các vị trí nguyên tử mô phỏng. Nó tăng tốc một phép tính mất hàng giờ / ngày và biến nó thành phút. Nó là một chút phức tạp để thiết lập.
Chổi Brian
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.