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?
8
if not dead position += 1
oder 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.Antworten:
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.
quelle
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:
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.
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.
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.
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.
quelle