サイトトップ

Director Flash 書籍 業務内容 プロフィール

Adobe Flash非公式テクニカルノート

StarlingフレームワークでつくったテクスチャのボールをBox2Dで弾ませる

ID: FN1203004 Product: Flash CS5 and above Platform: All Version: 11 and above/ActionScript 3.0

Starlingフレームワークで使える物理演算エンジン「Box2DFlash」(以下単に単にBox2Dと呼びます)の基本的な使い方は「Starlingフレームワークで物理演算エンジンBox2Dを使う」で解説しました。本稿では、そのスクリプトに手を加えて、テクスチャからつくったボールを弾ませてみます。

なお、Box2Dはバージョンによって、細かい仕様が変わることもありますのでご注意ください。本稿ではバージョン2.1aを使います。また、Starlingフレームワークの基礎とその使い方については、「Starlingフレームワークを使う」をお読みください。


01 テクスチャでボールをつくる
物理演算シミュレーションに入る前に、Starlingフレームワークでテクスチャのボールをつくりましょう。背景が抜かれたボール用の円形ビットマップをFlashの[ライブラリ]に納め、[ビットマッププロパティ]ダイアログボックスでクラスを設定しておきます(図001)。

図001■[ライブラリ]に用意したボール用ビットマップにクラスを設定する
図001

さて、この[ライブラリ]のビットマップをどうやってStarlingフレームワークにもっていくかです。というのは、前出「Starlingフレームワークを使う」01「Starlingフレームワークで何からつくり始めるか」に述べたとおり、「Starlingフレームワークの表示リストには、Starlingの表示オブジェクトしか加えられません」。そこで使うのが、Textureクラスです。

テクスチャ(texture)というのは、Starlingフレームワークでイメージを表示するために用いられるビットマップデータのことです。TextureクラスにはTexture.fromBitmapData()という静的メソッドが備わっていて、BitmapDataオブジェクトからTextureインスタンスがつくれます。

もっとも、Textureインスタンスは表示オブジェクトではないので、そのままではStarlingフレームワークのステージには置けません。Imageクラスのコンストラクタメソッドに引数として渡したうえで、そのImageオブジェクトを表示リストに加えます。

var myTexture:Texture = Texture.fromBitmapData(BitmapDataオブジェクト);
var myImage:Image = new Image(myTexture);
addChild(myImage);

Starlingフレームワークの表示リスト最上位(ルート)に置く表示オブジェクトとして以下のクラス(MySprite)を定めると(スクリプト001)、[ライブラリ]にクラス(Pen)を設定したビットマップがステージの中央に表示されます(図002)。なお、FLAファイル(ASファイルと同階層)には、つぎのフレームアクションが書かれているものとします(前出「Starlingフレームワークを使う」03「Starlingフレームワークの初期化」スクリプト002参照)。

// フレームアクション: メインタイムライン
import starling.core.Starling;
var myStarling:Starling = new Starling(MySprite, stage);
myStarling.start();

スクリプト001■[ライブラリ]のビットマップからテクスチャのボールをつくって表示する
    // ActionScript 3.0クラス定義ファイル: MySprite.as
  1. package {
  2.   import flash.display.BitmapData;
  3.   import starling.display.Sprite;
  4.   import starling.display.Image;
  5.   import starling.textures.Texture;
  6.   import starling.events.Event;
  7.   public class MySprite extends Sprite {
  8.     public function MySprite() {
  9.       addEventListener(Event.ADDED_TO_STAGE, initialize);
  10.     }
  11.     private function initialize(eventObject:Event):void {
  12.       var nStageWidth:Number = stage.stageWidth;
  13.       var nStageHeight:Number = stage.stageHeight;
  14.       var myBitmapData:BitmapData = new Pen();
  15.       var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
  16.       var nRadius:Number = myBitmapData.width / 2;
  17.       var myBall:Image = createBall(nRadius, myTexture);
  18.       myBall.x = nStageWidth / 2;
  19.       myBall.y = nStageHeight / 2;
  20.       addChild(myBall);
  21.     }
  22.     private function createBall(nRadius:Number, myTexture:Texture):Image {
  23.       var myImage:Image = new Image(myTexture);
  24.       myImage.pivotX = nRadius;
  25.       myImage.pivotY = nRadius;
  26.       return myImage;
  27.     }
  28.   }
  29. }

図002■[ライブラリ]のビットマップがStarlingフレームワークでステージ中央に表示された
図002

初期設定のメソッド(initialize())は、[ライブラリ]のビットマップに設定したクラス(Pen)からインスタンスをつくり、Texture.fromBitmapData()メソッドの引数に渡してTextureインスタンス(myTexture)にしています(スクリプト001第14〜15行目)。

さらに、ボールの半径とTextureインスタンスを引数として、ボールのImageインスタンスをつくるメソッド(createBall())が呼出されます(スクリプト001第17行目)。このメソッドは、受取ったTextureオブジェクト(myTexture)から新たなImageインスタンス(myImage)をつくり、半径(nRadius)に合わせてインスタンスの原点を調整します(第23〜25行目)。そして、Imageインスタンスを返します(第26行目)。


02 Box2Dのスクリプトにテクスチャのボールを組込む
初めに述べましたとおり、本稿はスクリプトを頭から書くのではなく、FN1202006「Starlingフレームワークで物理演算エンジンBox2Dを使う」のサンプルに手を加えます。具体的には、スクリプト003「動的な剛体が落下して静的な剛体のうえで弾むアニメーション」です(FN1203004図005再掲)。

FN1203004図005■落下した矩形の剛体が床で弾む(再掲)
図002

この落下する矩形のQuadインスタンスを、テクスチャのボールに置換えます。そのために、FN1203004スクリプト003のクラス(MySprite)にまずはつぎのような追加・修正を加えます。第1に、落下する矩形のQuadインスタンスを扱う処理は、スクリプトから除きます。プロパティ(boxEdge)やメソッド(createDynamicBox())とその呼出しのステートメントなどです。

第2に、テクスチャのボールをつくり、Box2Dの剛体として組入れます。Textureインスタンス(myTexture)のつくり方は、前掲スクリプト001(第14〜16行目)と同じです。Box2Dの剛体に組入れるメソッド(createDynamicBall())は後で定めますので、その呼出しを初期設定のメソッド(initialize())に加え、でき上がったボールのImageインスタンス(myBall)は表示リストに納めます。

// import宣言追加
import flash.display.BitmapData;
import starling.display.Image;
import starling.textures.Texture;
// ...[中略]...
public class MySprite extends Sprite {
  // ...[中略]...
  // private var boxEdge:Number = 20;   // 削除
  // ...[中略]...

  private function initialize(eventObject:Event):void {
    // ...[中略]...
    // [ライブラリ]のビットマップからTextureインスタンスをつくる
    var myBitmapData:BitmapData = new Pen();
    var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
    var nRadius:Number = myBitmapData.width / 2;
    // ...[中略]...
    // テクスチャのボールを剛体として表示リストに加える
    // var myQuad:Quad = createDynamicBox(nCenterX, boxEdge, boxEdge, boxEdge, 0x0000FF);   // 削除
    var myBall:Image = createDynamicBall(nCenterX, 0, nRadius, myTexture);
    // addChild(myQuad);   // 削除
    addChild(myBall);
    // ...[中略]...
  }
  // ...[中略]...
  /* 削除
  private function createDynamicBox(nX:Number, nY:Number, nWidth:Number, nHeight:Number, nColor:uint):Quad {
    // ...[中略]...
  }
  */

  // ...[中略]...
}

それでは、取りあえずメソッドをふたつ定義します。できあがりのスクリプトは、後にスクリプト002として掲げます。引用したステートメントには、その行番号を添えました。

ひとつ目は、初期設定のメソッド(initialize())から呼出すメソッド(createDynamicBall())で、Box2Dの剛体を定義し、テクスチャからつくったImageインスタンスを返します(スクリプト002第52〜59行目)。中からふたつのメソッド(createBodyDef()とcreateBallForBodyDef())を呼出しています(前者は前出「Starlingフレームワークで物理演算エンジンBox2Dを使う」ですでに定められています)。この引用部分だけでは、まだ完成していません。

ふたつ目は、ひとつ目のメソッド(createDynamicBall())から呼出したメソッド(createBallForBodyDef())です。テクスチャからImageインスタンスをつくり、剛体定義のb2BodyDefオブジェクトに関連づけて返します(スクリプト002第73〜79行目)。中身は前掲スクリプト001のImageインスタンスをつくるメソッド(createBall())とほとんど同じで、ただインスタンスをb2BodyDef.userDataプロパティに設定するステートメントが加わっています(第77行目)。

    // Box2Dの剛体を定義してボールのImageインスタンスを返す
  1. private function createDynamicBall(nX:Number, nY:Number, nRadius:Number, myTexture:Texture):Image {
  2.   var bodyDef:b2BodyDef = createBodyDef(nX, nY, b2Body.b2_dynamicBody);
  3.   var myImage:Image = createBallForBodyDef(bodyDef, nRadius, myTexture);
  1.   return myImage;
  2. }
    // テクスチャからつくったImageインスタンスに剛体定義を関連づけて返す
  1. private function createBallForBodyDef(bodyDef:b2BodyDef, nRadius:Number, myTexture:Texture):Image {
  2.   var myImage:Image = new Image(myTexture);
  3.   myImage.pivotX = nRadius;
  4.   myImage.pivotY = nRadius;
  5.   bodyDef.userData = myImage;
  6.   return myImage;
  7. }

ここまで修正しただけで動きを確かめると、ステージ左上角にテクスチャからつくったボールのImageインスタンスが表れます(図003)。Box2Dのシミュレーションにはまだ組込んでいないので、インスタンスは動きません。けれど、物理演算はメモリ内で行われます。ですから、目に見えるところで確かめておくと、後で問題が起こったときにも原因を絞り込みやすくなります。

図003■ステージ左上角にボールのインスタンスが表れる
図003


03 テクスチャのボールをBox2Dで弾ませる
それでは、テクスチャでつくったボールをBox2Dのシミュレーションに組入れます。前項で定めた、Box2Dの剛体を定義してボールのImageインスタンスを返すメソッド(createDynamicBall())には、まだ足りないところがあるといいました。このメソッドに、ステートメントを3行加えます(スクリプト002第55〜57行目)。

まず、フィクスチャの定義となるb2FixtureDefインスタンス(fixtureDef)をつくります(スクリプト002第55行目)。そのための関数(createFixtureDefAndCircleShape)を新たに定めます。つぎに、フィクスチャに、重さ(密度)や滑りやすさ(摩擦)、跳ね返りの度合い(弾性)などを定めます(第56行目)。そして、剛体とフィクスチャのふたつの定義から剛体(body)をつくるのです(第57行目)。後のふたつの処理については、すでにメソッド(setFixtureDef()とcreateBodyWithFixture())が定められていますので、それぞれに必要な引数を与えて呼出します。

b2FixtureDefインスタンスをつくるメソッド(createFixtureDefAndCircleShape())は、新たなb2FixtureDefインスタンス(fixtureDef)に剛体のかたちを定めた上で、インスタンスを返します(スクリプト002第85〜90行目)。ボールのかたちは円形ですので、b2CircleShapeオブジェクトでつくります。コンストラクタメソッドの引数は円の半径です(第87行目)。b2CircleShapeオブジェクトは、b2FixtureDefインスタンスのb2FixtureDef.shapeプロパティに設定します(第88行目)。

    // import宣言追加
  1. import Box2D.Collision.Shapes.b2CircleShape;
  2. public class MySprite extends Sprite {
    // Box2Dの剛体を定義してボールのImageインスタンスを返す
  1.   private function createDynamicBall(nX:Number, nY:Number, nRadius:Number, myTexture:Texture):Image {
  2.     var bodyDef:b2BodyDef = createBodyDef(nX, nY, b2Body.b2_dynamicBody);
  3.     var myImage:Image = createBallForBodyDef(bodyDef, nRadius, myTexture);
        // 3ステートメント追加
  4.     var fixtureDef:b2FixtureDef = createFixtureDefAndCircleShape(nRadius);
  5.     setFixtureDef(fixtureDef, density, friction, restitution);
  6.     var body:b2Body = createBodyWithFixture(world, bodyDef, fixtureDef);
  7.     return myImage;
  8.   }
    // 剛体のかたちが定められた新たなb2FixtureDefインスタンスを返す
  1.   private function createFixtureDefAndCircleShape(nRadius:Number):b2FixtureDef {
  2.     var fixtureDef:b2FixtureDef = new b2FixtureDef();
  3.     var myShape:b2CircleShape = new b2CircleShape(nRadius * SCALE);
  4.     fixtureDef.shape = myShape;
  5.     return fixtureDef;
  6.   }
  1. }

これでクラス(MySprite.as)の修正は済みました。テクスチャのボールが物理演算シミュレーションに組込まれましたので、ボールがステージ上から落ちてきて、下にある床に当たって弾みます(図004)。定義したクラス(MySprite)全体は、以下のスクリプト002のとおりです。

図004■テクスチャのボールが落ちて床で弾む
図004

スクリプト002■テクスチャでつくったボールの剛体が床に落ちて弾むアニメーション
    // ActionScript 3.0クラス定義ファイル: MySprite.as
  1. package {
  2.   import flash.display.BitmapData;
  3.   import starling.display.DisplayObject;
  4.   import starling.display.Sprite;
  5.   import starling.display.Quad;
  6.   import starling.display.Image;
  7.   import starling.textures.Texture;
  8.   import starling.events.Event;
  9.   import Box2D.Common.Math.b2Vec2;
  10.   import Box2D.Dynamics.b2World;
  11.   import Box2D.Dynamics.b2BodyDef;
  12.   import Box2D.Dynamics.b2Body;
  13.   import Box2D.Dynamics.b2FixtureDef;
  14.   import Box2D.Collision.Shapes.b2PolygonShape;
  15.   import Box2D.Collision.Shapes.b2CircleShape;
  16.   public class MySprite extends Sprite {
  17.     private const SCALE:Number = 1 / 30;
  18.     private var world:b2World;
  19.     private var gravityVertical:Number = 10;
  20.     private var velocityIterations:int = 10;
  21.     private var positionIterations:int = 10;
  22.     private var time:Number = 1 / 24;
  23.     private var density:Number = 1;
  24.     private var friction:Number = 0.5;
  25.     private var restitution:Number = 0.8;
  26.     public function MySprite() {
  27.       addEventListener(Event.ADDED_TO_STAGE, initialize);
  28.     }
  29.     private function initialize(eventObject:Event):void {
  30.       var nStageWidth:Number = stage.stageWidth;
  31.       var nStageHeight:Number = stage.stageHeight;
  32.       var nCenterX:Number = nStageWidth / 2;
  33.       var nFloorWidth:Number = nStageWidth * 0.8;
  34.       var nFloorHeight:Number = 20;
  35.       var myBitmapData:BitmapData = new Pen();
  36.       var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
  37.       var nRadius:Number = myBitmapData.width / 2;
  38.       world = new b2World(new b2Vec2(0, gravityVertical), true);
  39.       var floorQuad:Quad = createStaticFloor(nCenterX,nStageHeight - nFloorHeight,nFloorWidth,nFloorHeight,0xCCCCCC);
  40.       addChild(floorQuad);
  41.       var myBall:Image = createDynamicBall(nCenterX, 0, nRadius, myTexture);
  42.       addChild(myBall);
  43.       addEventListener(Event.ENTER_FRAME, update);
  44.     }
  45.     private function createStaticFloor(nX:Number, nY:Number, nWidth:Number, nHeight:Number, nColor:uint):Quad {
  46.       var bodyDef:b2BodyDef = createBodyDef(nX, nY);
  47.       var myQuad:Quad = createQuadForBodyDef(bodyDef, nWidth, nHeight, nColor);
  48.       var fixtureDef:b2FixtureDef = createFixtureDefWithShape(nWidth, nHeight);
  49.       var body:b2Body = createBodyWithFixture(world, bodyDef, fixtureDef);
  50.       return myQuad;
  51.     }
  52.     private function createDynamicBall(nX:Number, nY:Number, nRadius:Number, myTexture:Texture):Image {
  53.       var bodyDef:b2BodyDef = createBodyDef(nX, nY, b2Body.b2_dynamicBody);
  54.       var myImage:Image = createBallForBodyDef(bodyDef, nRadius, myTexture);
  55.       var fixtureDef:b2FixtureDef = createFixtureDefAndCircleShape(nRadius);
  56.       setFixtureDef(fixtureDef, density, friction, restitution);
  57.       var body:b2Body = createBodyWithFixture(world, bodyDef, fixtureDef);
  58.       return myImage;
  59.     }
  60.     private function createBodyDef(nX:Number, nY:Number, nType:uint= 0):b2BodyDef {
  61.       var bodyDef:b2BodyDef = new b2BodyDef();
  62.       bodyDef.position.Set((nX * SCALE), nY * SCALE);
  63.       bodyDef.type = nType;
  64.       return bodyDef;
  65.     }
  66.     private function createQuadForBodyDef(bodyDef:b2BodyDef, nWidth:Number, nHeight:Number, nColor:uint=0):Quad {
  67.       var myQuad:Quad = new Quad(nWidth,nHeight,nColor);
  68.       myQuad.pivotX = myQuad.width / 2;
  69.       myQuad.pivotY = myQuad.height / 2;
  70.       bodyDef.userData = myQuad;
  71.       return myQuad;
  72.     }
  73.     private function createBallForBodyDef(bodyDef:b2BodyDef, nRadius:Number, myTexture:Texture):Image {
  74.       var myImage:Image = new Image(myTexture);
  75.       myImage.pivotX = nRadius;
  76.       myImage.pivotY = nRadius;
  77.       bodyDef.userData = myImage;
  78.       return myImage;
  79.     }
  80.     private function createFixtureDefWithShape(nWidth:Number, nHeight:Number):b2FixtureDef {
  81.       var fixtureDef:b2FixtureDef = new b2FixtureDef();
  82.       setShapeToFixtureDef(fixtureDef, nWidth / 2, nHeight / 2);
  83.       return fixtureDef;
  84.     }
  85.     private function createFixtureDefAndCircleShape(nRadius:Number):b2FixtureDef {
  86.       var fixtureDef:b2FixtureDef = new b2FixtureDef();
  87.       var myShape:b2CircleShape = new b2CircleShape(nRadius * SCALE);
  88.       fixtureDef.shape = myShape;
  89.       return fixtureDef;
  90.     }
  91.     private function setShapeToFixtureDef(fixtureDef:b2FixtureDef, nX:Number, nY:Number):void {
  92.       var myShape:b2PolygonShape = new b2PolygonShape();
  93.       myShape.SetAsBox(nX * SCALE, nY * SCALE);
  94.       fixtureDef.shape = myShape;
  95.     }
  96.     private function setFixtureDef(fixtureDef:b2FixtureDef, density:Number, friction:Number, restitution:Number = 0):void {
  97.       fixtureDef.density = density;
  98.       fixtureDef.friction = friction;
  99.       fixtureDef.restitution = restitution;
  100.     }
  101.     private function createBodyWithFixture(world:b2World, bodyDef:b2BodyDef, fixtureDef:b2FixtureDef):b2Body {
  102.       var body:b2Body = world.CreateBody(bodyDef);
  103.       body.CreateFixture(fixtureDef);
  104.       return body;
  105.     }
  106.     private function update(eventObject:Event):void {
  107.       world.Step(time, velocityIterations, positionIterations);
  108.       var body:b2Body = world.GetBodyList();
  109.       while (body) {
  110.         var myObject:DisplayObject = body.GetUserData() as DisplayObject;
  111.         if (myObject) {
  112.           var position:b2Vec2 = body.GetPosition();
  113.           myObject.x = position.x / SCALE;
  114.           myObject.y = position.y / SCALE;
  115.           myObject.rotation = body.GetAngle();
  116.         }
  117.         body = body.GetNext();
  118.       }
  119.     }
  120.   }
  121. }

04 ボールをたくさん落として互いに弾ませる
ひとつのボールが無事に落ちましたので、数を増やしてみましょう。100個のボールをランダムな水平位置から、時間差で落とすことにします。Timerクラスを使えば、時間の間隔も、イベントリスナーの呼出し回数も決められるので便利です。でき上がりのクラス(MySprite)は、後にスクリプト003として掲げてあります。そのうち、修正した部分をつぎに抜出しました。

  1. import flash.utils.Timer;
  2. import flash.events.TimerEvent;
  1. public class MySprite extends Sprite {
  1.   private var restitution:Number = 0.8;   // 0.5;
      // プロパティとして宣言
  2.   private var myTexture:Texture;
  3.   private var nStageWidth:Number;
  4.   private var nStageHeight:Number;
  5.   private var nRadius:Number;
  6.   private var myTimer:Timer = new Timer(200, 100);
  1.   private function initialize(eventObject:Event):void {
        // var nStageWidth:Number = stage.stageWidth;
  2.     nStageWidth = stage.stageWidth;
        // var nStageHeight:Number = stage.stageHeight;
  3.     nStageHeight = stage.stageHeight;
        // var myTexture:Texture = Texture.fromBitmapData(myBitmapData);
  1.     myTexture = Texture.fromBitmapData(myBitmapData);
        // var nRadius:Number = myBitmapData.width / 2;
  2.     nRadius = myBitmapData.width / 2;
        // var myBall:Image = createDynamicBall(nCenterX, 0, nRadius, myTexture);
        // addChild(myBall);

  1.     myTimer.addEventListener(TimerEvent.TIMER, addBall);
  2.     myTimer.start();
  3.   }
      // ランダムな水平位置からボールを落とす
  4.   private function addBall(eventObject:TimerEvent):void {
  5.     var nX:Number = Math.random() * nStageWidth;
  6.     var myBall:Image = createDynamicBall(nX, -nRadius, nRadius, myTexture);
  7.     addChild(myBall);
  8.   }
  1. }

まず、Timerクラスを使った処理を見ていきます。必要なクラスをimport宣言し(スクリプト003第2〜3行目)、Timerインスタンスをプロパティ(myTimer)に納めます(第32行目)。200ミリ秒間隔で、イベントリスナーを100回呼出します。そして、初期設定のメソッド(initialize())は、Timerオブジェクトにリスナーメソッド(addBall())を定めて、Timer.start()メソッドを呼出します(第49〜50行目)。

つぎに、リスナーメソッド(addBall())は、呼出されるたびにひとつボールの剛体を定義してImageインスタンスをつくります(スクリプト003第52〜56行目)。前掲スクリプト002(第41〜42行目)では、初期設定のメソッド(initialize())でこの処理を行っていましたので、そのステートメントは削除しました。また、このリスナーメソッドの中で用いている値には、初期設定のメソッド(initialize())で得ているもの(nStageWidthやnRadius、myTexture)があります。したがって、それらはプロパティとして宣言しました(第28〜31行目)。

これで、200ミリ秒ごとにランダムな水平位置から、つぎつぎと100個のボールが落とされ、互いにぶつかり合います(図005)。

図005■ランダムな水平位置から時間差で100個のボールが落とされて互いにぶつかり合う
図005

スクリプト003■ボールの剛体をつぎつぎに落として互いに弾ませるアニメーション
    // ActionScript 3.0クラス定義ファイル: MySprite.as
  1. package {
  2.   import flash.display.BitmapData;
  3.   import flash.utils.Timer;
  4.   import flash.events.TimerEvent;
  5.   import starling.display.DisplayObject;
  6.   import starling.display.Sprite;
  7.   import starling.display.Quad;
  8.   import starling.display.Image;
  9.   import starling.textures.Texture;
  10.   import starling.events.Event;
  11.   import Box2D.Common.Math.b2Vec2;
  12.   import Box2D.Dynamics.b2World;
  13.   import Box2D.Dynamics.b2BodyDef;
  14.   import Box2D.Dynamics.b2Body;
  15.   import Box2D.Dynamics.b2FixtureDef;
  16.   import Box2D.Collision.Shapes.b2PolygonShape;
  17.   import Box2D.Collision.Shapes.b2CircleShape;
  18.   public class MySprite extends Sprite {
  19.     private const SCALE:Number = 1 / 30;
  20.     private var world:b2World;
  21.     private var gravityVertical:Number = 10;
  22.     private var velocityIterations:int = 10;
  23.     private var positionIterations:int = 10;
  24.     private var time:Number = 1 / 24;
  25.     private var density:Number = 1;
  26.     private var friction:Number = 0.5;
  27.     private var restitution:Number = 0.8;
  28.     private var myTexture:Texture;
  29.     private var nStageWidth:Number;
  30.     private var nStageHeight:Number;
  31.     private var nRadius:Number;
  32.     private var myTimer:Timer = new Timer(200, 100);
  33.     public function MySprite() {
  34.       addEventListener(Event.ADDED_TO_STAGE, initialize);
  35.     }
  36.     private function initialize(eventObject:Event):void {
  37.       nStageWidth = stage.stageWidth;
  38.       nStageHeight = stage.stageHeight;
  39.       var nCenterX:Number = nStageWidth / 2;
  40.       var nFloorWidth:Number = nStageWidth * 0.8;
  41.       var nFloorHeight:Number = 20;
  42.       var myBitmapData:BitmapData = new Pen();
  43.       myTexture = Texture.fromBitmapData(myBitmapData);
  44.       nRadius = myBitmapData.width / 2;
  45.       world = new b2World(new b2Vec2(0, gravityVertical), true);
  46.       var floorQuad:Quad = createStaticFloor(nCenterX, nStageHeight - nFloorHeight, nFloorWidth, nFloorHeight, 0xCCCCCC);
  47.       addChild(floorQuad);
  48.       addEventListener(Event.ENTER_FRAME, update);
  49.       myTimer.addEventListener(TimerEvent.TIMER, addBall);
  50.       myTimer.start();
  51.     }
  52.     private function addBall(eventObject:TimerEvent):void {
  53.       var nX:Number = Math.random() * nStageWidth;
  54.       var myBall:Image = createDynamicBall(nX, -nRadius, nRadius, myTexture);
  55.       addChild(myBall);
  56.     }
  57.     private function createStaticFloor(nX:Number, nY:Number, nWidth:Number, nHeight:Number, nColor:uint):Quad {
  58.       var bodyDef:b2BodyDef = createBodyDef(nX,nY);
  59.       var myQuad:Quad = createQuadForBodyDef(bodyDef, nWidth, nHeight, nColor);
  60.       var fixtureDef:b2FixtureDef = createFixtureDefWithShape(nWidth, nHeight);
  61.       var body:b2Body = createBodyWithFixture(world, bodyDef, fixtureDef);
  62.       return myQuad;
  63.     }
  64.     private function createDynamicBall(nX:Number, nY:Number, nRadius:Number, myTexture:Texture):Image {
  65.       var bodyDef:b2BodyDef = createBodyDef(nX, nY, b2Body.b2_dynamicBody);
  66.       var myImage:Image = createBallForBodyDef(bodyDef, nRadius, myTexture);
  67.       var fixtureDef:b2FixtureDef = createFixtureDefAndCircleShape(nRadius);
  68.       setFixtureDef(fixtureDef, density, friction, restitution);
  69.       var body:b2Body = createBodyWithFixture(world, bodyDef, fixtureDef);
  70.       return myImage;
  71.     }
  72.     private function createBodyDef(nX:Number, nY:Number, nType:uint= 0):b2BodyDef {
  73.       var bodyDef:b2BodyDef = new b2BodyDef();
  74.       bodyDef.position.Set((nX * SCALE), nY * SCALE);
  75.       bodyDef.type = nType;
  76.       return bodyDef;
  77.     }
  78.     private function createQuadForBodyDef(bodyDef:b2BodyDef, nWidth:Number, nHeight:Number, nColor:uint=0):Quad {
  79.       var myQuad:Quad = new Quad(nWidth, nHeight, nColor);
  80.       myQuad.pivotX = myQuad.width / 2;
  81.       myQuad.pivotY = myQuad.height / 2;
  82.       bodyDef.userData = myQuad;
  83.       return myQuad;
  84.     }
  85.     private function createBallForBodyDef(bodyDef:b2BodyDef, nRadius:Number, myTexture:Texture):Image {
  86.       var myImage:Image = new Image(myTexture);
  87.       myImage.pivotX = nRadius;
  88.       myImage.pivotY = nRadius;
  89.       bodyDef.userData = myImage;
  90.       return myImage;
  91.     }
  92.     private function createFixtureDefWithShape(nWidth:Number, nHeight:Number):b2FixtureDef {
  93.       var fixtureDef:b2FixtureDef = new b2FixtureDef();
  94.       setShapeToFixtureDef(fixtureDef, nWidth / 2, nHeight / 2);
  95.       return fixtureDef;
  96.     }
  97.     private function setShapeToFixtureDef(fixtureDef:b2FixtureDef, nX:Number, nY:Number):void {
  98.       var myShape:b2PolygonShape = new b2PolygonShape();
  99.       myShape.SetAsBox(nX * SCALE, nY * SCALE);
  100.       fixtureDef.shape = myShape;
  101.     }
  102.     private function setFixtureDef(fixtureDef:b2FixtureDef, density:Number, friction:Number, restitution:Number = 0):void {
  103.       fixtureDef.density = density;
  104.       fixtureDef.friction = friction;
  105.       fixtureDef.restitution = restitution;
  106.     }
  107.     private function createBodyWithFixture(world:b2World, bodyDef:b2BodyDef, fixtureDef:b2FixtureDef):b2Body {
  108.       var body:b2Body = world.CreateBody(bodyDef);
  109.       body.CreateFixture(fixtureDef);
  110.       return body;
  111.     }
  112.     private function createFixtureDefAndCircleShape(nRadius:Number):b2FixtureDef {
  113.       var fixtureDef:b2FixtureDef = new b2FixtureDef();
  114.       var myShape:b2CircleShape = new b2CircleShape(nRadius * SCALE);
  115.       fixtureDef.shape = myShape;
  116.       return fixtureDef;
  117.     }
  118.     private function update(eventObject:Event):void {
  119.       world.Step(time, velocityIterations, positionIterations);
  120.       var body:b2Body = world.GetBodyList();
  121.       while (body) {
  122.         var myObject:DisplayObject = body.GetUserData() as DisplayObject;
  123.         if (myObject) {
  124.           var position:b2Vec2 = body.GetPosition();
  125.           myObject.x = position.x / SCALE;
  126.           myObject.y = position.y / SCALE;
  127.           myObject.rotation = body.GetAngle();
  128.         }
  129.         body = body.GetNext();
  130.       }
  131.     }
  132.   }
  133. }

作成者: 野中文雄
作成日: 2012年2月19日


Copyright © 2001-2012 Fumio Nonaka.  All rights reserved.