RTS: Tầm nhìn đơn vị trong sương mù chiến tranh


7

Tôi đang làm một trò chơi RTS, và giống như hầu hết các RTS khác, bạn có thể thấy những gì đang diễn ra trong một phần của bản đồ chỉ khi bạn có một đơn vị ở đó.

Tôi có vài ý tưởng làm thế nào để thực hiện điều này, nhưng có vấn đề với chúng.

Vấn đề là:

  • Thuật toán phải hiệu quả.
  • Tôi cần thông báo cho người chơi (qua mạng), khi kẻ thù xuất hiện.
  • Làm thế nào để tôi giải thích cho các chướng ngại vật cản trở tầm nhìn (như vách đá).

Cách tiếp cận ngây thơ sẽ như thế này:

// pseudocode
func calculate_visibility:
   vector<Bitfield> visible;

   for all units on map:
        let unit = enumerated unit

        for all human players:
             let player = enumerated player

             for all units of player:
                  let player_unit = enumerated unit

                  if player_unit sees unit
                       visible[unit.id][player.id] = true
                       process next player

Và sau đó chạy phép tính này từng khung, so sánh kết quả với khung trước đó và gửi các sự kiện, như đơn vị kẻ thù đã phát hiện ra.

Tôi đã thực hiện một số điểm chuẩn và sử dụng cách tiếp cận như thế này có thể mất tới 2/3 khung hình, điều này không thể chấp nhận được.


Tôi đã tìm ra một cách tốt hơn để tính toán đơn vị nào có thể nhìn thấy. Cách tiếp cận này là sử dụng bản đồ tầm nhìn.

  • Chia nhỏ bản đồ thành các ô N × M.
  • Mỗi ô được đánh dấu bằng ID của người chơi có thể nhìn thấy ô.
  • Để xác định người chơi nào có thể thấy một đơn vị, tôi chỉ cần kiểm tra các ô có chứa đơn vị đã cho.

Tuy nhiên, tôi không biết làm thế nào để điền vào bản đồ ở nơi đầu tiên. Tôi có thể lặp qua tất cả các đơn vị và sử dụng thuật toán vẽ vòng tròn.

Nhưng tôi sẽ phải vẽ lại bản đồ hiển thị mọi khung hình, vì vậy điều này trông không hiệu quả hơn nhiều so với cách tiếp cận đầu tiên. Trừ khi tôi cập nhật nó ở tốc độ thấp hơn, nhưng sau đó có vấn đề với các đơn vị chuyển động nhanh.

Và làm cách nào để phát hiện khi đơn vị xuất hiện trong tầm nhìn của người chơi, ngoại trừ việc lặp qua mọi đơn vị và so sánh với khung trước đó?


Làm thế nào điều này được thực hiện trong RTS hiện đại, chẳng hạn như StarCraft 2?


1
Đây có phải là bản sao của Làm cách nào để tối ưu hóa tính toán hình nón 2D không? Nếu không, có gì khác? Các đơn vị của bạn nhìn theo tất cả các hướng đồng thời?
Anko

Vâng, nó trông tương tự. Trường hợp của tôi hơi khác một chút (các đơn vị nhìn theo vòng tròn, thay vì hình nón và các đơn vị của một người chơi chia sẻ tầm nhìn), nhưng tôi nghĩ cách tiếp cận chung là giống nhau.
Hedede

Câu trả lời:


2

Bạn nên có một bản đồ hiển thị với ít nhất một bit cho mỗi ô cho mỗi nhóm.

Đừng vũ phu cập nhật bản đồ đầy đủ từng khung. Chỉ khi vị trí và / hoặc phạm vi tầm nhìn của một đơn vị thay đổi. Trước và sau khi di chuyển, kiểm tra tầm nhìn của anh ấy đối với từng đội. Nếu anh ta không thể nhìn thấy một đội và trở nên hữu hình. Gửi một sự kiện.


Điều đó dường như đủ dễ dàng, ngoại trừ một điều. Khi đơn vị di chuyển, phải làm gì với các ô mà đơn vị không còn nhìn thấy? Với các ô "mới" thật dễ dàng, tôi chỉ có thể đặt chúng thành "1", tuy nhiên, tôi không thể chỉ đặt chúng thành "0" khi đơn vị không còn nhìn thấy chúng nữa, vì có thể có các đơn vị khác.
Hedede

1
Bạn vẽ lại bản đồ khả năng hiển thị của mình, bất kỳ khung hình nào mà đơn vị di chuyển hoặc phạm vi hiển thị của đơn vị đứng yên thay đổi. Điều này có thể được tối ưu hóa bằng cách biết phạm vi hiển thị tối đa và sau đó chỉ cập nhật bản đồ theo các đơn vị rơi vào đó. Ngoài ra, bạn đúng về thời gian. Bạn có thể sẽ không làm điều này trong vòng lặp chính. Một bộ đếm thời gian chậm hơn sẽ ổn. Có thể 4 - 6 lần / giây?
RobStone

2

Vâng, cách tôi được dạy ở trường đại học là lần đầu tiên lưu trữ các đơn vị của bạn trong bộ nhớ dựa trên vị trí di động của chúng. Vì vậy, bạn có một lưới ô vuông (bạn có thể cần phải chơi xung quanh với kích thước ô để tìm ra ô nào hiệu quả nhất) và bất cứ khi nào một đơn vị di chuyển, bạn cập nhật ô của nó (nếu nó thay đổi, hãy xóa nó khỏi ô cũ của nó và thêm nó vào cái mới) Lưu ý, bạn chỉ di chuyển con trỏ đến các đơn vị, không phải chính các đối tượng, vì vậy điều này rất hiệu quả.

Tôi thường sử dụng cấu trúc TreeMap, có khóa là Điểm (x, y) và giá trị là danh sách các đơn vị trong điểm đó. Lưu ý rằng bạn muốn lưu trữ các đơn vị của mình dưới dạng một mảng thẳng khi cần lặp lại qua các bước cập nhật (hoặc kết xuất) của chúng. Bạn có thể phân đoạn cấu trúc này theo 'đội'. Bạn không cần phải kiểm tra các đơn vị của riêng mình với nhau khi nhìn thấy, chỉ các đơn vị trong các đội khác.

Sử dụng phương pháp này, khi tiến hành kiểm tra thực tế, bạn lấy bán kính xem của mỗi đơn vị và tìm ra tất cả các ô có thể mà họ có thể nhìn thấy dựa trên ô nào họ đang ở (làm tròn lên). Sau đó, bạn nhìn vào các đơn vị của các đội khác trong các ô đó và kiểm tra xem khoảng cách giữa chúng có nằm trong bán kính xem không. Khi kẻ thù bị phát hiện, bạn lưu trữ nó trong danh sách để không cần kiểm tra lại. (Danh sách cũng có thể được phân chia theo ô theo cùng một cách để rút ngắn danh sách và số lượng kiểm tra cần thiết để tìm đơn vị trong đó).

Tôi đã sử dụng phương pháp này để phát hiện va chạm giữa các hạt pháo hoa trong một mô phỏng pháo hoa nhỏ mà tôi đã thực hiện. Nó thu hẹp số lượng kiểm tra tôi cần để thực hiện trung bình từ 500.000 đến 500.

Ưu điểm: Cực kỳ hiệu quả.

Nhược điểm: Tăng RAM nhỏ, do lưu trữ con trỏ thứ cấp.
Lỗi trong mã có thể khiến các đơn vị trở nên bất khả chiến bại / vô hình.

Nếu bạn cần một dự án ví dụ với tất cả hoạt động này, tôi có thể sửa một cái gì đó cho bạn. Nó dễ dàng hơn nhiều so với bạn nghĩ.

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.