Cách nhanh nhất để nhóm các đơn vị có thể nhìn thấy nhau?


12

Trong trò chơi 2D tôi đang làm việc, công cụ trò chơi có thể cung cấp cho tôi, cho mỗi đơn vị, danh sách các đơn vị khác nằm trong phạm vi xem của nó.

Tôi muốn biết nếu có một thuật toán được thiết lập để sắp xếp các đơn vị theo nhóm , trong đó mỗi nhóm sẽ được xác định bởi tất cả các đơn vị được "kết nối" với nhau (thậm chí thông qua các đơn vị khác).

Một ví dụ có thể giúp hiểu câu hỏi tốt hơn (E = kẻ thù, O = đơn vị riêng). Đầu tiên, dữ liệu mà tôi sẽ nhận được từ công cụ trò chơi:

E1 can see E2, E3, O5
E2 can see E1
E3 can see E1
E4 can see O5
E5 can see O2
E6 can see E7, O9, O1
E7 can see E6
O1 can see E6
O2 can see O5, E5
O5 can see E1, E4, O2
O9 can see E6

Sau đó, tôi nên tính toán các nhóm như sau:

G1 = E1, E2, E3, E4, E5, O2, O5
G2 = O1, O9, E6, E7

Có thể giả định một cách an toàn rằng có một thuộc tính giao hoán cho trường nhìn: [nếu A thấy B, thì B thấy A].

Chỉ cần làm rõ: Tôi đã viết một triển khai ngây thơ lặp đi lặp lại trên mỗi hàng của thông tin công cụ trò chơi, nhưng từ cái nhìn của nó, có vẻ như một vấn đề chung đủ để nó được nghiên cứu sâu và có nhiều thuật toán được thiết lập (có thể vượt qua thông qua một số cấu trúc giống như cây?). Vấn đề của tôi là tôi không thể tìm ra cách nào để mô tả vấn đề của mình đã trả lại các lượt truy cập google hữu ích.

Cảm ơn rất nhiều về sự trợ giúp của bạn!


1
Tôi nghĩ rằng câu hỏi này đủ chung để nó có thể nhận được câu trả lời tốt hơn trong stackoverflow, hoặc thậm chí có thể là toán học (lý thuyết tập hợp?). Đó không phải là phát triển trò chơi cụ thể là quan điểm của tôi.
Tor Valamo

1
@Tor - Có lẽ đúng, nhưng thực tế là chúng tôi biết rằng trò chơi có thể cho phép mọi người đưa ra các câu trả lời cụ thể hơn cho vấn đề.
Robert Fraser

Tôi nghĩ rằng bạn có thể làm một số công cụ thông minh với một cú xoay trong băm không gian và bản đồ khả năng hiển thị - tôi chỉ cần nghĩ về nó.
Jonathan Dickinson

Câu trả lời:


7

Nếu mối quan hệ "có thể thấy" của bạn là đối xứng, do đó "A có thể thấy B" ngụ ý "B có thể thấy A", thì các nhóm bạn muốn tính toán là các thành phần được kết nối của biểu đồ được xác định bởi mối quan hệ "có thể thấy". Như những người khác đã lưu ý, có những thuật toán đơn giản để tính toán những thứ này, chẳng hạn như:

while ungrouped units remain:
    let u = arbitrary ungrouped unit
    let g = new group
    let s = temporary stack
    assign u to g
    push u onto s
    while s is not empty:
        let v = topmost unit in s
        remove v from s
        for each unit w that v can see:
            if w is ungrouped:
                assign w to g
                push w onto s
            end if
        end for
    end while
 end while

(Một hàng đợi hoặc bất kỳ bộ sưu tập nào khác thực hiện hiệu quả các hoạt động "thêm phần tử mới" và "loại bỏ và trả lại một số phần tử" có thể được sử dụng thay cho ngăn xếp sở trên.)

Nếu mối quan hệ "có thể thấy" của bạn không đối xứng, bạn cần quyết định xem bạn muốn các nhóm của mình là thành phần được kết nối mạnh hay yếu . Đối với các thành phần được kết nối yếu, thuật toán ở trên sẽ hoạt động như bình thường, ngoại trừ dòng for each unit w that v can seenên được thay thế bằng for each unit w that can see v, or that v can see. Đối với các thành phần được kết nối mạnh , bạn có thể sử dụng một trong các thuật toán (của Kosaraju , Tarjan hoặc Gabow ) được đề cập trên trang Wikipedia được liên kết.

Đối với các mối quan hệ không đối xứng, bạn cũng có thể muốn tính toán đóng cửa quá độ của mối quan hệ hoặc các thành phần được kết nối mạnh mẽ của nó. Đối với điều này, bạn có thể sử dụng thuật toán Floyd Nhận Warshall ; xem câu trả lời này trên SO để biết thêm (một chút) thông tin.


Thi thiên Như bài viết Wikipedia tôi đã liên kết với các ghi chú ở trên, có thể hiệu quả hơn khi cập nhật động các nhóm khi mối quan hệ hiển thị thay đổi. Tôi không quen thuộc với các thuật toán nâng cao (?) Được đề cập trên Wikipedia, nhưng không khó để kết hợp lại một cái gì đó mà ít nhất là nhịp đập tính toán lại các nhóm từ đầu mỗi lần.

Một nửa điều này là dễ dàng: nếu hai đơn vị trong các nhóm khác nhau có được một đường ngắm giữa chúng, hợp nhất các nhóm. Đối phó với các đơn vị mất tầm nhìn của nhau là một chút khó khăn hơn; một giải pháp đơn giản nhưng có lẽ không tối ưu là chạy lại thuật toán nhóm cho các đơn vị trong nhóm bị ảnh hưởng bất cứ khi nào điều đó xảy ra. Có một số tối ưu hóa bạn có thể thực hiện, nếu thay đổi mức độ hiển thị xảy ra một cặp đơn vị tại một thời điểm:

  • Nếu một đơn vị chỉ có thể nhìn thấy một đơn vị khác và mất tầm nhìn của đơn vị đó, chỉ cần xóa đơn vị đó khỏi nhóm trước đó và gán nó cho một nhóm mới.
  • Nếu không, bạn có thể bắt đầu tại một trong các đơn vị bị ảnh hưởng và chạy tìm kiếm A * trên biểu đồ khả năng hiển thị (ví dụ: sử dụng khoảng cách đường thẳng là heuristic) cho đơn vị khác. Nếu bạn tìm thấy nó, nhóm đã không chia tay; nếu bạn không, tập hợp các đơn vị tìm kiếm đã truy cập sẽ tạo thành nhóm mới.
  • Bạn có thể thử đoán xem đơn vị nào trong hai đơn vị có nhiều khả năng thuộc về một nửa nhỏ hơn của nhóm, nếu nó tách ra và bắt đầu tìm kiếm từ đơn vị đó. Một khả năng sẽ là luôn luôn bắt đầu từ đơn vị có thể trực tiếp nhìn thấy ít đơn vị khác.

4

Những gì bạn có là một biểu đồ kết nối. Và nói chung, cách tốt nhất để nhóm các nút được kết nối (ví dụ: ký tự) với nhau là với thuật toán tìm kiếm biểu đồ. Độ sâu-đầu tiên, chiều rộng đầu tiên, tùy theo cái nào. Tất cả những gì bạn đang làm là xây dựng một danh sách các nút có thể truy cập được từ tất cả các nút khác. Miễn là đồ thị của bạn không bị ảnh hưởng (nếu A hiển thị với B, thì B hiển thị với A), điều này hoạt động tốt.

Có thể có một số thuật toán để cải thiện điều này cho các trường hợp cụ thể. Ví dụ: nếu đôi khi các ký tự không di chuyển (và địa hình cũng không di chuyển, do đó, các ký tự bất động vẫn hiển thị) thì bạn có thể chọn không kiểm tra lại chúng để cập nhật biểu đồ kết nối của chúng.

Nhưng nói chung, bạn sẽ phải kiểm tra lại khả năng hiển thị mọi khung hình. Điều lạ lùng là, điều đó sẽ chậm hơn so với biểu đồ để tìm các nhóm khả năng hiển thị.


3
Chỉ cần thêm thuật ngữ kỹ thuật: thứ bạn đang cố gắng tìm là các thành phần được kết nối của biểu đồ và thuật toán tiêu chuẩn là: (1) đặt tất cả các nút trong danh sách, (2) chọn một nút, (3) tìm tất cả các nút được kết nối bằng BFS / DFS, (4) xóa tất cả các nút bạn tìm thấy khỏi danh sách, (5) lặp lại cho đến khi không còn nút nào nữa.
Nathan Reed

3

Có vẻ như một vấn đề kết nối đồ thị tiêu chuẩn. Cũng có thể có một số loại thuật toán cho việc này, và nó có thể trông giống như sau:

remaining units = all units
for each unit in remaining units:
    current group = create a new group
    add this unit to current group
    for each unit visible to this unit:
        if unit is in a group already:
            merge current group into that existing group
            set current group as that existing group
        else:
            remove that unit from remaining units
            add that unit to current group

Tôi hy vọng có thể thực hiện điều này thông qua một cây, như phân cụm theo thứ bậc, nhưng tôi nghi ngờ rằng nó sẽ hoạt động nhanh hơn - cây có xu hướng là O (log N) trong khi hầu hết các kiểm tra tôi đưa ra ở trên có thể được thực hiện như O (1) .


Không quan tâm, cách tiếp cận phân cụm theo thứ bậc là một chút như thế này: Đối với mỗi đơn vị, tạo một nhóm. Sau đó, đối với mọi cặp đơn vị có thể nhìn thấy nhau, nếu chúng thuộc các nhóm khác nhau, hãy hợp nhất các nhóm thành một và loại bỏ các nhóm khác.
Kylotan

Đây là những gì tôi gọi là triển khai ngây thơ trong OP của tôi. Thật tốt khi biết rằng nó có thể không tệ như tôi nghĩ lúc đó! :)
mac

Cách bạn làm như một cái cây là sử dụng một tập hợp với nén đường dẫn . Điều đó không ngây thơ lắm, và thực tế là tối ưu.
Peter Taylor

2

Tôi đồng ý với tất cả những người khác phản hồi về vấn đề này là vấn đề kết nối đồ thị, tuy nhiên hãy để tôi chỉ ra rằng thứ bạn cần ở đây là biểu đồ Tam giác Delaunay được tạo từ tất cả các đơn vị có liên quan của bạn. Điều này làm là đảm bảo rằng chỉ các đơn vị gần nhau nhất sẽ được kết nối trong biểu đồ mà bạn tạo. Bạn sẽ thấy rất khó khăn khi thực hiện điều này theo bất kỳ cách nào khác, bởi vì giao cắt đồ thị (không phẳng) sẽ khiến các đơn vị ở quá xa nhau được kết nối không chính xác trong biểu đồ.

Những điều trên chỉ áp dụng nếu bạn đang sử dụng một không gian liên tục (như trong hầu hết các FPS chuyển động tự do); tuy nhiên nếu bạn đã có một lưới bên dưới (biểu đồ phẳng) mà các đơn vị của bạn di chuyển, thì bạn chỉ có thể sử dụng nó để đánh giá kết nối thay thế.

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.