物理エンジンライブラリ Box2DFlashAS3 ジョイント3(滑車ジョイント・マウスジョイント)

滑車ジョイントとマウスジョイントについてのメモ。
サンプルコードでは、距離ジョイントも使ってます。

ついでに任意で物体に力を加える方法も。

滑車ジョイント(PulleyJoint)

  • 滑車の上に紐で繋がった物体をかけたようなジョイント。
  • 二つの滑車を軸に、紐の長さ分だけ物体が動く。
  • 紐の長さ、伸びやすさ(ゴムっぽくしたり)を指定できる。

使い方

  • 滑車の場所(b2Vec2)を左右指定。
  • 滑車に繋げる物体を指定(b2Body)。
  • ジョイントの性質(b2PulleyJointDef)を設定(長さ等)
  • ジョイントの初期化→ジョイント登録

マウスジョイント(MouseJoint)

  • マウスと物体を繋ぐジョイント。
  • マウス(座標)と他の物体を繋ぐだけ。
  • マウスを動かしたら付いてくる、というような動作は自力で実装。

使い方

  • マウス押した時に、マウス座標の下にある物体を取得(これは自力で作る)
  • マウス座標、取得した物体、引っ張る力など設定(b2MouseJointDef)
  • ジョイント作成(b2MouseJoint)
  • 一定時間毎(EnterFrameイベントなど)マウスドラッグ時のマウス座標を反映させる
  • マウス放したらジョイントを削除

物体に任意で力を加える

bb.ApplyForce(new b2Vec2(0, 200), bb.GetPosition());
(bbはb2Bodyオブジェクト。力を加える方向と、力を加える位置を指定する)

サンプルコード

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

※Box2Dのまとめたクラス(Box2DBase)を作って、基本クラスで使ってます。ソースは物理エンジンライブラリ Box2DFlashAS3(基本)にあります。

マウスジョイントの辺りは、Box2Dの付属コード、TestBedのTest.asのサンプルからほぼコピペです。

Actionscript:
  1. public class StudyB2Joint3 extends B2Base
  2. {
  3.     private var m_display_blocks:Sprite;  //表示用
  4.    
  5.     private var m_left_fook1:b2Body;
  6.     private var m_left_fook2:b2Body;
  7.     private var m_left_box1:b2Body;
  8.     private var m_left_box2:b2Body;  
  9.    
  10.     public function StudyB2Joint3() : void{
  11.        
  12.         super(24, 10, 10);
  13.        
  14.         //滑車ときのこ表示用
  15.         this.m_display_blocks = new Sprite();
  16.         this.addChild(this.m_display_blocks);
  17.        
  18.         //水面
  19.         var water:MovieClip = new water_mc;
  20.         water.y = 350;
  21.         this.addChild(water);
  22.        
  23.         //上下左右の壁作成
  24.         this.makeB2Walls();
  25.        
  26.         //島作成
  27.         this.makeShima();
  28.        
  29.         //滑車表示用のMC
  30.         var kassha_left_fook1:Sprite = new kinoko_fook_mc as Sprite;
  31.         var kassha_left_fook2:Sprite = new kinoko_fook_mc as Sprite;
  32.         var kassha_left_box1:Sprite = new board_100_mc as Sprite;
  33.         var kassha_left_box2:Sprite = new board_100_mc as Sprite;
  34.        
  35.         var kassha_right_fook1:Sprite = new kinoko_fook_mc as Sprite;
  36.         var kassha_right_fook2:Sprite = new kinoko_fook_mc as Sprite;
  37.         var kassha_right_box1:Sprite = new board_50_mc as Sprite;
  38.         var kassha_right_box2:Sprite = new board_50_mc as Sprite;
  39.        
  40.         //左の島の滑車
  41.         this.makeKassha(200, 175, 75, 175, 100, 10, 250,
  42.                         kassha_left_fook1, kassha_left_fook2,
  43.                         kassha_left_box1, kassha_left_box2);
  44.         //右の島の滑車
  45.         this.makeKassha(150, 450, 50, 150, 50, 10, 200,
  46.                         kassha_right_fook1, kassha_right_fook2,
  47.                         kassha_right_box1, kassha_right_box2);
  48.        
  49.         //きのこ
  50.         this.makeKinokos();
  51.        
  52.         //this.setDebugDraw();
  53.        
  54.         this.addEventListener(Event.ENTER_FRAME, this.update, false, 0, true);
  55.        
  56.         this.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown, false, 0, true);
  57.         this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp, false, 0, true);
  58.     }
  59.    
  60.    
  61.     //上下左右の壁作成
  62.     private function makeB2Walls():void {
  63.  
  64.         this.makeB2BodyBox(700, 10, 300, -50, 0, 0, 0.5, 0.5)//上
  65.         this.makeB2BodyBox(700, 10, 300, 550, 0, 0, 0.5, 0.5)//下
  66.         this.makeB2BodyBox(10, 600, -50, 250, 0, 0, 0.5, 0.5)//左
  67.         this.makeB2BodyBox(10, 600, 650, 250, 0, 0, 0.5, 0.5)//右
  68.     }      
  69.  
  70.     //島作成
  71.     private function makeShima():void {
  72.        
  73.         this.makeB2BodyBox(100, 350, 175, 425, 0, 0, 0.5, 0.5);
  74.         this.makeB2BodyBox(100, 400, 450, 400, 0, 0, 0.5, 0.5);
  75.     }
  76.    
  77.     //滑車作成
  78.     private function makeKassha(pulley_w:int, pulley_x:int, pulley_y:int,
  79.                                  fook_y:int,
  80.                                  box_w:int, box_h:int, box_y:int,
  81.                                  disp_fook1:Sprite = null, disp_fook2:Sprite = null,
  82.                                  disp_box1:Sprite = null, disp_box2:Sprite = null):void {
  83.        
  84.         var x1:Number = pulley_x - (pulley_w / 2);
  85.         var x2:Number = pulley_x + (pulley_w / 2)
  86.        
  87.         //フックに繋がる箱
  88.         var box1:b2Body = this.makeB2BodyBox(box_w, box_h, x1, box_y, 0, 1, 0.5, 0.5, disp_box1, this.m_display_blocks);
  89.         var box2:b2Body = this.makeB2BodyBox(box_w, box_h, x2, box_y, 0, 1, 0.5, 0.5, disp_box2, this.m_display_blocks);
  90.        
  91.         //フック
  92.         var fook1:b2Body = this.makeB2BodyBox(10, 10, x1, fook_y, 0, 1, 0.5, 0.5, disp_fook1, this.m_display_blocks);
  93.         var fook2:b2Body = this.makeB2BodyBox(10, 10, x2, fook_y, 0, 1, 0.5, 0.5, disp_fook2, this.m_display_blocks);
  94.        
  95.         //滑車
  96.         var ga1:b2Vec2 = this.createB2Vec(x1, pulley_y);
  97.         var ga2:b2Vec2 = this.createB2Vec(x2, pulley_y);
  98.        
  99.         //滑車ジョイント作成
  100.         var pj_def:b2PulleyJointDef = new b2PulleyJointDef;
  101.         pj_def.Initialize(fook1, fook2, ga1, ga2, fook1.GetPosition(), fook2.GetPosition(), 1);
  102.        
  103.         this.m_b2_world.CreateJoint(pj_def);
  104.        
  105.         //フックは他のものと衝突しないようにする
  106.         fook1.GetShapeList().m_filter.maskBits = 0;
  107.         fook2.GetShapeList().m_filter.maskBits = 0;
  108.        
  109.         //距離ジョイントで左の箱とフックつなぐ
  110.         var box1_anc_L:b2Vec2 = this.createB2Vec(x1 - (box_w / 2) + 5, box_y);
  111.         var box1_anc_R:b2Vec2 = this.createB2Vec(x1 + (box_w / 2) - 5, box_y);
  112.        
  113.         var dj_def1_L:b2DistanceJointDef = new b2DistanceJointDef;
  114.         dj_def1_L.Initialize(fook1, box1, fook1.GetPosition(), box1_anc_L);
  115.        
  116.         this.m_b2_world.CreateJoint(dj_def1_L);
  117.        
  118.         var dj_def1_R:b2DistanceJointDef = new b2DistanceJointDef;
  119.         dj_def1_R.Initialize(fook1, box1, fook1.GetPosition(), box1_anc_R);
  120.        
  121.         this.m_b2_world.CreateJoint(dj_def1_R);
  122.        
  123.         //距離ジョイントで右の箱とフックつなぐ           
  124.         var box2_anc_L:b2Vec2 = this.createB2Vec(x2 - (box_w / 2) + 5, box_y);
  125.         var box2_anc_R:b2Vec2 = this.createB2Vec(x2 + (box_w / 2) - 5, box_y);
  126.        
  127.         var dj_def2_L:b2DistanceJointDef = new b2DistanceJointDef;
  128.         dj_def2_L.Initialize(fook2, box2, fook2.GetPosition(), box2_anc_L);
  129.        
  130.         this.m_b2_world.CreateJoint(dj_def2_L);
  131.        
  132.         var dj_def2_R:b2DistanceJointDef = new b2DistanceJointDef;
  133.         dj_def2_R.Initialize(fook2, box2, fook2.GetPosition(), box2_anc_R);
  134.        
  135.         this.m_b2_world.CreateJoint(dj_def2_R)
  136.     }
  137.    
  138.    
  139.     //きのこを降らせる
  140.     private function makeKinokos():void {
  141.        
  142.         //滑車の上に
  143.         this.makeKinoko(75, 225);
  144.         this.makeKinoko(257, 225);
  145.        
  146.         this.makeKinoko(375, 175);
  147.         this.makeKinoko(525, 175);
  148.        
  149.         //島の上に
  150.         this.makeKinoko(150, 0);
  151.         this.makeKinoko(200, 0);
  152.  
  153.         this.makeKinoko(425, 0);
  154.         this.makeKinoko(475, 0);
  155.     }
  156.    
  157.     //きのこ作成
  158.     private function makeKinoko(x:Number, y:Number):void {
  159.        
  160.         var kinoko:Sprite;
  161.         var land = Math.random();
  162.        
  163.         if (land>= 0.6) {
  164.             //普通のきのこ
  165.             kinoko = new kinoko_S1_MC as Sprite;
  166.         }
  167.         else if(land>= 0.3){
  168.             //怪しいきのこ
  169.             kinoko = new kinoko_S2_MC as Sprite;
  170.         }
  171.         else {
  172.             //しめじ
  173.             kinoko = new kinoko_S3_MC as Sprite;
  174.         }   
  175.        
  176.         this.makeB2BodyCircle(30, x, y, 1.0, 0.5, 0.5, kinoko, this.m_display_blocks);
  177.     }
  178.    
  179.    
  180.     //演算
  181.     private function update(e:Event):void{
  182.  
  183.         //B2Boxの時間進める
  184.         this.m_b2_world.Step(this.m_b2_timeStep, this.m_b2_iterations);
  185.        
  186.         //マウス座標設定
  187.         this.updateMouseWorld();
  188.        
  189.         //マウスドラッグ時の設定
  190.         this.mouseDrag();
  191.        
  192.        
  193.         //滑車とフック・箱の間の線を引く
  194.         var self:StudyB2Joint3 = this;
  195.         //Body位置から描画位置移動する関数
  196.         var moveTo_Pos:Function = function(graphics:Graphics, anchor:b2Vec2):void {
  197.            
  198.             var x:Number = anchor.x * self.m_b2_physcale;
  199.             var y:Number = anchor.y * self.m_b2_physcale;
  200.        
  201.             graphics.moveTo(x, y);
  202.         }
  203.        
  204.         //Body位置へ線を引く関数
  205.         var lineTo_Pos:Function = function(graphics:Graphics, anchor:b2Vec2):void {
  206.            
  207.             var x:Number = anchor.x * self.m_b2_physcale;
  208.             var y:Number = anchor.y * self.m_b2_physcale;
  209.  
  210.             graphics.lineTo(x, y);
  211.         }   
  212.        
  213.         //線表示用ディスプレイオブジェクト
  214.         var disp_gra:Graphics = this.m_display_blocks.graphics;
  215.         disp_gra.clear();
  216.         disp_gra.lineStyle(1, 0x9FD06F);
  217.        
  218.         //worldに登録してあるBodyの動きを描画する
  219.         for (var bb:b2Body = this.m_b2_world.m_bodyList; bb; bb = bb.m_next){
  220.             if (bb.IsDynamic()) {
  221.                
  222.                 //Bodyの動きにあわせて中のSpriteを動かす
  223.                 if (bb.m_userData is Sprite){
  224.                     //メートルからピクセル単位に変換
  225.                     bb.m_userData.x = bb.GetPosition().x * m_b2_physcale;
  226.                     bb.m_userData.y = bb.GetPosition().y * m_b2_physcale;
  227.                     bb.m_userData.rotation = bb.GetAngle() * (180/Math.PI);
  228.                 }
  229.                
  230.                 //ジョイントを持ってるもののみ、線を描画
  231.                 if (bb.m_jointList is b2JointEdge) {
  232.                     for (var joint:b2Joint = bb.m_jointList.joint; joint; joint=joint.m_next){
  233.                         //ジョイントのタイプで分岐)
  234.                         if (joint.m_type == b2Joint.e_pulleyJoint) {
  235.                             //滑車ジョイントの場合(滑車とフックを線で繋ぐ)
  236.                             var joint_p:b2PulleyJoint = joint as b2PulleyJoint;
  237.                             moveTo_Pos(disp_gra, joint_p.GetGroundAnchor1());
  238.                             lineTo_Pos(disp_gra, joint_p.GetAnchor1());
  239.                             moveTo_Pos(disp_gra, joint_p.GetGroundAnchor2());
  240.                             lineTo_Pos(disp_gra, joint_p.GetAnchor2());
  241.                         }
  242.                         else {
  243.                             //その他のジョイント(アンカー同士を線で繋ぐ)
  244.                             //ここでは距離ジョイントとマウスジョイント
  245.                             moveTo_Pos(disp_gra, joint.GetAnchor1());
  246.                             lineTo_Pos(disp_gra, joint.GetAnchor2());
  247.                         }
  248.                     }
  249.                 }
  250.                
  251.                 //水面に浮かぶような感じを出してみる(物体に任意で力を加える)
  252.                 var bb_y:Number = bb.GetPosition().y * m_b2_physcale;
  253.                 if (bb_y> 350) {               
  254.                     //水中は上方向に力を加える
  255.                     var force:b2Vec2;
  256.                     if (bb_y> 500) {
  257.                         //深いところは多めに力を加える
  258.                         force = new b2Vec2(0, -200);
  259.                     }
  260.                     else {
  261.                         force = new b2Vec2(0, -100);
  262.                     }
  263.                     bb.ApplyForce(force, bb.GetPosition());
  264.                 }
  265.                 else if(bb_y <= 350 && bb_y> 250){
  266.                     if (bb.GetLinearVelocity().y <0) {
  267.                         //水面近くは下方向に力を加える(上方向に向かってる場合のみ)
  268.                         bb.ApplyForce(new b2Vec2(0, 200), bb.GetPosition());
  269.                     }
  270.                 }
  271.             }
  272.         }
  273.     }
  274.    
  275.    
  276.     //マウスジョイント設定    ---------------------------------------------------------------------
  277.     //Box2D付属のGeneralのInputをほぼこコピペ
  278.    
  279.     // world mouse position
  280.     private var m_mouseJoint:b2MouseJoint;  //マウスジョイント
  281.    
  282.     private var m_mouseXWorldPhys:Number//メートル単位のマウス座標
  283.     private var m_mouseYWorldPhys:Number;
  284.  
  285.     private var m_mouse_dowm:Boolean = false//マウスボタン押されてるか
  286.    
  287.     private function onMouseDown(e:Event):void {
  288.         this.m_mouse_dowm = true;
  289.     }
  290.    
  291.     private function onMouseUp(e:Event):void {
  292.         this.m_mouse_dowm = false;
  293.     }
  294.    
  295.     //マウス座標取得
  296.     public function updateMouseWorld():void{
  297.         //メートル単位のマウス座標に変換
  298.         m_mouseXWorldPhys = this.mouseX / this.m_b2_physcale;
  299.         m_mouseYWorldPhys = this.mouseY / this.m_b2_physcale;
  300.     }
  301.    
  302.     //マウスドラッグ時
  303.     public function mouseDrag():void{
  304.        
  305.         //マウスボタン押した(ジョイントまだ)
  306.         if (this.m_mouse_dowm && !this.m_mouseJoint){
  307.            
  308.             var body:b2Body = GetBodyAtMouse()//マウスの下の物体取得
  309.  
  310.             if (body)
  311.             {
  312.                 var md:b2MouseJointDef = new b2MouseJointDef();
  313.                 md.body1 = this.m_b2_world.GetGroundBody();
  314.                 md.body2 = body;
  315.                 md.target.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
  316.                 md.maxForce = 300000.0 * body.GetMass();
  317.                 md.timeStep = this.m_b2_timeStep;
  318.                 m_mouseJoint = this.m_b2_world.CreateJoint(md) as b2MouseJoint;
  319.                 body.WakeUp();
  320.             }
  321.         }
  322.        
  323.        
  324.         //マウスボタン離した
  325.         if (!this.m_mouse_dowm){
  326.             if (this.m_mouseJoint)
  327.             {
  328.                 this.m_b2_world.DestroyJoint(m_mouseJoint);
  329.                 m_mouseJoint = null;
  330.             }
  331.         }
  332.        
  333.        
  334.         //マウス押されてる状態(ジョイントされてる)
  335.         if (this.m_mouse_dowm && this.m_mouseJoint)
  336.         {
  337.             var p2:b2Vec2 = new b2Vec2(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
  338.             m_mouseJoint.SetTarget(p2);
  339.         }
  340.     }
  341.    
  342.    
  343.     //マウスの下の物体取得
  344.     private var mousePVec:b2Vec2 = new b2Vec2();
  345.     public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
  346.         // Make a small box.
  347.         mousePVec.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
  348.         var aabb:b2AABB = new b2AABB();
  349.         aabb.lowerBound.Set(this.m_mouseXWorldPhys - 0.001, this.m_mouseYWorldPhys - 0.001);
  350.         aabb.upperBound.Set(this.m_mouseXWorldPhys + 0.001, this.m_mouseYWorldPhys + 0.001);
  351.        
  352.         // Query the world for overlapping shapes.
  353.         var k_maxCount:int = 10;
  354.         var shapes:Array = new Array();
  355.         var count:int = this.m_b2_world.Query(aabb, shapes, k_maxCount);
  356.         var body:b2Body = null;
  357.         for (var i:int = 0; i <count; ++i)
  358.         {
  359.             if (shapes[i].GetBody().IsStatic() == false || includeStatic)
  360.             {
  361.                 var tShape:b2Shape = shapes[i] as b2Shape;
  362.                 var inside:Boolean = tShape.TestPoint(tShape.GetBody().GetXForm(), mousePVec);
  363.                 if (inside)
  364.                 {
  365.                     body = tShape.GetBody();
  366.                     break;
  367.                 }
  368.             }
  369.         }
  370.         return body;
  371.     }
  372.  
  373. }

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

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

    [...] AS3 ジョイント3(滑車ジョイント・マウスジョイント) http://hokori.net/2009/04/29/box2dflashas3_joint3/ [...]

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