Chrono in Unreal, Teil 2: Die Architektur der Integration

Software Projekte Agentic Engineering

Teil 1 hat gezeigt, was die Demo tut und warum Project Chrono®. Dieser Teil beschreibt den Aufbau: wie die Bibliothek in die Engine kommt, welche Klassen es gibt, was in einem Bild in welcher Reihenfolge passiert, und nach welchem Muster eigene Modelle an Chrono andocken. Es ist der Teil, den man braucht, wenn man das nachbauen will. Die Fallen stehen in Teil 3.

Die Demo, um die es in der Serie geht. Auf YouTube in 1080p ist die Konsole lesbar.

Die Bibliothek

Chrono 10.0 wird aus den Quellen gebaut, als Shared Library mit dem Fahrzeugmodul, ohne Demos und ohne SIMD. Der letzte Punkt entscheidet: Unreal® kompiliert ohne AVX, und eine Chrono Bibliothek mit SIMD hat an den Schnittstellen andere Strukturlayouts. Dazu Eigen 5 als Header Bibliothek. Die Build Konfiguration ist ein PowerShell® Skript, zwölf Minuten auf einem Ryzen® 5 7600.

Zwei Module, ein Plugin

Die Integration ist ein Unreal Plugin in C++.

Das externe Modul kennt die Include Pfade und Import Bibliotheken und kopiert die Chrono DLLs neben die Engine Binaries. Delay Loading, der übliche Weg für externe DLLs in Unreal, funktioniert nicht, weil Chrono neben Funktionen auch Daten exportiert. Ein kleines Vorlademodul lädt die DLLs deshalb in der frühesten Ladephase des Plugins explizit.

Das Laufzeitmodul hat Ausnahmen und RTTI eingeschaltet, beides braucht Chrono und beides ist in Unreal Modulen standardmässig aus. Die Chrono Header werden an genau einer Stelle eingebunden, in einem Header, der vorher die Makros wegsichert, über die sich beide Welten streiten: check, PI, TEXT und einige mehr. Keine andere Datei bindet Chrono direkt ein.

Die Klassen

ChronoWorldActor, einer pro Level. Er hält das ChSystem, das Terrain und die Bodenparameter, und er ist der einzige Ort, an dem die Physik schreitet. Drei Terrain Modi: eben, starre Heightmap, verformbarer Sandkorridor nach dem Soil Contact Model (SCM). Er verwaltet die Kraftakkumulatoren, über die eigene Modelle Kräfte auf Chrono Körper geben, den Startcountdown für Aufnahmen, das sichtbare Wellengitter, die Steine und den Steg als starre Körper, und den Reset.

ChronoVehiclePawn, das Fahrzeug. Er hält das Chrono Fahrzeug (der schwere 4x4 aus der Modellbibliothek, HMMWV® Modell), die Eingaben aus Enhanced Input, den Autopiloten, die Kameras, die Effekte und die Messwerte für das HUD. Das Chassis Mesh kommt aus 53 Einzelteilen der Chrono Datenverzeichnisse.

ChronoParasail hält Boot, Leine, Schirm und Pilot als Körper im selben System, mit dem Autopiloten des Bootes und den Bootslichtern; ChronoParasailKite ist der sichtbare Schirm mit Pilot als eigener Actor, damit die Drohne ihn als Ziel filmen kann. Das Modell steht in Teil 6.

ChronoDrone war ein kinematischer Actor und ist jetzt ein Chrono Körper mit Rotoren, Regler und Akkumodell, Teil 7.

ChronoPufferfish, ChronoPedestrian, ChronoBeachBall, ChronoBeachGame: kinematische Actors für die Inszenierung. Sie kennen den Weltactor für die Geländehöhe und die Wasseroberfläche, aber nicht Chrono.

ChronoHUD zeichnet die Telemetrie, die Zusatzansichten aus Render Targets und die Scheibentropfen.

Vier Synth Komponenten für Untergrund, Motor, Drohne und den Aussenborder des Bootes erzeugen den Klang im Audiothread aus Parametern, die der Spielthread jedes Bild setzt. Mehr dazu in Teil 4.

Ein Bild, in Reihenfolge

Der Tick des Fahrzeugs ist die eigentliche Integration. In dieser Reihenfolge:

  1. Eingaben lesen: Gamepad oder Autopilot. Der Autopilot ist eine Phasenmaschine mit Zielpunkten und einem Pure Pursuit Regler, dazu Wächter, die in jeder Phase gelten: Fussgänger und Ball mit Bremswegschätzung, Felsen mit Ausweichen auf der freien Seite, Rückwärtssetzen nach Stillstand.
  2. Eigene Kräfte berechnen und in die Akkumulatoren schreiben: Wasser, Anschläge, Schaltentscheidung.
  3. Chrono schreitet, in festen Teilschritten von 2 ms, so viele, wie seit dem letzten Bild vergangen sind, höchstens für 34 ms. Boot, Leine, Schirm, Pilot und Drohne bekommen ihre Kräfte nicht pro Bild, sondern in einem Rückruf pro Teilschritt: die Leine straffen und die Rotoren reagieren schneller, als ein Bild dauert, und eine Kraft, die 16 ms lang konstant bleibt, macht aus einem Gelenk eine Feder.
  4. Transformationen von Chassis und Rädern aus Chrono lesen und auf die Unreal Komponenten setzen. Mehr passiert an der Darstellung nicht.
  5. Effekte aus den Zuständen ableiten: Spuren, Gischt, Scheibentropfen, Ereignisse für den Klang wie Wassereintritt, Landung oder Richtungswechsel der Federung.
  6. Messwerte für das HUD und die Logzeilen für die Leistungsmessung.

Die Kameras und die Drohne ticken danach, mit den fertigen Transformationen.

Die Inszenierung: Ereignisse statt Uhr

Die Demo läuft neun Minuten ohne Eingriff, und sie muss bei jedem Lauf dasselbe zeigen, obwohl die Physik nicht auf die Sekunde reproduzierbar ist: ein Rad greift einmal früher, das Boot strandet einmal später, der Akku ist einmal bei 487 und einmal bei 490 Sekunden bei 15 Prozent. Deshalb hängt die Choreografie nicht an einer Uhr, sondern an Zuständen. Der Fahrzeug Pawn ist der Dirigent. Er kennt die Phasen des Autopiloten, den Zustand von Drohne und Parasail, und er schaltet die Szenen an Ereignissen:

Jeder Trigger hat einen Rückfall: kommt die Drohne nicht auf den Kamm, beginnt der Abend 135 Sekunden nach dem Sinken; landet der Schirm nicht, fliegt die Drohne 240 Sekunden nach der Dämmerung trotzdem los; bleibt das Boot nicht stehen, gilt es acht Sekunden nach der ersten Bodenberührung als gestrandet. Ein Rückfall ist nie die schöne Variante, aber er ist der Grund, warum jeder Lauf zu Ende kommt und ein Video ergibt.

Zwei Dinge haben sich bewährt. Jeder Trigger schreibt eine Logzeile mit Zeit und Zustand, “Dusk begins at t=342.6 (drone on the crest)”, und die Zeile ist der Beweis, nicht das Gefühl beim Zuschauen; die Zeitachse eines Laufs lässt sich aus dem Log allein rekonstruieren. Und die Szenen sind einzeln startbar: eine Konsolenvariable setzt das Fahrzeug in eine Phase, etwa gesunken vor dem Steg an der gemessenen Position, eine andere beschleunigt den Akku, eine dritte zieht Screenshots zu festen Zeiten. Das Ende der Demo lässt sich so in fünf Minuten prüfen statt in neun, und das war bei jedem Umbau der Nacht der Unterschied zwischen einem und vier Läufen. Das ganze Programm mit seinen Phasen und Rückfällen steht in Teil 9.

Koordinaten

Chrono ist rechtshändig, Z nach oben, Meter. Unreal ist linkshändig, Z nach oben, Zentimeter. Die Umrechnung spiegelt Y, skaliert mit 100 und dreht die Quaternion um: aus Chrono (w, x, y, z) wird Unreal FQuat(-x, y, -z, w). Die Lenkung ist in Chrono positiv nach links, in Unreal positiv nach rechts. Alle Umrechnungen stehen in einem einzigen Header, und jede eigene Kraft wird in Chrono Koordinaten formuliert, nie in Unreal Koordinaten und dann konvertiert.

Der Zeitschritt

Feste Schritte von 2 ms, bei 60 Bildern pro Sekunde acht pro Bild. Die Physik hat einen konstanten Zeitschritt, unabhängig davon, ob das Bild in 8 oder in 25 ms fertig wird. Das Aufholen ist auf zwei Bilder begrenzt, der Rest wird fallen gelassen, sonst zieht ein langes Bild immer mehr Teilschritte nach sich. Für Aufnahmen gibt es einen Countdown, in dem das System steht und Drohne und Fahrerfigur warten.

Terrain aus einer Datei

Ein Python Skript berechnet die Insel als 16 Bit Höhenkarte mit 1009 mal 1009 Bildpunkten, ein Meter pro Punkt, Höhe kodiert als 32768 plus Höhe in Metern mal 128. Dieselbe Datei importiert Unreal als Landscape und liest Chrono für das starre Terrain. Für den verformbaren Korridor schreibt das Skript einen Ausschnitt in eine zweite Datei. Das Skript kennt auch den Parcours: Sandbank, Kopfsteinfeld, Buckelpiste, Sprung mit 11 Grad Anlauf, Düne mit 140 m Luvhang, Nordstrand mit 2.5 cm Gefälle pro Meter, das 4.5 m tiefe Becken vor dem Steg.

Das Soil Contact Model rechnet nach Bekker und Janosi: der Reifen drückt in ein Gitter aus Bodenknoten, die Knoten sinken ein, verfestigen sich und schieben Material zur Seite. Drei Bodenvorgaben, als Startwerte gedacht:

VorgabeKphi [Pa/m^n]KcnKohäsion [Pa]ReibwinkelJanosi [m]
Dünensand trocken8e55001.150028 Grad0.025
Strandsand nass5e65e30.9300033 Grad0.012
Fester Boden2e71e40.81e435 Grad0.010

Unterhalb der Wasserlinie gilt gesättigter Sand: keine Kohäsion, Reibungswinkel 19 statt 30 Grad, Bekker Kphi halbiert. Auf SCM Sand brauchen die Reifen starre Kollisionsgeometrie. Der Speicher wächst mit dem Quadrat der Gitterauflösung: 500 mal 924 Meter bei 15 cm sind rund 20.5 Millionen Knoten, und weil Chrono die Grundhöhen als dichte Matrix aus je acht Byte hält, rund 165 MB. Verformt wird nur, was ein Rad berührt: die Knotenzustände liegen in einer Hashtabelle, die mit der befahrenen Fläche wächst, nicht mit dem Korridor.

Im ersten Gang den Hang hoch: Wählhebel 2, die Räder tief im Sand

Eigene Kräfte: das Akkumulator Muster

Chrono kennt kein Wasser und hat im Fahrzeugmodell weder Zuganschlag noch Lenkanschlag. Alles davon ist eigener Code, und alles folgt demselben Muster: die Kraft wird aus dem Chrono Zustand berechnet, in Chrono Koordinaten, und über einen Kraftakkumulator auf den betroffenen Körper gegeben, das Chassis oder eine Radspindel. Chrono integriert sie im nächsten Schritt wie jede andere Kraft. Das Fahrzeug bleibt ein einziges konsistentes System, und die Reaktion, etwa der Zug zur See hin, wenn zwei Räder im Wasser laufen, fällt aus dem Modell heraus, statt gebaut zu werden.

Das Wassermodell: Auftrieb an vier Ecken des Chassis gegen eine Wellenfunktion mit Dünung, Angriffspunkt über dem Schwerpunkt für das aufrichtende Moment, Wasserwiderstand pro Rad auf die eingetauchte Reifenfläche, Schaufelradschub der drehenden Räder mit einem Viertel der Laufflächenkraft, quadratische Dämpfung. Dieselbe Wellenfunktion setzt jedes Bild die Höhen und Normalen eines Gitters im Meer vor der Nordküste, 166 mal 101 Punkte auf 2 Meter Raster über 330 mal 200 Meter, als zweite Sektion des Meeres. Das Fahrzeug schaukelt auf denselben Wellen, die man sieht.

Die Anschläge: ein Zuganschlag als begrenzte Federkraft ab 41 cm Ausfederung, ein Lenkanschlag als begrenztes Drehmoment um den Achsschenkelbolzen ab 45 Grad, mit Dämpfung. Warum es sie braucht, steht in Teil 3.

Die Schaltlogik: das Getriebe läuft im manuellen Modus, der Pawn schaltet nach Motordrehzahl, Turbinendrehzahl und Fahrgeschwindigkeit und kennt eine Wählhebelstellung 2. Wandler, Wellen und Trägheiten rechnet Chrono weiter. Nur die Schaltentscheidung liegt dort, wo sie in einem realen Automaten auch liegt.

Alles aus Skripten

Das Level mit Sonne, Himmel, Landschaft, Weltactor und Startpunkt legt ein Python Skript an, das als Commandlet ohne Editorfenster läuft. Die Materialien, Sand, Wasser, Lack, Rotorscheiben, Scheibentropfen, entstehen als Knotengraphen aus Python, ohne ein einziges Mal im Materialeditor zu klicken. Das Bauen läuft aus der Kommandozeile, die Testläufe über ein Skript, das eine Phase startet und zu festen Zeiten Screenshots zieht, die Aufnahme über ein Skript, das das Spiel mit Countdown startet, OBS Studio® mit Aufnahme dazu, dem Spiel den Fokus zurückgibt und am Ende beides sauber schliesst.

Der Grund ist nicht Eleganz, sondern Prüfbarkeit. Wer eine Umgebung so aufbaut, dass sie skriptgetrieben startet, prüft und aufnimmt, findet Fehler, die im Code unsichtbar sind. Nur was ein Skript machen kann, kann auch ein Agent tun.

Das Kopfsteinfeld von oben: 420 starre Körper, jeder aus dem Levelskript gesetzt

Das HUD als Messgerät

HUD Ausschnitt mit Drehzahl, Gang, Schlupf und Einsinktiefe pro Rad

Das HUD zeigt Drehzahl, Drehmoment und Last, Gang und Wählhebel, Abtriebsmoment, Pedale, Schlupf und Einsinktiefe pro Rad, Verbrauch, Schwimmwinkel, dazu Bildzeit und aus der NVML Bibliothek des Grafiktreibers Auslastung, Speicher und Temperatur der Grafikkarte. Dazu zwei Logzeilen: eine pro Sekunde mit mittlerer und maximaler Bildzeit, GPU Zeit und Chrono Zeit, und eine pro Ausreisser über 28 ms mit den Kosten der einzelnen Tick Abschnitte. Mit diesen beiden Zeilen wurden alle Leistungsprobleme des Tages gefunden, und die meisten Fahrprobleme mit dem HUD.

Leistung

Im gezeigten Lauf 18.6 ms mittlere Bildzeit, also 54 Bilder pro Sekunde, bei 13 ms GPU und 13 ms Chrono; auf dem Parcours und der Düne sind es 57 Bilder, am Nordstrand 48. Der Weg dahin: Lumen aus, Reflexionen als Screen Space Reflections, Zusatzansichten ohne globale Beleuchtung, Spuren nach 120 Sekunden verwerfen, Distance Field Ambient Occlusion aus. Die Physik wurde nie beschnitten. Für eine Fahrsimulation ist das die richtige Reihenfolge: messen, am Bild sparen, nie an der Physik.

Was dabei alles schiefging, steht in Teil 3. Die Modelle von Boot, Leine und Schirm in Teil 6, die Drohne in Teil 7.

Schlüsselwörter: Project Chrono Unreal Integration, Unreal Engine 5.8 Plugin C++, Chrono::Vehicle Architektur, Soil Contact Model, Bekker Janosi, Kraftakkumulator, fester Zeitschritt Physik, Heightmap Landscape, Mehrkörpersimulation, Serious Gaming, Simulation Beratung Schweiz, swissEmbedded

Eine Physikbibliothek in eure Umgebung bringen? Wir integrieren Simulationskerne in Visualisierung und Prüfstände, mit sauberen Schnittstellen und messbarer Leistung.

Kontaktieren Sie uns →

Unreal® und Unreal Engine® sind Marken oder eingetragene Marken von Epic Games, Inc. in den USA und anderswo; diese Artikel sind von Epic Games weder unterstützt noch geprüft. Project Chrono ist ein Open-Source-Projekt der University of Wisconsin-Madison und des Politecnico di Milano unter BSD-3-Lizenz.