Juli 2008
Zähflüssige Physik
Fluide Tendenzen mit ActionScript
Flüssige Aggregatzustände mit ActionScript nachzubilden, ist nicht immer einfach, gerade wenn es sich dabei um biomorphe Formen handelt. Will man dazu noch auf physikalisch korrekte Verhaltensweisen wie beispielsweise Kollisionen nicht verzichten, kann man an diesem Thema mit Blick auf ein performantes Ergebnis schon mal länger knabbern. Das sollte uns jetzt aber nicht davon abhalten, Blob-Architekt zu spielen und etwas ordentlich Schleimiges zu entwickeln.
Text: Frank Reitberger
Die physikalische Basis
Die Grundlage unserer Blobs bildet eine Physics-Engine, der wir vertrauensvoll die wesentlichen Aufgaben der Kollisionsabfrage, Fließgeschwindigkeit und -richtung sowie die Objektverwaltung überlassen.
An dieser Stelle muss man den Kreis auch nicht mehr selber quadratieren, sondern kann auf zahlreiche Physic-Engines für die Verwendung mit AS2- und AS3 zurückgreifen. Zu den bekanntesten gehören wohl APE, Box2Dflash und Flashfisix.
In diesem Beispiel gebrauchen wir allerdings keinen der eben genannten Kandidaten, sondern übertragen diese Anforderungen einer Engine namens "Motor2". Bei der Entwicklung von Motor2 legte der Autor Michael Baczynski besonderen Wert auf Performance. Sehr sympathisch in unserem Fall, denn wir brauchen ja nicht nur den physikalischen Unterbau, sondern müssen unsere Freiformen schließlich auch noch rendern. Ein nicht zu unterschätzender Teil, der ordentlich Ressourcen beansprucht.
Listing 1
import display.BoxRenderer;import display.PolyRenderer;import display.ShapeRenderer;import display.Style;import de.polygonal.motor2.dynamics.contact.*;import de.polygonal.motor2.dynamics.*;import de.polygonal.motor2.collision.shapes.*;import de.polygonal.motor2.World;var world :World;var displayList :Array;var worldShapes :ShapeRenderer;var i :int;// setup: object(s).listthis.displayList = [];// setup: worldthis.world = new World(null, true);this.world.setGravity(0, 400);createWorld();// create: moving.polygon(s)for( this.i = 0; this.i < 40; this.i++ ) {createPoly(50,-10,10,10,10,10,true);}function createWorld() :void {// create: left.bordercreateBox( 0, 180, 2, 378, 0, 0, 0, 0 );// create: right.bordercreateBox( 506, 180, 2, 378, 0, 0, 0, 0 );// create: top.floorcreateBox( 130, 100, 300, 10, 0, 0, 0, .05 );// create: middle.floorcreateBox( 400, 190, 220, 10, 0, 0, 0, -.05 )}function onFrameUpdate() :void {// refresh(s) worldworld.step(.018, 400);// render worldfor (this.i=0;this.i<this.displayList.length;this.i++ ) {// update: each object in listthis.displayList[i].update();// render: each object in listthis.displayList[i].render(1);}}function createBox(x:Number,y:Number,w:Number,h:Number,density:Number,mx:Number=0,my:Number=0,mr:Number = 0, addTo:Boolean = false):RigidBody{var sd:BoxData = new BoxData(density, w, h);sd.mx = mx;sd.my = my;sd.mr = mr;sd.friction = .2;var bd:RigidBodyData = new RigidBodyData();bd.addShapeData(sd);bd.x = x;bd.y = y;var rb:RigidBody = world.createBody(bd);var sr:ShapeRenderer = newBoxRenderer(rb.shapeList);sr.draw();this.displayList.push(sr);return rb;}function createPoly(x:Number,y:Number,sides:int,xradius:Number,yradius:Number, density:Number,addTo:Boolean =false):RigidBody{var vertices:Array = ShapeLib.ngon(sides, xradius, yradius);var sd:PolyData = new PolyData(density, vertices);sd.mx = 0;sd.my = 0;sd.friction = .25;var bd:RigidBodyData = new RigidBodyData();bd.addShapeData(sd);bd.x = x;bd.y = y;var b:RigidBody = world.createBody(bd);var sr:ShapeRenderer = newPolyRenderer(b.shapeList);sr.draw();if ( isBlob ) {this.radius = 20 + Math.random()*8 +Math.sin(.3) * 12;this.blobList.push( new soapBlob( sr, this.col,this.radius ) );}if ( addTo ) {this.displayList.push(sr);}return b;}

































