物理エンジンライブラリ Box2DFlashAS3 衝突判定と衝突のグループ・カテゴリ分け

Box2Dの衝突判定と、衝突のグループ・カテゴリ分けをする方法のメモです。

衝突判定

Box2Dでは、衝突が起こった物体同士のリストが作成させるので、そのリストを取得して、衝突判定をします。
衝突の起こった物体同士は、b2Worldクラスのm_contactList(ContactListクラス)にb2Contactオブジェクトとして入れられます。
このm_contactListを、Box2Dの演算時に、順次取り出して、衝突時の処理をします。

衝突のグループ・カテゴリ分け

Box2Dで作った物体は、デフォルトでは全ての物体と衝突するようになっています。
これを、ある物体とは衝突するけどある物体とは衝突しないような、グループ分けをするには、b2ShapeDefクラスのfilter(b2FilterDataクラス)の値を変更します。

衝突のグループ・カテゴリ分けの方法

下記の二種類があって、両方を併用することもできます。

  1. groupIndex を使用する
  2. categoryBit と maskBit を使用する
1.groupIndexを使用する

groupIndexを0以外の値を指定して、グループを作る。値が正負かによって、処理が異なる。

  • の同じ値のグループ内の物体は、maskBit・categoryBitの値が何であれ、必ず衝突する
  • の同じ値のグループ内の物体は、maskBit・categoryBitの値が何であれ、必ず衝突しない

別の値のグループとは、maskBit・categoryBitの値によって、衝突判定が決まる。

2.categoryBit と maskBit を使用する

16進数4桁(2進数16桁)の categoryBit を指定。(0x0001)
maskBitも同じく16進数4桁をつける
categoryBitとmaskBitを論理積(AND)で演算して、0にならない物体同士は衝突。

	Aグループ:
		categoryBit=0x0001(0000 0000 0000 0001)
	Bグループ:
		categoryBit=0x0002(0000 0000 0000 0010)
	Cグループ:
		categoryBit=0x0004(0000 0000 0000 0100)

	各グループ内の物体同士を衝突させる
		A
		maskBit=0x0003(0000 0000 0000 0011)
		B
		maskBit=0x0003(0000 0000 0000 0011)
		C
		maskBit=0x0003(0000 0000 0000 0100)

	各グループ内の物体同士と、AとBグループの物体同士を衝突させる
		AとB
		maskBit=0x0006(0000 0000 0000 0011)
		C
		maskBit=0x0006(0000 0000 0000 0100)

maskBit・categoryBitのみで衝突のグループ分けしたい場合は、groupIndexをデフォルトの0のままにしておく

Body作成後に、m_filterの値を変えた場合は、b2WorldクラスのRefilterメソッドを使って、フィルターを再設定してやる必要があるようです。
変えなくてもフィルター設定反映されたのですが、念のため。

サンプルコード

実行結果(きのこと箱)
きのこと箱

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

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

Actionscript:
  1. public class StudyB2Contact extends B2Base
  2. {   
  3.    
  4.     public function StudyB2Contact()
  5.     {      
  6.         makeB2Walls();
  7.         makeGroup();
  8.        
  9.         this.addEventListener(Event.ENTER_FRAME, update, false, 0, true);
  10.        
  11.         //マウスジョイント用
  12.         this.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown, false, 0, true);
  13.         this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp, false, 0, true);
  14.     }
  15.    
  16.     //Box2D 上下左右の壁作成
  17.     private function makeB2Walls():void {
  18.        
  19.         //デフォルトの衝突フィルターの値
  20.         //categorybit = 0x0001
  21.         //maskBit = 0xFFFF
  22.         //groupIndex = 0
  23.         this.makeB2BodyBox(620, 10, 300, 5, 0, 0, 0.5, 0.5)//上
  24.         this.makeB2BodyBox(620, 10, 300, 495, 0, 0, 0.5, 0.5)//下
  25.         this.makeB2BodyBox(10, 520, 5, 250, 0, 0, 0.5, 0.5)//左
  26.         this.makeB2BodyBox(10, 520, 595, 250, 0, 0, 0.5, 0.5)//右
  27.     }
  28.    
  29.  
  30.     //衝突のグループ分けした物体作成
  31.     private function makeGroup():void {
  32.        
  33.         //表示用Sprite
  34.         var disp_box = new Sprite();
  35.         this.addChild(disp_box);
  36.        
  37.         var disp_item = new Sprite();
  38.         this.addChild(disp_item);
  39.        
  40.        
  41.         var i:int;
  42.         var s_item_num:int = 3;
  43.        
  44.         var item_size:int = 40;
  45.         var box_size:int = 100;
  46.        
  47.         var category_bit:uint;
  48.         var mask_bit:uint;
  49.        
  50.        
  51.         //赤と赤、緑と緑、赤と緑は衝突
  52.         //壁とも衝突
  53.        
  54.         //赤色
  55.         category_bit = 0x0002; // 0000 0000 0000 0010
  56.         mask_bit =     0x0007; // 0000 0000 0000 0111
  57.        
  58.         var box_red:Sprite = new box_red_mc as Sprite;
  59.         this.setBox(box_size, box_size, 120, 150, box_red, disp_box, category_bit, mask_bit);
  60.  
  61.         for (i = 0; i <s_item_num; i++) {
  62.             var kinoko_red:Sprite = new kinoko_red_mc as Sprite;
  63.             this.setCircle(item_size, Math.random() * 500, 20, kinoko_red, disp_item, category_bit, mask_bit);
  64.         }
  65.        
  66.         //緑色
  67.         category_bit = 0x0004; // 0000 0000 0000 0100
  68.         mask_bit =     0x0007; // 0000 0000 0000 0111
  69.        
  70.         var box_green:Sprite = new box_green_mc as Sprite;
  71.         this.setBox(box_size, box_size, 240, 350, box_green, disp_box, category_bit, mask_bit);
  72.  
  73.         for (i = 0; i <s_item_num; i++) {
  74.             var kinoko_green:Sprite = new kinoko_green_mc as Sprite;
  75.             this.setCircle(item_size, Math.random() * 500, 20, kinoko_green, disp_item, category_bit, mask_bit);
  76.         }
  77.        
  78.        
  79.         //黄と黄は衝突
  80.         //壁とも衝突
  81.        
  82.         //黄色
  83.         category_bit = 0x0011; // 0000 0000 0001 0001
  84.         mask_bit =     0x0011; // 0000 0000 0001 0001
  85.        
  86.         var box_yellow:Sprite = new box_yellow_mc as Sprite;
  87.         this.setBox(box_size, box_size, 480, 350, box_yellow, disp_box, category_bit, mask_bit);
  88.  
  89.         for (i = 0; i <s_item_num; i++) {
  90.             var kinoko_yellow:Sprite = new kinoko_yellow_mc as Sprite;
  91.             this.setCircle(item_size, Math.random() * 500, 20, kinoko_yellow, disp_item, category_bit, mask_bit);
  92.         }
  93.  
  94.  
  95.         //青は壁以外衝突しない(青と青も衝突しない)
  96.  
  97.         //青色
  98.         category_bit = 0x0008; // 0000 0000 0000 1000
  99.         mask_bit =     0x0001; // 0000 0000 0000 0001
  100.        
  101.         var box_blue:Sprite = new box_blue_mc as Sprite;
  102.         this.setBox(box_size, box_size, 360, 150, box_blue, disp_box, category_bit, mask_bit);
  103.  
  104.         for (i = 0; i <s_item_num; i++) {
  105.             var kinoko_blue:Sprite = new kinoko_blue_mc as Sprite;
  106.             this.setCircle(item_size, Math.random() * 500, 20, kinoko_blue, disp_item, category_bit, mask_bit);
  107.         }         
  108.     }
  109.    
  110.    
  111.     //四角い物体作成
  112.     private function setBox(w:Number, h:Number, x:Number, y:Number, disp_block:Sprite, disp_blocks:Sprite, category_bits:uint, mask_bit:uint):void {
  113.        
  114.         disp_block.width = w;
  115.         disp_block.height = h;
  116.        
  117.         var block:b2Body = this.makeB2BodyBox(w, h, x, y, 0, 0, 0.5, 0.5, disp_block, disp_blocks);
  118.         block.m_shapeList.m_filter.categoryBits = category_bits;
  119.         block.m_shapeList.m_filter.maskBits = mask_bit;
  120.        
  121.         //フィルターの更新
  122.         this.m_b2_world.Refilter(block.m_shapeList);           
  123.     }
  124.    
  125.    
  126.     //丸い物体作成
  127.     private function setCircle(size:Number, x:Number, y:Number, disp_block:Sprite, disp_blocks:Sprite, category_bits:uint, mask_bit:uint):void {
  128.        
  129.         disp_block.width = size;
  130.         disp_block.height = size;
  131.        
  132.         var block:b2Body = this.makeB2BodyCircle(size, x, y, 1, 0.5, 0.5, disp_block, disp_blocks);
  133.         block.m_shapeList.m_filter.categoryBits = category_bits;
  134.         block.m_shapeList.m_filter.maskBits = mask_bit;
  135.        
  136.         //フィルターの更新
  137.         this.m_b2_world.Refilter(block.m_shapeList);           
  138.     }      
  139.    
  140.    
  141.     //演算
  142.     private function update(e:Event):void {
  143.  
  144.         m_b2_world.Step(m_b2_timeStep, m_b2_iterations);
  145.        
  146.         for (var bb:b2Body = m_b2_world.m_bodyList; bb; bb = bb.m_next){
  147.            
  148.             if (bb.m_userData is Sprite){
  149.                 bb.m_userData.x = bb.GetPosition().x * m_b2_physcale;
  150.                 bb.m_userData.y = bb.GetPosition().y * m_b2_physcale;
  151.                 bb.m_userData.rotation = bb.GetAngle() * (180 / Math.PI);
  152.             }
  153.         }
  154.        
  155.         //衝突判定
  156.         for (var cbb:b2Contact = m_b2_world.m_contactList; cbb; cbb = cbb.m_next) {
  157.            
  158.             var userdata1:MovieClip = cbb.m_shape1.m_body.m_userData as MovieClip;
  159.             if (userdata1 is Sprite) {
  160.                 userdata1.gotoAndPlay("contact");
  161.             }
  162.            
  163.             var userdata2:MovieClip = cbb.m_shape2.m_body.m_userData as MovieClip;
  164.             if (userdata2 is Sprite) {
  165.                 userdata2.gotoAndPlay("contact");
  166.             }
  167.         }
  168.        
  169.         //マウスジョイント
  170.         this.updateMouseWorld();
  171.         this.mouseDrag();
  172.     }
  173.    
  174.     //マウスジョイント設定    ---------------------------------------------------------------------
  175.     //Box2D付属のGeneralのInputをほぼこコピペ
  176.    
  177.     // world mouse position
  178.     private var m_mouseJoint:b2MouseJoint;  //マウスジョイント
  179.    
  180.     private var m_mouseXWorldPhys:Number//メートル単位のマウス座標
  181.     private var m_mouseYWorldPhys:Number;
  182.  
  183.     private var m_mouse_dowm:Boolean = false//マウスボタン押されてるか
  184.    
  185.     private function onMouseDown(e:Event):void {
  186.         this.m_mouse_dowm = true;
  187.     }
  188.    
  189.     private function onMouseUp(e:Event):void {
  190.         this.m_mouse_dowm = false;
  191.     }
  192.    
  193.     //マウス座標取得
  194.     public function updateMouseWorld():void{
  195.         //メートル単位のマウス座標に変換
  196.         m_mouseXWorldPhys = this.mouseX / this.m_b2_physcale;
  197.         m_mouseYWorldPhys = this.mouseY / this.m_b2_physcale;
  198.     }
  199.    
  200.     //マウスドラッグ時
  201.     public function mouseDrag():void{
  202.        
  203.         //マウスボタン押した(ジョイントまだ)
  204.         if (this.m_mouse_dowm && !this.m_mouseJoint){
  205.            
  206.             var body:b2Body = GetBodyAtMouse()//マウスの下の物体取得
  207.  
  208.             if (body)
  209.             {
  210.                 var md:b2MouseJointDef = new b2MouseJointDef();
  211.                 md.body1 = this.m_b2_world.GetGroundBody();
  212.                 md.body2 = body;
  213.                 md.target.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
  214.                 md.maxForce = 300000.0 * body.GetMass();
  215.                 md.timeStep = this.m_b2_timeStep;
  216.                 m_mouseJoint = this.m_b2_world.CreateJoint(md) as b2MouseJoint;
  217.                 body.WakeUp();
  218.             }
  219.         }
  220.        
  221.        
  222.         //マウスボタン離した
  223.         if (!this.m_mouse_dowm){
  224.             if (this.m_mouseJoint)
  225.             {
  226.                 this.m_b2_world.DestroyJoint(m_mouseJoint);
  227.                 m_mouseJoint = null;
  228.             }
  229.         }
  230.        
  231.        
  232.         //マウス押されてる状態(ジョイントされてる)
  233.         if (this.m_mouse_dowm && this.m_mouseJoint)
  234.         {
  235.             var p2:b2Vec2 = new b2Vec2(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
  236.             m_mouseJoint.SetTarget(p2);
  237.         }
  238.     }
  239.    
  240.    
  241.     //マウスの下の物体取得
  242.     private var mousePVec:b2Vec2 = new b2Vec2();
  243.     public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
  244.         // Make a small box.
  245.         mousePVec.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
  246.         var aabb:b2AABB = new b2AABB();
  247.         aabb.lowerBound.Set(this.m_mouseXWorldPhys - 0.001, this.m_mouseYWorldPhys - 0.001);
  248.         aabb.upperBound.Set(this.m_mouseXWorldPhys + 0.001, this.m_mouseYWorldPhys + 0.001);
  249.        
  250.         // Query the world for overlapping shapes.
  251.         var k_maxCount:int = 10;
  252.         var shapes:Array = new Array();
  253.         var count:int = this.m_b2_world.Query(aabb, shapes, k_maxCount);
  254.         var body:b2Body = null;
  255.         for (var i:int = 0; i <count; ++i)
  256.         {
  257.             if (shapes[i].GetBody().IsStatic() == false || includeStatic)
  258.             {
  259.                 var tShape:b2Shape = shapes[i] as b2Shape;
  260.                 var inside:Boolean = tShape.TestPoint(tShape.GetBody().GetXForm(), mousePVec);
  261.                 if (inside)
  262.                 {
  263.                     body = tShape.GetBody();
  264.                     break;
  265.                 }
  266.             }
  267.         }
  268.         return body;
  269.     }
  270.    
  271. }

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

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

    [...] ライブラリ Box2DFlashAS3 衝突判定と衝突のグループ・カテゴリ分け http://hokori.net/2009/05/27/box2dflashas3_contact/ [...]

  2. blog.alumican.net » Blog Archive » Box2DFlashAS3での衝突判定 2009-07-05 02:48:47 (ピンバック)

    [...] 参考にさせていただいたページ 物理エンジンライブラリ Box2DFlashAS3 衝突判定と衝突のグループ・カテゴリ分け Box2D:衝突判定のグループ分け Posted in DEVELOPMENT [...]

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