RTS: Sicht der Einheit im Nebel des Krieges


7

Ich mache ein RTS-Spiel und wie die meisten RTS können Sie nur dann sehen, was in einem Teil einer Karte vor sich geht, wenn Sie dort eine Einheit haben.

Ich habe nur wenige Ideen, wie dies erreicht werden kann, aber es gibt Probleme damit.

Probleme sind:

  • Der Algorithmus muss effizient sein.
  • Ich muss Spieler (über das Netzwerk) benachrichtigen, wenn ein Feind in Sicht kommt.
  • Wie berücksichtige ich Hindernisse, die die Sicht behindern (z. B. Klippen)?

Ein naiver Ansatz wäre wie folgt:

// 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

Führen Sie diese Berechnung dann in jedem Frame aus, vergleichen Sie die Ergebnisse mit dem vorherigen Frame und senden Sie Ereignisse wie "erkannte feindliche Einheit" aus.

Ich habe ein Benchmarking durchgeführt, und die Verwendung eines solchen Ansatzes kann bis zu 2/3 eines Frames dauern, was nicht akzeptabel ist.


Ich habe einen besseren Weg gefunden, um zu berechnen, welche Einheiten sichtbar sind. Bei diesem Ansatz wird eine Sichtbarkeitskarte verwendet.

  • Unterteilen Sie die Karte in N × M-Zellen.
  • Jede Zelle ist durch IDs von Spielern gekennzeichnet, die die Zelle sehen können.
  • Um festzustellen, welche Spieler eine Einheit sehen können, muss ich nur Zellen überprüfen, die eine bestimmte Einheit enthalten.

Ich weiß jedoch nicht, wie ich die Karte überhaupt füllen soll. Ich könnte alle Einheiten durchlaufen und einen Kreiszeichnungsalgorithmus verwenden.

Aber ich muss die Sichtbarkeitskarte in jedem Frame neu zeichnen, damit dies nicht viel effizienter aussieht als der erste Ansatz. Es sei denn, ich aktualisiere es mit einer niedrigeren Rate, aber dann gibt es ein Problem mit sich schnell bewegenden Einheiten.

Und wie erkenne ich, wann eine Einheit in Sichtweite des Spielers kommt, außer dass ich jede Einheit durchlaufe und mit dem vorherigen Frame vergleiche?


Wie wird dies in modernen RTS wie StarCraft 2 gemacht?


1
Ist dies ein Duplikat von Wie optimiere ich 2D-Sichtbarkeitskegelberechnungen ? Wenn nicht, was ist anders? Schauen Ihre Einheiten gleichzeitig in alle Richtungen?
Anko

Ja, es sieht ähnlich aus. Mein Fall ist ein bisschen anders (Einheiten sehen im Kreis statt Kegel und Einheiten eines Spielers teilen die Vision), aber ich denke, der allgemeine Ansatz ist der gleiche.
Hedede

Antworten:


2

Sie sollten eine Sichtbarkeitskarte mit mindestens einem Bit pro Zelle und Team haben.

Erzwingen Sie nicht brutal eine vollständige Kartenaktualisierung für jeden Frame. Nur wenn sich der Standort und / oder die Sichtweite eines Geräts ändert. Überprüfen Sie vor und nach der Bewegung seine Sichtbarkeit anhand der einzelnen Teambits. Wenn er für ein Team nicht sichtbar war und sichtbar wird. Senden Sie eine Veranstaltung.


Das scheint einfach zu sein, bis auf eine Sache. Was tun mit Zellen, die die Einheit nicht mehr sieht, wenn sie sich bewegt? Mit "neuen" Zellen ist es einfach, ich kann sie einfach auf "1" setzen, aber ich kann sie nicht einfach auf "0" setzen, wenn die Einheit sie nicht mehr sieht, weil es möglicherweise andere Einheiten gibt.
Hedede

1
Sie zeichnen Ihre Sichtbarkeitskarte neu, jeden Rahmen, in dem sich eine Einheit bewegt, oder den Sichtbarkeitsbereich einer stationären Einheit. Dies kann optimiert werden, indem ein maximaler Sichtbarkeitsbereich bekannt ist und die Karte dann nur nach Einheiten aktualisiert wird, die in diesen Bereich fallen. Sie haben auch Recht mit dem Timing. Sie werden dies wahrscheinlich sowieso nicht in der Hauptschleife tun. Ein langsamerer Timer sollte in Ordnung sein. 4-6x / Sek vielleicht?
RobStone

2

Die Art und Weise, wie ich an der Universität unterrichtet wurde, bestand darin, zuerst Ihre Einheiten aufzubewahren anhand ihres Zellenstandorts im Speicher . Sie haben also ein quadratisches Gitter von Zellen (möglicherweise müssen Sie mit der Zellengröße herumspielen, um herauszufinden, welche am effizientesten ist), und wenn sich eine Einheit bewegt, aktualisieren Sie ihre Zelle (wenn sie sich ändert, entfernen Sie sie aus ihrer alten Zelle und füge es dem neuen hinzu). Beachten Sie, dass Sie nur Zeiger auf Einheiten verschieben, nicht auf die Objekte selbst. Dies ist also sehr effizient.

Normalerweise verwende ich eine TreeMap-Struktur, deren Schlüssel ein Punkt (x, y) ist und deren Wert eine Liste von Einheiten innerhalb dieses Punkts ist. Beachten Sie, dass Sie Ihre Einheiten auch als gerades Array speichern möchten, wenn Sie ihre Aktualisierungsschritte (oder das Rendern) durchlaufen möchten. Sie können diese Struktur weiter nach "Team" segmentieren. Sie müssen Ihre eigenen Einheiten nicht gegeneinander prüfen, wenn es um die Sichtbarkeit geht, sondern nur die Einheiten anderer Teams.

Mit dieser Methode nehmen Sie bei der eigentlichen Überprüfung den Betrachtungsradius jeder Einheit und berechnen alle möglichen Zellen, die sie möglicherweise sehen können, basierend auf der Zelle, in der sie sich befinden (aufgerundet). Sie sehen sich dann die Einheiten der anderen Teams in diesen Zellen an und prüfen, ob der Abstand zwischen ihnen innerhalb des Betrachtungsradius liegt. Sobald ein Feind entdeckt wurde, speichern Sie ihn in einer Liste, damit Sie ihn nicht erneut überprüfen müssen. (Die Liste könnte auf die gleiche Weise auch nach Zellen segmentiert werden, um die Liste und die Anzahl der Überprüfungen zu verkürzen, die erforderlich sind, um die Einheit dort zu finden.)

Ich habe diese Methode verwendet, um Kollisionen zwischen Feuerwerkspartikeln in einem kleinen Feuerwerkssimulator zu erkennen, den ich erstellt habe. Die Anzahl der Überprüfungen, die ich durchführen musste, wurde von durchschnittlich 500.000 auf 500 verringert.

Vorteile: Extrem effizient.

Nachteile: Geringe Erhöhung des Arbeitsspeichers aufgrund der Speicherung von Sekundärzeigern.
Fehler im Code können dazu führen, dass Einheiten unbesiegbar / unsichtbar sind.

Wenn Sie ein Beispielprojekt benötigen, bei dem alles funktioniert, kann ich etwas für Sie reparieren. Es ist viel einfacher als Sie denken.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.