| INDEX |

最近久しぶりにAS2を触ったりしています。始める前は「ぬぅぅ」とか唸ってましたが、実際書いてみるとAS2のやわらかい感じが「けっこう楽しいね」とか思ったりとか。今ではFlashDevelopでAS2も書けるし、案外ストレスもないし。
とAS2とは関係ないですが、Box2Dの回転Jointのテストしてみようと。AS3はライブラリが豊富なのが魅力ですね。
Box2Dは、Box2DFlashAS3 2.0.1です。

今回行ったのは回転Joint(RevoluteJoint)のテストです。何かしらのオブジェクトをドラッグしつつ、クリック座標で回転する的な。とりあえずSWFはこんな感じで。
小さい「□」はJoint用に用意したオブジェクトです。ドラッグするのは大きい「□」です。

まず回転Jointは、以下のような手順で書くとよいらしい。定義をつくって実体化って流れがオブジェクトつくるときと同じですね。

//とりあえずJoint定義
var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
//つなげるb2Body(オブジェクトの実体)と繋げる位置を指定
jointDef.Initialize( b2body_1, b2body_2, b2Vec2_joint_pos );
//b2Worldインスタンスで実体化する
var joint:b2Joint = b2world.CreateJoint(jointDef);

つないだJointを切り離す場合は、こんな感じ

b2world.DestroyJoint( joint );

以上で何かしらつながる、と。

で、マウスでドラッグするようにしたかったので、今回の場合ドラッグの基点(回転の基点)用のダミーオブジェクト(上のサンプルの小さい□)を使ってみた。Jointするには2つのオブジェクトが必要な為です。この方法が正しいの?か知れませんが、表示しなければいいだけなんで、まぁいいかと。

次に、ドラッグらしくする為に、マウスの座標とダミーオブジェクトの座標を合わせる必要がある。逆に合わせれば、後の計算をしてくれるので楽。座標の指定は、SetXForm() メソッドでやってみた。他の方法はあるのかなぁ。

//joint_bodyはダミーオブジェクトの実体
//第2引数は回転だったかな…
joint_body.SetXForm( new b2Vec2(x,y), 0 );

最後に、ダミーオブジェクト自体は衝突計算とかしなくてよいので、ShapeDefのfilter使ってみる。

//0にしとくと、どれとも衝突しなくなる。詳しくは b2FilterData
joint_shapeDef.filter.maskBits = 0;

という手順で以下のソースです。ソース中にある、Box2DUtilBox2DElement は前回テストしたとき、割とBox2Dのお約束なのかなぁと思った部分をClassにしたものです。Box2D は手続きが多いのでHelper的な Class があるともっとサクサクつくれるのだろうなぁ。

TestWorldRigid.as

package  
{
  import Box2D.Common.Math.b2Vec2;
  import Box2D.Dynamics.b2World;
  import Box2D.Dynamics.b2Body;
  import Box2D.Dynamics.Joints.b2RevoluteJointDef;
  import Box2D.Dynamics.Joints.b2Joint;
  import flash.geom.Rectangle;
  import flash.display.MovieClip;
  import flash.events.Event;
  import flash.events.MouseEvent;
  
  /**
  * Box2DなJointテスト
  * @author nutsu
  */
  public class TestWorldRigid extends MovieClip
  {
    private var world:b2World;
    
    private var jointDef:b2RevoluteJointDef;
    private var joint:b2Joint;
    private var joint_body:b2Body;
    private var rigid_body:b2Body;
    
    private var mouse_pos:b2Vec2;
    
    private var WORLD_SCALE:Number = 100; // 100pixel/m
    private var VIEW_RECT:Rectangle;
    
    public function TestWorldRigid() 
    {
      super();
      
      Box2DUtil.scale = WORLD_SCALE;
      Box2DUtil.usePixelSize = true;
      
      //とりあえずワールドつくる
      world = Box2DUtil.mkWorld();
      
      //静的なオブジェクト(四方壁)をつくる
      VIEW_RECT = new Rectangle( 0, 0, stage.stageWidth, stage.stageHeight );
      Box2DUtil.buildWall( world, VIEW_RECT, 10 );
      
      //動的なオブジェクトつくる
      initObjects();
      
      //テストドローな設定
      Box2DUtil.setDebugDraw( this, world );
      
      //マウス座標用のb2Vec2
      mouse_pos = new b2Vec2( 0, 0 )
      
      //イベント
      addEventListener( Event.ENTER_FRAME, onEnterFrame );
      stage.addEventListener( MouseEvent.MOUSE_DOWN, onMouseDown );
      stage.addEventListener( MouseEvent.MOUSE_UP, onMouseUp );
      
      stage.quality = "low";
    }
    
    private function initObjects():void
    {
      //ドラッグして動かすオブジェクト
      var target_obj:Box2DElement;
      target_obj = new Box2DElement( Box2DUtil.defBox( 40, 80, 1, 0.2 ), 
                       Box2DUtil.defBody( VIEW_RECT.width/2, VIEW_RECT.height/2 ) );
      target_obj.build( world );
      
      //ドラッグ位置でJoint作る用のオブジェクト
      var joint_obj:Box2DElement;
      joint_obj = new Box2DElement( Box2DUtil.defBox( 10, 10, 0 ), 
                      Box2DUtil.defBody( VIEW_RECT.width/2, VIEW_RECT.height/2 ) );
      
      //どれにも衝突しないように
      joint_obj.shapeDef.filter.maskBits = 0;
      joint_obj.build( world );
      
      //あとで使うBody
      rigid_body = target_obj.body;
      joint_body = joint_obj.body;
      
      //とりあえずJoint定義
      jointDef = new b2RevoluteJointDef();
    }
    
    private function onMouseDown( e:MouseEvent ):void
    {
      if ( ! joint )
      {
        //クリック位置をワールド座標へ変換
        mouse_pos.x = mouseX / WORLD_SCALE;
        mouse_pos.y = mouseY / WORLD_SCALE;
        
        //オブジェクト上ならドラッグ処理へ
        if ( rigid_body.m_shapeList.TestPoint( rigid_body.m_xf, mouse_pos ) )
        {
          //スリープしないようにする
          world.m_allowSleep = false;
          
          //スリープ状態を起こす
          if ( rigid_body.IsSleeping() )
            rigid_body.WakeUp();
            
          //ジョイント用のダミーオブジェクトの位置をマウス座標へ
          joint_body.SetXForm( mouse_pos, 0 );
          
          //ジョイントする
          jointDef.Initialize( rigid_body, joint_body, joint_body.GetWorldCenter() );
          joint = world.CreateJoint(jointDef);
        }
      }
    }
    
    
    private function onMouseUp(e:MouseEvent):void 
    {
      if ( joint )
      {
        //ジョイント外す
        world.DestroyJoint( joint );
        joint = null;
        
        //スリープ設定もどす
        world.m_allowSleep = true;
      }
    }
    
    private function onEnterFrame( e:Event ):void
    {
      if ( world )
      {
        if ( joint )
        {
          //ジョイント用のダミーオブジェクトの位置をマウス座標へ
          mouse_pos.x = mouseX / WORLD_SCALE;
          mouse_pos.y = mouseY / WORLD_SCALE;
          joint_body.SetXForm( mouse_pos, 0 );
        }
        //計算
        world.Step( 1/30, 10 );
      }
    }
  }
}

今回のテストを行ってみる際も、Box2Dのソースを見ながら調べていましたが、メソッドとか変数名が分かりやすい印象があります。物理計算という何となく「やること」のイメージがあって、イメージにあった名前が見つかって、あぁやっぱりそうね、みたいな。ライブラリって名前大事だなぁと改めて思った今日この頃でした。とやんわり自戒。

| INDEX |