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?
quelle
Antworten:
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.
quelle
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.
quelle