April 2009
Flash AS3, Box2D und PV3D
Akronyme, die sich blendend verstehen
Physikalisch korrekte Zustände in Flash ab- oder nachzubilden ist in diesen Tagen dank einem reichhaltigen Angebot an Physic Engines nicht weiter schwierig. Nicht ganz so zahlreich, dennoch gut bestückt, ist das Angebot an 3D-Engines, die man mit Flash verwenden kann. Zuletzt hatte ich mich noch damit beschäftigt, ordentlich schleimige 2D-Aggregatzustände flüssig in Flash nachzubilden. Das Ganze seinerzeit unter Verwendung der angenehm performanten Motor2 Phyics-Engine von Michael Baczynski. Dem fügen wir heute eine weitere Dimension hinzu und schauen mal, ob wir auch in 3D etwas effektvoll Flüssiges in physikalisch korrekte Bahnen leiten können.
Text: Frank Reitberger
Die physikalische Grundlage
Als Basis dient uns dieses Mal Box2DFlashAS3, welche auf Erin Cattos wohlbekannter C++-Physics-Bibliothek Box2D basiert. Ein wohl sehr bekanntes Beispiel für den Einsatz der Box2D-Engine ist das 2D-Spiel/-Puzzle „Crayon Physics Deluxe". Um es noch einmal zu betonen, welche Engine an dieser Stelle ihre Verwendung findet, z. B. die WOW-Engine oder aber JiglibFlash, ist ganz dem persönlichen Gusto überlassen. Zu jeder der o. g. Engines gibt es eine Vielzahl an Beispielen und Tutorials, sodass der Einstieg meist kinderleicht ist und sich das gewünschte Ergebnis meist mit jeder der Engines erzielen lässt.
Hallo Welt
Um jetzt aber nicht bloß Theorie zu knüppeln, soll nicht länger um den heißen Brei herumgeredet und sogleich die physikalischen Rahmenbedingungen für unsere kleine Spielerei geschaffen werden.
package {import Box2D.Collision.Shapes.b2CircleDefimport Box2D.Collision.Shapes.b2PolygonDef;import Box2D.Collision.b2AABB;import Box2D.Common.Math.b2Vec2;import Box2D.Dynamics.b2Body;import Box2D.Dynamics.b2BodyDef;import Box2D.Dynamics.b2World;import org.papervision3d.core.math.Matrix3D;import org.papervision3d.core.math.Number3D;import org.papervision3d.lights.PointLight3D;import org.papervision3d.materials.shadematerials.FlatShadeMaterial;import org.papervision3d.objects.DisplayObject3D;import org.papervision3d.objects.primitives.Sphere;import org.papervision3d.view.BasicView;import flash.display.Bitmap;import flash.events.Event;import flash.filters.BevelFilter;public class Box2DPhysics extends BasicView {[Embed(source ="../library/squaredSphere.gif")] private var SphereImg:Class;[Embed(source = "../library/squares.jpg")] private var CanvasImg:Class; [Embed(source = "../library/squareTiles.png")] private var squareImg:Class;private var world :b2World;private var mainBody :b2Body;private var radians :Array = new Array();private var w :Number = 647;private var h :Number = 500;private var wScale :Number = 30;private var rndX :Number = .02;private var rndY :Number = .05;private var sx :Number = 0;private var sy :Number = 0;private var gx :Number = 0;private var gy :Number = 0;private var iterations :int = 2;private var timeStep :Number = 1.0 / 30.0;private var waves1 :phongWave;private var waves2 :phongWave;private var waves3 :phongWave;private static const FORWARD :Number3D =new Number3D( 0, 0, 1 );private var light :PointLight3D =new PointLight3D();public function Box2DPhysics (){ launch(); }public function launch():void {stage.quality = "LOW";setupPV3D ();setupWorld();setupFloor();setupCeiling();setupWalls();setupShapes();}///////////////////////////////////// setup: 3d.engine///////////////////////////////////private function setupPV3D():void {camera.y = 0;camera.z = -1500;camera.zoom = 210;}///////////////////////////////////// setup: physic(s).world///////////////////////////////////private function setupWorld():void {var worldBounds:b2AABB = new b2AABB();worldBounds.lowerBound = new b2Vec2( 0, 0 );worldBounds.upperBound = new b2Vec2(w/wScale, h/wScale);var gravity:b2Vec2 = new b2Vec2(0.0, 0.0);var sleep:Boolean = true;world = new b2World(worldBounds,gravity,sleep);}///////////////////////////////////// setup: floor///////////////////////////////////private function setupFloor():void {var floorShapeDef:b2PolygonDef = new b2PolygonDef();var floorBodyDef:b2BodyDef = new b2BodyDef();var floor:b2Body;floorShapeDef.SetAsBox( (w+40)/wScale/2,100/wScale);floorBodyDef.position = new b2Vec2(w/wScale/2,(h+50)/wScale);floor = world.CreateBody(floorBodyDef);floor.CreateShape(floorShapeDef);floor.SetMassFromShapes();}///////////////////////////////////// setup: ceiling///////////////////////////////////private function setupCeiling():void {var ceilingShapeDef:b2PolygonDef =new b2PolygonDef();var ceilingBodyDef:b2BodyDef = new b2BodyDef();var ceiling:b2Body;ceilingShapeDef.SetAsBox((w+40)/wScale/2,100/wScale);ceilingBodyDef.position = new b2Vec2(w/wScale /2,-65/wScale);ceiling = world.CreateBody(ceilingBodyDef);ceiling.CreateShape(ceilingShapeDef);}////////////////////////////////////// setup: wall(s)///////////////////////////////////private function setupWalls():void {var wallShapeDef:b2PolygonDef =new b2PolygonDef();var wallBodyDef:b2BodyDef = new b2BodyDef();var wall:b2Body;wallShapeDef.SetAsBox(100/wScale,(h+40)/wScale/2);wallBodyDef.position = new b2Vec2(-25/wScale,h/wScale /2);wall = world.CreateBody(wallBodyDef);wall.CreateShape(wallShapeDef);wallBodyDef.position = new b2Vec2((w+65) /wScale,h/wScale /2);wall = world.CreateBody(wallBodyDef);wall.CreateShape(wallShapeDef);}///////////////////////////////////// initialize: physic.shape(s)///////////////////////////////////private function setupShapes():void {for (var i:int = 0; i < 3; i++) {//radius for physics circle and spherevar radius:Number = 40 + Math.random() * 30;var bodyDef:b2BodyDef = new b2BodyDef();//random positionbodyDef.position = new b2Vec2( Math.random() * w /wScale, Math.random() * h/1.2/wScale);var body:b2Body = world.CreateBody(bodyDef);var shapeDef:b2CircleDef = new b2CircleDef();shapeDef.radius = radius/wScale;shapeDef.density = .5;shapeDef.friction = .5;shapeDef.restitution = .9;body.CreateShape(shapeDef);body.SetMassFromShapes();var flatShadeMaterial:FlatShadeMaterial =new FlatShadeMaterial( light, 0xFF | Math.random() * 255 << 24 | Math.random() * 255 << 8 | Math.random() * 255 );var sphere:Sphere =new Sphere( flatShadeMaterial, radius, 8, 8 );this.radians.push( radius );sphere.name = "Sphere" + i;scene.addChild(sphere);body.m_userData = sphere;mainBody = body;}mainBody.m_userData.x = 350;mainBody.m_userData.y = 300;addBmpMaterial();}///////////////////////////////////// add: bitmap.material///////////////////////////////////private function addBmpMaterial ():void {var img:Bitmap = new SphereImg();var bMat:BitmapMaterial = new BitmapMaterial(img.bitmapData );for(var bb:b2Body=world.m_bodyList;bb;bb=bb.m_next){if ( bb.m_userData is DisplayObject3D) {bb.m_userData.material = bMat;}}light.z = -1500;}}}
Wer hier aufmerksam mitgebastelt hat, der dürfte bemerkt haben, dass sich hier bereits das 3D-Setup (Papervision3D) bewältigen lässt. Die eigentlich wichtige Verknüpfung zwischen Physics- und 3D-Engine liegt in der Zeile body.m_userData = sphere. Hiermit wird sozusagen dem einzelnen 2D-Containerobjekt der Physics-Engine der 3D-Körper "Sphere" samt 3D-Eigenschaften zugeordnet. Für unser vorliegendes Beispiel ebenfalls relevant ist die Festlegung eines Hauptobjekts "mainBody = body", das wir als initialen Bewegungsantrieb zum späteren Animieren benötigen.
eines der wenigen Tutorials, die zeigen, wie man in einer 3D-Flash-Umgebung 2D-Physik verwenden kann.
Allerdings für Beginner so gut wie wertlos, weil: mangelhaft dokumentiert.
Vielleicht sollte hier doch mal etwas Zeit für das Kommentieren der einzelnen Codeblöcke aufgewandt werden. Dann wird auch verständlich, was du eigentlich vorhast.
lg, Fred #zitieren

































