AS3-Tutorium:Physics:3D-Physics-Engines
Im Folgenden erfahren Sie, welche Möglichkeiten Ihnen für die Arbeit mit vorhandenen 3D-Physics-Engines in Actionscript 3 gegeben sind. Wer sich die Grundlagen der Physik aneignen will oder gar eine eigene (2D)Physics-Engine bauen will, wird der Artikel AS3-Tutorium:Physics:Grundlagen empfohlen.
Grundlegendes Prinzip
Physics-Engines stellen lediglich Algorithmen für die physikalische Berechnung, jedoch keine Routinen zur Darstellung von Objekten, zur Verfügung! Da Flex oder Flash selber auch keine echten 3D-Engines besitzen, brauchen Sie für die Darstellung eine zusätzliche Bibliothek. Im Code müssen Sie selbständig die physikalische Objekte mit den darstellenden Objekten verbinden, da beide sonst unabhängig voneinander agieren würden. Weiterhin müssen sie beachten, dass die physikalische Berechnung so kompliziert ist, dass nicht alle Objekte die Sie darstellen können, auch physikalisch in ihrer ganzen Komplexität berücksichtigen können. Auf den Umgang mit den Libraries in AS3 wird später genauer eingegangen.
3D-Engines
Papervision3D
Papervision3D eine sehr populäre 3D-Engine für Actionscript. Sie kann alle wichtigen primitive Objekte wie Quader, Kugeln, Ebenen und Zylinder darstellen. Auch komplexe Objekte, beispielsweise 3D-Models aus Maya sind mit einer Hilfsklasse, dem Collada, sehr einfach einzubinden. Man kann koordinatengenau Punktlichter setzen und verschiedene Materialen, wie beispielweise das einfache FlatShadeMaterial oder das WireFrameMaterial auf seine Objekte anwenden. Und auch sonst ist Papervision eine recht mächtige Engine. Sie unterstützt beispielsweise die komplexe Rechnung mit Matrizen und stellt uns mit der Matrix3D eine Klasse mit vielen nützlichen Methoden zur Verfügung.
Die Dokumentation von Papervision3D fällt etwas spärlich aus und verzichtet auf eine ausführliche Beschreibung der Methoden. Da aber einige gute Tutorials im Web zu finden sind fällt der Einstieg nicht schwer.Insgesamt ist die Arbeit mit Papervision sehr simpel und effektiv: Man erstellt seine Objekte; fügt diese der Szene hinzu, konfiguriert den Renderer, und los geht’s.
Sonstige Engines
Sandy3D
Away3D
Alternativa
3D-Physics-Engines
WOW-Engine
WOW, nicht zu verwechseln mit World of Warcraft, ist die verbreitetste aller 3D-Physics-Engines für Actionscript. Man stößt bei der Internet-Recherche fast nur auf Beispiele mit der WOW-Engine, was nicht ganz verständlich ist.
Die Arbeit mit der WOW-Engine ist durchweg intuitiv und komfortabel. Man erstellt seine Objekte, modifiziert ggbf. die Elastizität o.ä., verbindet sie mit der Darstellung und aktualisiert sie jedes Frame.
Die Engine hat jedoch einige große Nachteile:
1. Die Performance lässt zu wünschen übrig. Schon nicht allzu viele gleichzeitig zu berechnende Objekten zwingen die Physics-Engine in die Knie.
2. Es gibt keine Kollisionsberechnung von Quader, Würfeln.
3. Auch sonstige Feinheiten wie Federn o.ä. fehlen.
Was dann überhaupt noch übrig bleibt: Die Kollision von Kugeln sowie einfache Gravitation; bei schlechter Performance. Es ist daher unverständlich, warum die WOW-Engine einen solchen Status erlangt hat. Eine bessere Alternative wird im Folgenden dargestellt.
flashjiglib
Die flashjiglib ist aus unerklärchlich Gründen in den Weiten des World Wide Webs sehr schwer zu entdecken. Sie ist eine Portierung der jiglib aus C++ und deutlich ausgereifter als die WOW-Engine:
Die Arbeit mit der flashjiglib ist ebenso intuitiv, die Performance deutlich besser. Die jiglib unterstützt die Kollision von Kugeln, Quadern, begrenzten Ebenen und Kapseln. Die physikalische Berechnung von Federn ist ebenfalls möglich. Last but not least bringt die flashjiglib Komfortfunktionen mit sich, die es beispielsweise erlauben, die Arbeit mit den 3D-Render-Engines zu erleichtern. Eine Demo können sie hier ausprobieren.
Tutorial
Im Folgenden werden wir eine einfache 3D-Physics-Szene mit Papervision3D und flashjiglib bauen.
package {
import flash.display.Sprite;
import flash.events.Event;
import jiglib.geometry.JBox;
import jiglib.math.JNumber3D;
import jiglib.physics.RigidBody;
import jiglib.plugin.papervision3d.Papervision3DPhysics;
import jiglib.plugin.papervision3d.Pv3dMesh;
import org.papervision3d.cameras.Camera3D;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.WireframeMaterial;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.render.LazyRenderEngine;
import org.papervision3d.scenes.Scene3D;
import org.papervision3d.view.Viewport3D;
public class Physics3DTutorial extends Sprite
{
private var physics:Papervision3DPhysics;
private var camera3D:Camera3D;
private var viewport3D:Viewport3D;
private var renderEngine:LazyRenderEngine;
private var scene3D:Scene3D;
public function Physics3DTutorial()
{
initPapervision();
initJiglib();
initObjects();
}
Man kann den Programmfluß in 4 wichtige Schritte aufteilen. Drei davon sind Initialisierungsschritte und werden direkt im Konstruktor aufgerufen. (( Der vierte Schritt, die Aktualisierung der Engines pro Frame, wird über einen EventListener auf ENTER_FRAME aufgerufen. Der EventListener wird bei erfolgreicher Initalisierung, also nach der Erzeugung aller Objekte hinzugefügt. ))
private function initPapervision () :void {
this.camera3D = new Camera3D();
this.viewport3D = new Viewport3D(640, 480);
this.scene3D = new Scene3D();
this.renderEngine = new LazyRenderEngine(this.scene3D, this.camera3D, this.viewport3D);
this.addChild(this.viewport3D);
}
Hier wird die Papervision3D-Engine zur Darstellung von dreidimensionalen Objekten intialisiert. Dafür braucht man folgende Elemente:
- Die Kamera. Sie ist der Blickpunkt auf die Szene. Du kannst es dir also genau so vorstellen wie eine Filmkamera. Man sie beispielsweise nach oben verschieben um eine „Draufsicht“ auf die Szene zu erreichen.
- Die Szene. Sie enthält alle zu renderenden Objekte. Wenn du später ein Objekt erzeugst musst du es der Szene per addChild() übergeben.
- Der Viewport ist die Zeichenfläche für die Darstellung. Den Viewport musst du immer deiner darstellenden Klassen mitt addChild übergeben. In unserem Falle ist dies das eigene Objekt.
- Der Renderer. Ihm musst die restlichen Elemente übergeben damit er weiß was er wie rendern muss. Außerdem musst du ihn später pro Schritt updaten.
private function initJiglib():void
{
physics = new Papervision3DPhysics(this.scene3D, 7);
physics.engine.setGravity(new JNumber3D(0, -1, 0));
}
Die Initialisierung der Physics-Engine ist recht einfach; wir müssen lediglich ein Papervision3DPhysics-Objekt erzeugen. Dieses Objekt muss wissen auf welche Szene sie einen Einfluss hat, in unserem Falle „scene3D“. Anschließend können wir noch Einstellungen vornehmen und so z.B. die Gravitation verändern.
private function initObjects():void
{
var pointLight3D:PointLight3D = new PointLight3D();
pointLight3D.x = 1000;
pointLight3D.y = 1000;
pointLight3D.z = -1000;
var materialsList1 :MaterialsList = new MaterialsList({all: new FlatShadeMaterial(pointLight3D)});
var boxPV:Cube = new Cube(materialsList1, 100, 10, 40 );
var boxJL:JBox = new JBox(new Pv3dMesh(boxPV), 100, 10, 40);
boxJL.y = 30;
boxJL.rotationY = 20;
boxJL.rotationZ = 20;
this.scene3D.addChild(boxPV);
this.physics.addBody(boxJL);
var ground:RigidBody = physics.createGround(new FlatShadeMaterial(pointLight3D), 300, 2);
ground.y = - 100;
var sphere:RigidBody = physics.createSphere(new WireframeMaterial(0xFF44FF), 20);
this.addEventListener(Event.ENTER_FRAME, this.onEnterFrame);
}
Zunächst erzeugen wir ein Objekt auf einem vergleichsweise komplizierten Weg. Dieser Weg eignet sich z.b. wenn noch andere Routinen in den Worflow eingebaut werden müssen.
Zunächst erzeugen wir ein Punktlicht der Klasse PointLight3D, das wir im Raum platzieren und eine MaterialsList, welche wir für die Darstellung eines Quaders brauchen. Als nächstes erzeugen wir die zwei grundlegenden Objekte.
- Das von Papervision3D darzustellende Objekt, welches immer von DisplayObject3D ableitet. In unserem Falle ist es ein „Cube“.
- Der dem darstellenden Objekt entsprechende physikalische Körper, immer eine Subklasse von RigidBody. Der RigidBody muss wissen, welches Darstellungs-Objekt er beeinflusst. In unserem Falle entspricht dem Cube die JBBox; sie sollte auch die selben Maße haben.
Da wir zwei getrennte Libraries haben wird grundsätzlich immer mit diesen beiden Objekten gearbeitet. Da es aber etwas umständlich ist, beide Objekte einzeln zu erzeugen, lernen wir im folgenden noch eine verkürzte Schreibweise, bei dir die Erzeugung und Verknüpfung beider Objekte im Hintergrund abläuft.
Wir können jetzt noch die Rotation oder Position des Quaders verändern. Beachte, dass die Koordinatenachsen nicht wie in der Informatik üblich von links oben nach rechts unten verlaufen. Stattdessen beginnen sie in der Mitte. Y-Werte werden nach oben und X-Werte nach rechts größer.
Wie bereits erwähnt stellt uns das Papervision3DPhysics-Objekt Komfortfunktionen zur Verfügung mit der wir die Erzeugung von Objekten vereinfachen können. Mit physics.createGround() erzeugen wir einen begrenzte Ebene und mit createSphere() eine Kugel. Das Objekt kümmert sich darum das flashjiglib-Objekt als auch das enstprechende Papervision-Objekt zu erzeugen und es zur Szene hinzuzufügen.
Zuletzt fügen wir einen EventListener auf ENTER_FRAME hinzu.
private function onEnterFrame(evt: Event):void
{
this.physics.step();
this.renderEngine.render();
}
}
}
Alles war wir jetzt noch tun müssen ist den Renderer sowie die Physics-Engine jeden Schritt zu aktualisieren.