物理エンジンライブラリ Box2DFlashAS3(基本)

Box2DFlashAS3に再チャレンジです。バージョンは2.0.2を使用
使い方等ざっと自分用にまとめ。

使い方

  1. 領域(b2AABB)・重力(b2Vec2)・スリープ(Boolean)設定して、Box2Dの世界(b2World)作成
  2. 性質(b2ShapeDef)設定(素材(密度・摩擦力・反発力)、形状(多角形・円、サイズ))
  3. 配置(b2BodyDef)設定(配置座標、配置角度、表示用オブジェクト、スリープ指定)
  4. B2世界上で、配置から物体(b2Body)作成。物体上で性質から形状(b2Shape)作成。形状を固める。
  5. 一定時間毎(EnterFrameイベントなど)で演算させ、結果を表示用オブジェクトに反映させる

Box2Dの単位

  • Box2Dの単位はメートル。縦横1を指定した箱を作ると、縦横1メートルの箱を作ることになる。
  • Box2Dの指定・算出したサイズや座標の値をそのまま使うと、1ピクセル1メートルの世界になってしまう。でかい。
  • なので、スケールを指定してやって、100ピクセル1メートルとかにして使用する。

Box2Dの基点(原点)

  • Box2Dの基点は中心。
  • サイズ指定するときは、中心からの距離で指定する。
  • 長方形なら、辺の半分、円なら半径を指定、多角形は中心座標を指定して、そこから頂点の距離
  • 座標の指定も、左上ではなく、中心を指定。Flashでパーツを作る場合は、ステージの中心を原点にする。

サンプルコード

実行結果(きのこの丘)
きのこの丘

ベースのクラス(Box2Dの機能を適当にまとめたもの)

Actionscript:
  1. public class B2Base extends Sprite
  2. {
  3.     //Box2Dのバージョンは2.0.2
  4.  
  5.     protected var m_b2_world:b2World;
  6.     //縮尺 1mを何ピクセルで表すか
  7.     protected var m_b2_physcale:Number = 10
  8.     //演算制度 大きいと正確だけど、負荷が大きい
  9.     protected var m_b2_iterations:int = 10;   
  10.     // 演算をfpsに合わせる
  11.     protected var m_b2_timeStep:Number = 24.0
  12.    
  13.     //constructor
  14.     public function B2Base(fps:int = 24, physcale:Number = 10,
  15.                  gravity:Number = 10):void{
  16.        
  17.         //timestep設定
  18.         this.m_b2_timeStep = 1.0 / fps;
  19.        
  20.         //スケール設定
  21.         this.m_b2_physcale = physcale;
  22.        
  23.         //B2DBoxの世界生成
  24.         this.createB2World(gravity);
  25.     }
  26.  
  27.     //Box2D世界作成
  28.     private function createB2World(gravity:Number):void {
  29.        
  30.         // Creat world AABB
  31.         var worldAABB:b2AABB = new b2AABB();
  32.         worldAABB.lowerBound.Set(-1000, -1000);
  33.         worldAABB.upperBound.Set(1000, 1000);
  34.        
  35.         // Define the gravity vector
  36.         var vec_gravity:b2Vec2 = new b2Vec2(0.0, 10.0);
  37.        
  38.         // Allow bodies to sleep
  39.         var doSleep:Boolean = true;
  40.        
  41.         // Construct a world object
  42.         this.m_b2_world = new b2World(worldAABB, vec_gravity, doSleep);
  43.     }
  44.    
  45.    
  46.     /**
  47.      * 長方形の物体作成
  48.      * @param   int w (ピクセル 内部でB2DBox用に変換)
  49.      * @param   int h (ピクセル 内部でB2DBox用に変換)
  50.      * @param   Number x(ピクセル 内部でB2DBox用に変換)
  51.      * @param   Number y(ピクセル 内部でB2DBox用に変換)
  52.      * @param   Number angle 角度(360度 内部でラジアンに変換)
  53.      * @param   density 密度 0にすると固定
  54.      * @param   friction 摩擦 大きいと滑りにくい
  55.      * @param   restitution 跳ね返り 大きいと、反発しやすい
  56.      * @param   viewdata 表示用のSprite
  57.      * @param   viewgroup 表示用のSpriteを入れるSprite
  58.      * @return  b2Body
  59.      */
  60.     protected function makeB2BodyBox(w:int, h:int,
  61.                      x:Number, y:Number, angle:Number,
  62.                      density:Number,
  63.                      friction:Number,
  64.                      restitution:Number,
  65.                      viewdata:Sprite = null,
  66.                      viewgroup:Sprite = null) :b2Body {
  67.        
  68.         var b2_boxdef:b2PolygonDef = this.createB2BoxDef(w, h, density,
  69.                                  friction, restitution);
  70.         var b2_bodydef:b2BodyDef = this.createB2BodyDef(x, y, angle,
  71.                                 viewdata, viewgroup);
  72.        
  73.         var b2_body:b2Body = this.m_b2_world.CreateBody(b2_bodydef);
  74.         b2_body.CreateShape(b2_boxdef);
  75.         b2_body.SetMassFromShapes();
  76.        
  77.         return b2_body;
  78.     }   
  79.    
  80.    
  81.     /**
  82.      * 円の物体作成
  83.      * @param   diameter 直径(ピクセル 内部でメートルに変換)
  84.      * @param   x (ピクセル 内部でメートルに変換)
  85.      * @param   y(ピクセル 内部でメートルに変換)
  86.      * @param   density 密度 0にすると固定 大きいと、ぶつかったとき強い
  87.      * @param   friction 摩擦 大きいと滑りにくい
  88.      * @param   restitution 跳ね返り 大きいと、反発しやすい
  89.      * @param   viewdata 表示用のSprite
  90.      * @param   viewgroup 表示用のSpriteを入れるSprite
  91.      * @return  b2Body
  92.      */
  93.     protected function makeB2BodyCircle(diameter:int, x:Number, y:Number,
  94.                     density:Number,
  95.                     friction:Number,
  96.                     restitution:Number,
  97.                     viewdata:Sprite = null,
  98.                     viewgroup:Sprite = null) :b2Body {
  99.        
  100.         var b2_boxdef:b2CircleDef = this.createB2CircleDef(diameter,
  101.                             density, friction, restitution);
  102.  
  103.         var b2_bodydef:b2BodyDef = this.createB2BodyDef(x, y, 0,
  104.                             viewdata, viewgroup);
  105.        
  106.         var b2_body:b2Body = this.m_b2_world.CreateBody(b2_bodydef);
  107.         b2_body.CreateShape(b2_boxdef);
  108.         b2_body.SetMassFromShapes();
  109.        
  110.         return b2_body;
  111.     }
  112.    
  113.    
  114.     /**
  115.      * 長方形の性質設定(形状と素材)
  116.      * @param   width (ピクセル 内部でメートルに変換)
  117.      * @param   height (ピクセル 内部でメートルに変換)
  118.      * @param   density
  119.      * @param   friction
  120.      * @param   restitution
  121.      * @return
  122.      */
  123.     private function createB2BoxDef(width:int, height:int,
  124.                     density:Number,
  125.                     friction:Number,
  126.                     restitution:Number):b2PolygonDef {
  127.        
  128.         var boxdef:b2PolygonDef = new b2PolygonDef();
  129.         //B2DBox用単位・中心座標からの距離のサイズに変換して作成
  130.         boxdef.SetAsBox(width / 2 / this.m_b2_physcale,
  131.                 height/ 2 / this.m_b2_physcale);
  132.         //密度 0だと動かない 大きいと、ぶつかったとき強い
  133.         boxdef.density = density;     
  134.         //摩擦 大きいと、滑りにくい
  135.         boxdef.friction = friction;   
  136.         //跳ね返り 大きいと、反発しやすい(ゴムっぽくなる)
  137.         //小さいと、粘土っぽくなる
  138.         boxdef.restitution = restitution; 
  139.        
  140.         return boxdef;
  141.     }
  142.  
  143.    
  144.     /**
  145.      * 円の性質設定(形状と素材)
  146.      * @param   diameter (ピクセル 内部でメートルに変換)
  147.      * @param   height (ピクセル 内部でメートルに変換)
  148.      * @param   density
  149.      * @param   friction
  150.      * @param   restitution
  151.      * @return
  152.      */
  153.     private function createB2CircleDef(diameter:int,
  154.                     density:Number,
  155.                     friction:Number,
  156.                     restitution:Number):b2CircleDef {
  157.        
  158.         var cdef:b2CircleDef = new b2CircleDef();
  159.         //メートル単位・中心座標からの距離のサイズに変換して作成
  160.         cdef.radius = radius / 2 / this.m_b2_physcale//半径
  161.         //密度 0だと動かない
  162.         cdef.density = density;     
  163.         //摩擦 大きいと、滑りにくい
  164.         cdef.friction = friction;   
  165.         //跳ね返り 大きいと、反発しやすい(ゴムっぽくなる)
  166.         //小さいと、粘土っぽくなる
  167.         cdef.restitution = restitution; 
  168.        
  169.         return cdef;
  170.     }
  171.    
  172.    
  173.     /**
  174.      * 配置設定
  175.      * @param   x(ピクセル worldの中心から物体の中心の距離)
  176.      * @param   y(ピクセル)
  177.      * @param   angle 角度 (360度単位)
  178.      * @param   viewdata  表示用Sprite
  179.      * @param   viegroup  表示用Spriteを入れるSprite
  180.      * @return  2BodyDef
  181.      */
  182.     private function createB2BodyDef(x:Number, y:Number, angle:Number,
  183.                     viewdata:Sprite = null,
  184.                     viewgroup:Sprite = null):b2BodyDef {
  185.        
  186.         var bodydef:b2BodyDef = new b2BodyDef();
  187.         //B2DBox用単位に変換して配置
  188.         bodydef.position.Set(x / this.m_b2_physcale, y / this.m_b2_physcale);
  189.         bodydef.angle = (angle / 180) * Math.PI; //ラジアンに変換
  190.        
  191.         //動かないときはスリープ→止まったらイベント受け付けなくなるので起こす
  192.         bodydef.allowSleep = true
  193.        
  194.         if(viewdata != null){
  195.             bodydef.userData = viewdata;           
  196.             bodydef.userData.width = viewdata.width;
  197.             bodydef.userData.height = viewdata.height;
  198.        
  199.             //表示用に追加
  200.             if(viewgroup != null){
  201.                 viewgroup.addChild(bodydef.userData);
  202.             }
  203.         }
  204.        
  205.         return bodydef;   
  206.     }
  207.  
  208.    
  209.     /**
  210.      * 座標指定用のベクトル作成(単位をピクセルからメートルに変換)
  211.      * @param   x
  212.      * @param   y
  213.      * @return  b2Vec2
  214.      */
  215.     protected function createB2Vec(x:Number, y:Number):b2Vec2 {
  216.        
  217.         //単位をb2用に変換
  218.         var vec:b2Vec2 = new b2Vec2(x / this.m_b2_physcale,
  219.                         y / this.m_b2_physcale);
  220.         return vec;
  221.     }
  222.    
  223. }

ベースを使っていろいろやるクラス

Actionscript:
  1. public class StudyB2Base extends B2Base
  2. {
  3.    
  4.     private var m_kinokoL:Sprite;
  5.     private var m_kinokoS:Sprite;
  6.    
  7.     public function StudyB2Base() :void {
  8.        
  9.         super(24, 10, 10);
  10.            
  11.         this.initOka()//丘
  12.         this.initKinokoL()//大きいきのこ
  13.         this.initKinokoS()//小さいきのこ
  14.        
  15.         this.addEventListener(Event.ENTER_FRAME, update, false, 0, true);
  16.        
  17.         //this.setDebugDraw();
  18.     }
  19.  
  20.    
  21.     //丘と地面作る(固定)
  22.     private function initOka():void {
  23.    
  24.         //固定なのでUserDataは入れない
  25.        
  26.         //丘
  27.         this.makeB2BodyBox(80, 200, 140, 380, 0, 0, 0.5, 0.5);
  28.         this.makeB2BodyBox(80, 40, 220, 260, 0, 0, 0.5, 0.5);
  29.         this.makeB2BodyBox(80, 40, 300, 220, 0, 0, 0.5, 0.5);
  30.        
  31.         this.makeB2BodyBox(40, 40, 360, 260, 0, 0, 0.5, 0.5);
  32.         this.makeB2BodyBox(40, 40, 400, 300, 0, 0, 0.5, 0.5);
  33.         this.makeB2BodyBox(40, 40, 440, 340, 0, 0, 0.5, 0.5);
  34.         this.makeB2BodyBox(40, 120, 480, 420, 0, 0, 0.5, 0.5);
  35.        
  36.         //地面
  37.         this.makeB2BodyBox(600, 20, 300, 490, 0, 0, 0.5, 0.5);
  38.     }
  39.    
  40.    
  41.     //丘の上に生えてる大きいきのこ作る
  42.     private function initKinokoL():void {
  43.        
  44.         var self:StudyB2Base = this;
  45.        
  46.         //これはMC配置するだけ
  47.         var kinoko:MovieClip = new kinoko_L_MC;
  48.         kinoko.x = 300;
  49.         kinoko.y = 125;
  50.        
  51.         kinoko.buttonMode = true;
  52.        
  53.         kinoko.addEventListener(MouseEvent.MOUSE_OVER,
  54.                         function(e:Event):void {
  55.                         kinoko.gotoAndPlay("frm_hover");
  56.                     });
  57.                                
  58.         kinoko.addEventListener(MouseEvent.MOUSE_UP,
  59.                     function(e:Event):void {
  60.                         kinoko.gotoAndPlay("frm_action");
  61.                         self.makeKinokoS(Math.random() * 100 + 250, 0);
  62.                     });
  63.                    
  64.         this.m_kinokoL = kinoko;
  65.        
  66.         this.addChild(this.m_kinokoL);
  67.  
  68.     }
  69.    
  70.    
  71.     //小さいきのこ入れるのを作って、小さいきのこ配置
  72.     private function initKinokoS():void {
  73.        
  74.         //小さいきのこを入れる
  75.         this.m_kinokoS = new Sprite();
  76.         this.addChild(this.m_kinokoS);
  77.        
  78.         //最初の小さいきのこ配置
  79.         this.makeKinokoS(120, 0);
  80.         this.makeKinokoS(160, 0);
  81.         this.makeKinokoS(200, 0);
  82.         this.makeKinokoS(240, 0);
  83.         this.makeKinokoS(360, 0);
  84.         this.makeKinokoS(400, 0);
  85.         this.makeKinokoS(440, 0);
  86.         this.makeKinokoS(480, 0);
  87.     }
  88.    
  89.    
  90.     //小さいきのこ作成
  91.     private function makeKinokoS(x:Number, y:Number):void {
  92.        
  93.         var kinoko:Sprite;
  94.         var land = Math.random();
  95.        
  96.         if (land>= 0.6) {
  97.             //普通のきのこ
  98.             kinoko = new kinoko_S1_MC as Sprite;
  99.         }
  100.         else if(land>= 0.3){
  101.             //怪しいきのこ
  102.             kinoko = new kinoko_S2_MC as Sprite;
  103.         }
  104.         else {
  105.             //しめじ
  106.             kinoko = new kinoko_S3_MC as Sprite;
  107.         }   
  108.        
  109.         this.makeB2BodyCircle(30, x, y, 1.0, 0.5, 0.5,
  110.                      kinoko, this.m_kinokoS);
  111.     }
  112.    
  113.    
  114.     //演算
  115.     private function update(e:Event):void{
  116.        
  117.         //B2Boxの時間進める
  118.         this.m_b2_world.Step(this.m_b2_timeStep, this.m_b2_iterations);
  119.        
  120.         //各B2BoxのBodyの動きにあわせて中のSpriteを動かす
  121.         for (var bb:b2Body = this.m_b2_world.m_bodyList; bb; bb = bb.m_next){
  122.            
  123.             var bb_x:Number = bb.GetPosition().x * m_b2_physcale;
  124.             var bb_y:Number = bb.GetPosition().y * m_b2_physcale;
  125.            
  126.             //画面外に出て行ったきのこ削除
  127.             if (bb_x <-50 || bb_x> 650 || bb_y <-50 || bb_y> 550) {
  128.                 this.m_b2_world.DestroyBody(bb);
  129.                 if(bb.m_userData != null){
  130.                     this.m_kinokoS.removeChild(bb.m_userData);
  131.                     bb.m_userData = null;
  132.                 }
  133.             }
  134.            
  135.             //演算結果にあわせて表示用データ移動
  136.             if (bb.m_userData is Sprite){
  137.                 //メートルからピクセル単位に変換
  138.                 bb.m_userData.x = bb_x;
  139.                 bb.m_userData.y = bb_y;
  140.                 bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);
  141.             }
  142.         }
  143.     }
  144.    
  145. }

コメント・トラックバック

  1. 冷吟閑酔 2009-06-18 06:11:47 (ピンバック)

    [...] 物理エンジンライブラリ Box2DFlashAS3(基本) http://hokori.net/2009/02/18/box2dflashas3_base/ [...]

  2. Box2DFlashAS3: 物理演算シミュレーションを可能にするActionScriptライブラリ | DigiTechLog Dot Com 2010-01-28 12:15:16 (ピンバック)

    [...] 物理エンジンライブラリ Box2DFlashAS3(基本) [...]

コメントを送る
※は入力必須です。コメントは管理者の承認後に表示されます。