Wie mag eine Engine Quellprozessentitäten?

9

In der Source-Engine (und es ist Antecessor, Goldsrc, Quake's) sind die Spielobjekte in zwei Typen unterteilt: Welt und Entitäten. Die Welt ist die Kartengeometrie und die Entitäten sind Spieler, Partikel, Sounds, Scores usw. (für die Source Engine).

Jede Entität hat eine Denkfunktion , die die gesamte Logik für diese Entität übernimmt.

Wenn also alles, was verarbeitet werden muss, von einer Basisklasse mit der Funktion think stammt, kann die Spiel-Engine alles in einer Liste speichern und in jedem Frame eine Schleife durchlaufen und diese Funktion aufrufen.

Auf den ersten Blick ist diese Idee vernünftig, aber sie kann zu viele Ressourcen beanspruchen, wenn das Spiel viele Entitäten hat.

Wie kümmert sich eine Engine wie Source um die Spielobjekte (Prozess, Aktualisierung, Zeichnen usw.)?

JulioC
quelle
2
Warum ist es wichtig, wie <some commercial engine>es geht?
Die kommunistische Ente
8
@ The Communist Duck, ich denke, die eigentliche Frage hier ist eher, wie ein erfolgreicher Motor das macht, damit ich von ihnen lernen kann.
Subb

Antworten:

5

Nun, es gibt so gut wie keinen anderen Weg, dies zu tun - Sie müssenthink() jede Entität mindestens alle paar Frames durchlaufen und aufrufen .

Sie könnten Entitäten auf ihren eigenen Thread setzen, aber dann haben Sie den ganzen Alptraum der Zustandssynchronisation, der sich definitiv nicht lohnt.

Auf den ersten Blick ist diese Idee vernünftig, aber sie kann zu viele Ressourcen beanspruchen, wenn das Spiel viele Entitäten hat.

Deshalb setzt die Source-Engine die Anzahl der Entitäten, die gleichzeitig existieren können, stark : 4096 Entitäten, von denen nur die Hälfte (2048) vernetzt werden kann. Wenn Sie eine dieser Grenzen überschreiten, stürzt das Spiel ab.

Aus diesem Grund wird beim Erstellen einer Karte empfohlen, nicht mehr als 800 Objekte zu verwenden.

BlueRaja - Danny Pflughoeft
quelle
Sind 2 ^ 12 Funktionsaufrufe nicht immer noch eine GROSSE Nummer für jeden Frame?
JulioC
@ Júlio: Nun, bei 60 fps sind das 246.000 Funktionsaufrufe pro Sekunde - das ist viel, aber auf der heutigen Hardware definitiv machbar. Denken Sie jedoch daran, dass dies das absolute Maximum ist, das zulässig ist, bevor die Quell-Engine abstürzt. In der Regel befinden sich weit weniger Objekte auf einer Karte.
BlueRaja - Danny Pflughoeft
5
Die Entitäten haben eine nächste Denkzeit, die Denkfunktion wird nicht als Everyframe und nicht für alle Entitäten bezeichnet. Ich erinnere mich, dass für Beben 2 die minimale Denkzeit 0,1 (100 ms) betrug, nur 10 fps für die Verarbeitung von Entitäten.
Bcsanches
"Sie könnten Entitäten auf ihren eigenen Thread setzen, aber dann haben Sie den ganzen Alptraum der Zustandssynchronisation, der sich definitiv nicht lohnt." Überprüfen Sie diese Killzone 4-Folien, wenn Sie der Meinung sind, dass es sich nicht lohnt: de.slideshare.net/jrouwe/…
Tara
3

Diese Schritte, die Sie erwähnen, werden höchstwahrscheinlich in separaten Engines ausgeführt. Es ist nur so, dass einfache Spiel-Engines sie normalerweise in einem Durchgang haben. Deine Sequenz

for each object
    do physics
    do game logic
    draw

wird

call physics subsystem
call game logic subsystem
call drawing subsystem

Physics Engine kümmert sich um Positionen und Größen.

Game Logic Engine kümmert sich darum zu interpretieren, was Physics Engine geändert hat (er könnte einige Wegpunkte blockieren ...), welche Ziele Charaktere haben und welches Verhalten sie tun sollten. Er führt geplante Skripte aus (diese Denkfunktion ).

Drawing Engine zeichnet, welche Objekte sichtbar sind, und er weiß, welche Objekte sichtbar sind, da Quake Engines hier betrügen (siehe Abschnitt Zeichnen).

Mein Rat an Sie ist, eher zu untersuchen, wie Simulationen durchgeführt werden, als Spiel-Engines. Es gibt eine riesige Popkultur in Bezug auf die Spieleentwicklung und Spiel-Engines werden in zwingenden Sprachen erstellt (aufgrund von Tradition und Geschwindigkeit). Daher war es für mich aufschlussreicher, gute Lehrbücher (eher Theorie) zu bekommen und dann Motoren (Praxis) zu betrachten, anstatt stundenlang Motoren und Rätsel zu betrachten, wie sie es taten.

Physik

Die ganze Vorstellung, alle Entitäten zu iterieren und {think, draw} zu tun, wird wahrscheinlich zu Problemen führen. Es wird Konflikte geben und so weiter. Ich glaube, Valve hat Havok und ich denke, Havok kümmert sich um eine ausreichend korrekte Physik.

Überlegen

Die Denkfunktion wird ausgeführt, wenn eine Zeit in einem Spiel der Zeit in nextthink entspricht . Dies funktioniert in der Quake-Engine auf diese Weise, und die Quake-Engine ist die Basis für Half Life-Motoren. Es wird NICHT jedes Mal ausgeführt.

Intern sollte es eine einfache Iteration durch eine Liste von Entitäten sein und prüfen, ob die Zeit vergangen ist, um die think-Funktion aufzurufen. Die zeitliche Komplexität ist O (N), wobei N die Anzahl der Entitäten ist.

Wenn es eine sehr große Anzahl von Entitäten gibt Sie sollten messen, um wie viel es die fps verbessert. Beachten Sie, dass es sich aufgrund des Amdahlschen Gesetzes möglicherweise um eine unsichtbare Beschleunigung handelt. Ich meine, Sie iterieren einfach durch alle Elemente und verringern und überprüfen eine Zahl.

Ich würde es beschleunigen, indem ich Entitäten nach nextthink sortiere (Liste von Zeigern auf Entitäten erstellen und jedes Mal sortieren; kein Array von Entitäten, da Entitäten ihre nächste Denkweise jederzeit ändern könnten, sodass die Neuanordnung in Array O (N) anstelle von O ( 1) in Liste).

Sie sollten sich auch den O (1) Scheduler unter Linux ansehen .

Zeichnen

Der Motor zeichnet, was ungefähr aus dem Bereich sichtbar ist, in dem sich die Kamera befindet. Das Spiellevel ist die Aufteilung in einen Baum, und ein Bereich ist das Blatt dieses Baumes. Ich werde Sie nicht mit Details darüber belästigen ... Wenn also eine Entität sichtbar ist, wird sie in eine Reihe sichtbarer Entitäten eingefügt und sie werden gezeichnet.

Sie speichern, welche Bereiche potenziell sichtbare Bereiche sind. Es heißt "Potentialy Visible Set", kurz PVS . Es gibt eine Visualisierung von PVS , die grüne Kapsel ist der Spieler und um ihn herum wird gerendert, was sein PVS enthält.

user712092
quelle
2

Wenn also alles, was verarbeitet werden muss, von einer Basisklasse mit der Funktion think stammt, kann die Spiel-Engine alles in einer Liste speichern und in jedem Frame eine Schleife durchlaufen und diese Funktion aufrufen.

Auf den ersten Blick ist diese Idee vernünftig, aber sie kann zu viele Ressourcen beanspruchen, wenn das Spiel viele Entitäten hat.

Tatsächlich ist es normalerweise weniger wünschenswert, alles auf eine große Liste zu setzen. Wenn Sie die Entitäten in Listen gruppieren, die beispielsweise auf ihrem Typ basieren, können Sie die Verarbeitung besser auf mehrere Threads verteilen. Wenn Sie beispielsweise wissen, dass alle Entitäten vom Typ Foo während der Simulationsphase niemals mit anderen Entitäten interagieren, können Sie sie vollständig auslagern. Wenn sie wohl oder übel auf einer großen Singularliste verstreut wären, wäre dies viel schwieriger.

Zu diesem Zeitpunkt müssen Sie nicht unbedingt alles von einer gemeinsamen Basisklasse ableiten. Die Quelle geht mit dem Missbrauch der Vererbung für das, was sonst in dieser Hinsicht als Daten implementiert werden könnte, ziemlich über Bord.

Sie haben natürlich immer eine Obergrenze für die Anzahl der Entitäten, die Sie pro Frame verarbeiten können, selbst wenn Sie anfangen, Arbeiten auf andere Kerne zu verlagern. Daran führt kein Weg vorbei. Sie müssen lediglich eine Vorstellung davon haben, wie hoch diese Grenze in Ihrer Implementierung ist, und Maßnahmen ergreifen, um sie zu verringern (ordnungsgemäße Auswahl der Verarbeitungsphasen für Objekte, die sie nicht benötigen, um eine Übergranularität der Objekte zu vermeiden) cetera).

BlueRaja - Danny Pflughoeft
quelle
1

Was Sie berücksichtigen müssen und dies nach dem Gedankengang früherer Antworten, ist, dass Ihre Leistung auch darauf ankommt, wann und wie Sie diese Denkfunktionen aufrufen.

Wenn Sie sich den Link ansehen, den Sie in der Quell-Engine gepostet haben, können Sie auch lesen, dass Sie Denkzeiten und unterschiedliche Denkkontexte für jede Ihrer Entitäten einrichten können. Abgesehen von der offensichtlichen harten Grenze, auf die bereits jemand hingewiesen hat, ist dies der Schlüssel zu einer höheren Leistung mit einer höheren Anzahl von Entitäten, indem entweder schrittweise Aktualisierungen erstellt werden, die die leistungshungrige Verarbeitung über mehrere Ausführungsrahmen verteilen, oder indem nicht benötigte Verarbeitung je nach aktuellem Kontext entfernt wird (dh Entitäten, die zu weit entfernt sind oder außerhalb der Wahrnehmung des Spielers liegen, müssen nicht benötigt werden) Das gleiche Maß an "Think Detail" wie Charaktere, die sich in der Nähe eines Spielers befinden, sieht einfach keinen Charakter, der 2 Meilen entfernt ist und seine Nase pickt.

Abhängig von Ihrer spezifischen Spiellogik und -situation gibt es weitere spezifischere Optimierungsstufen.

Sergio Franco
quelle
"Ein Spieler wird einfach keinen Charakter sehen, der 2 Meilen entfernt ist und seine Nase pickt." Haha! Aber warum weist niemand darauf hin, dass die Verwendung virtueller Funktionen sehr langsam ist?
Tara