Verwalten Sie eine große Anzahl unabhängiger Akteure in Echtzeit


8

Ich arbeite an einem großen Echtzeit-Strategiespiel - idealerweise mit Tausenden von Einheiten, die gleichzeitig aktiv sind -, aber ich habe Probleme, alle Einheiten gleichzeitig zu verwalten, ohne dass es erstaunlich langsam wird. Das Problem ist, dass es eine Weile dauert, die Positionen und Zustände von allem bei jedem Zeitschritt zu aktualisieren. Kennen Sie Entwurfsmuster / -methoden / -tipps, um dies zu mildern?


1
Obwohl Ihre Frage sehr unterschiedlich zu sein scheint, können Sie denselben Algorithmus wie hier beschrieben verwenden . In diesem Algorithmus muss keine Parralel-Verarbeitung verwendet werden. Mit nur einem Algorithmus erhalten Sie einen hohen Leistungsschub.
Ali1S232

Das Parallelschalten des Updates hilft nicht, wenn Sie beispielsweise zwei Kerne haben und die Hälfte davon bereits zu lange dauert. Wenn Sie jedoch mehrere Kerne zur Verfügung haben und diese noch nicht verwenden, sollte dies helfen. Idealerweise möchten Sie eine Lösung, bei der Sie nicht jedes einzelne Gerät in jedem Zeitschritt aktualisieren müssen, oder eine Möglichkeit, das Update zu vereinfachen, damit Sie dies können. Wie groß ist dieser Zeitschritt? Es ist unabhängig von Zeichenrahmen, oder?
Blecki

4
Das erste, was Sie tun können, ist zu erkennen, dass Ihre Geschwindigkeit nichts mit der Anzahl unabhängiger Akteure zu tun hat . Es hat damit zu tun, was diese Schauspieler tun . Ich kann hunderttausend unabhängige Schauspieler> 60 fps aktualisieren lassen, wenn alles, was sie tun, if not dead position += 1oder ein Schauspieler <1 fps aktualisieren, wenn es in eine Endlosschleife geht. Einige Ihrer Algorithmen - einige Teile dessen, was diese Einheiten tun - sind einfach zu teuer, wie Sie sie tun, und das ist alles. Es gibt wahrscheinlich viele verschiedene mögliche Ursachen und viele verschiedene mögliche Strategien für jede.
Doppelgreener

Antworten:


4

Bei der Messung und Optimierung der Leistung eines solchen großen Systems von Entitäten sind zwei unterschiedliche Aspekte zu berücksichtigen.

Auf der niedrigen Ebene haben Sie die physische Darstellung Ihrer Entitäten, die sich in der Regel auf die Verwendung effizienter Speicherlayouts wie SoA (Strukturen von Arrays) beschränkt, um die Kosten für das Iterieren und Aktualisieren aller aktiven Entitäten zu senken.

Auf der höheren Ebene haben Sie Entscheidungslogik, allgemeine Spiellogik, KI und Pfadfindung. Dies sind alles Aufgaben, die gemeinsam haben, dass sie nicht mit der gleichen Aktualisierungsrate wie Ihr Rendering ausgeführt werden müssen.

Da Sie eine ungleichmäßige Frame-Zeit erhalten würden, wenn Sie den naiven Ansatz wählen, diese Aufgaben nur alle N Frames auszuführen, ist es in der Regel vorteilhaft, die Kosten über mehrere Frames zu amortisieren.

Wenn die Aufgabe inkrementeller Natur ist, können Sie einen Teil des Algorithmus in jedem Frame ausführen und Teilergebnisse für Ihre Aktualisierungen verwenden.

Wenn die Aufgabe weitgehend monolithisch, aber pro Entität trennbar ist, können Sie diese Aufgabe für eine Teilmenge Ihrer Spielentitäten pro Frame ausführen und im Round-Robin-Verfahren zwischen ihnen wechseln. Dies hat den Vorteil, dass die Komplexität von Dingen wie Pfadfindung und KI reduziert wird, da nicht jeder versucht, gleichzeitig zu handeln.

In dem großen taktischen RTS, an dem ich gearbeitet habe, haben wir uns auf robuste Datenstrukturen konzentriert, um die Darstellung auf niedriger Ebene in den Algorithmen auf hoher Ebene abzufragen und die Nachbarn von Spieleinheiten zu finden. Der Aktualisierungsprozess auf niedriger Ebene wirkte sich auf die Absichten der langsam aktualisierenden Simulation auf hoher Ebene aus und lief am Ende auf eine billige Partikelsimulation hinaus, die sich auf Tausende skalierte.


+1. Genau meine Gedanken, insbesondere die Reduzierung der Logik auf verwaltete Gruppen, die nur alle paar Zyklen verarbeitet werden, wobei alle diese Gruppen möglicherweise für eine gleichmäßige Verarbeitung versetzt sind.
Ingenieur

1

Soweit ich mich erinnern kann, haben Sie immer weniger als 10.000 Einheiten pro Spiel. Es gibt kein Spiel, an das ich mich mit mehr als dieser Zahl erinnern kann, obwohl Empire Earth durch einen Hack bis zu 14000 erreichen könnte, aber niemand wird jemals an diesen Punkt gelangen. Nur ein statisches Array von 10.000 Objekten zu haben, scheint mehr als nötig zu sein.

Wie Sie wissen, ist das Iterieren von über 10000 Objekten keine große Sache, aber es kann viel Zeit in Anspruch nehmen, wenn Ihr Algorithmus langsamer als O (n) ausgeführt wird. Wenn Sie beispielsweise versuchen, alle zwei Objekte auf Kollisionserkennung zu überprüfen, dauert es O (n ^ 2), was viel Zeit bedeutet. Sie müssen also Ihre Algorithmen irgendwie brechen. Lassen Sie uns einen Beispielalgorithmus für alles überprüfen, was mir gerade einfällt:

  1. Kollisionserkennung: Für jeden Kollisionserkennungsalgorithmus müssen Sie alle zwei Objekte überprüfen, aber Sie können einige Überprüfungen beim Starten der Schleifen vermeiden. Wie ich in den Kommentaren vorgeschlagen habe, können Sie denselben Algorithmus verwenden, der für diese Frage angegeben wurde . Es ist nicht erforderlich, mehrere Threads oder ähnliches zu verwenden. Selbst mit einem Thread und 4 Regionen reduzieren Sie Ihre Überprüfungen von n * (n-1) auf 4 * (n / 4) ((n-1) / 4). Durch die Optimierung der Anzahl der Regionen können Sie noch bessere Ergebnisse erzielen. Ich denke, wenn man Regionen mit der besten Anzahl verwendet, kann man sogar zu O (n log (n)) gelangen.

  2. Sie müssen für jedes sich bewegende Objekt einen Pfad generieren. Das übliche System, das ich bisher gesehen habe, ist eine sehr einfache Sache: Wenn ein Spieler Einheiten befiehlt, sich irgendwohin zu bewegen, berechnet der Computer seinen Pfad. Danach bewegt er sich in jedem Zyklus, wenn sich das Objekt bewegen kann, wenn es diesen Zyklus nicht überspringen kann. da ist nichts Besonderes. Sie können zwar auch die hier angegebenen Algorithmen ändern, um die Anzahl der Pfadsuchanrufe zu verringern und die Pfadfindung in Echtzeit für jede Gruppe von Einheiten zu ermöglichen. Dies ist jedoch nicht unbedingt erforderlich.

  3. Sie müssen überprüfen, ob eine Kugel oder Bombe oder ähnliches auf eine der Einheiten trifft: Sie können hier dieselben Regionen verwenden, die Sie für die Kollisionserkennung erstellt haben.

  4. Zur Auswahl von Einheiten können Sie auch dieselben Regionen verwenden.

Im Allgemeinen würde ich vorschlagen, ein statisches Array (oder ein dynamisches Array mit reservierter Größe) von höchstens 10.000 oder 20.000 zu verwenden. Verwenden Sie dann etwa 10 oder 15 Schleifen, die jeweils über alle Einheiten iterieren. Das ist ein wirklich großes Array, das alle Einheiten aller Spieler enthält. Jeder Index enthält also sowohl Daten zum Einheitenbesitzer als auch zum Einheitentyp. Sie können auch einige andere Arrays für jeden Spieler erstellen. In jedem Index dieses sekundären Arrays müssen Sie nur Zeiger auf Objekte im Hauptarray speichern.

Wenn Sie weitere Fragen haben, geben Sie Kommentare ein, um sie meiner Antwort hinzuzufügen.


Ich erinnere mich, dass ich in Empires Dawn of the Modern World über 40.000 hatte :). Schade, dass der Renderer nur ungefähr 1000 auf dem Bildschirm halten konnte, bevor sie verschwanden, aber der Rest des Spiels funktionierte gut :).
Verlangsamte

@ Daniel: Das ist keine große Sache, Generäle hatten keine Einschränkungen bei der Anzahl der Einheiten. Ich habe nur ein Maß für die Anzahl der Einheiten angegeben, auf denen meine mathematischen Berechnungen basieren. 40000 und 10000 unterscheiden sich nicht sehr voneinander.
Ali1S232
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.