物理エンジンライブラリ Box2DFlashAS3 衝突判定と衝突のグループ・カテゴリ分け
Box2Dの衝突判定と、衝突のグループ・カテゴリ分けをする方法のメモです。
衝突判定
Box2Dでは、衝突が起こった物体同士のリストが作成させるので、そのリストを取得して、衝突判定をします。
衝突の起こった物体同士は、b2Worldクラスのm_contactList(ContactListクラス)にb2Contactオブジェクトとして入れられます。
このm_contactListを、Box2Dの演算時に、順次取り出して、衝突時の処理をします。
衝突のグループ・カテゴリ分け
Box2Dで作った物体は、デフォルトでは全ての物体と衝突するようになっています。
これを、ある物体とは衝突するけどある物体とは衝突しないような、グループ分けをするには、b2ShapeDefクラスのfilter(b2FilterDataクラス)の値を変更します。
衝突のグループ・カテゴリ分けの方法
下記の二種類があって、両方を併用することもできます。
- groupIndex を使用する
- 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のサンプルからほぼコピペです。
-
public class StudyB2Contact extends B2Base
-
{
-
-
public function StudyB2Contact()
-
{
-
makeB2Walls();
-
makeGroup();
-
-
this.addEventListener(Event.ENTER_FRAME, update, false, 0, true);
-
-
//マウスジョイント用
-
this.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown, false, 0, true);
-
this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp, false, 0, true);
-
}
-
-
//Box2D 上下左右の壁作成
-
private function makeB2Walls():void {
-
-
//デフォルトの衝突フィルターの値
-
//categorybit = 0x0001
-
//maskBit = 0xFFFF
-
//groupIndex = 0
-
this.makeB2BodyBox(620, 10, 300, 5, 0, 0, 0.5, 0.5); //上
-
this.makeB2BodyBox(620, 10, 300, 495, 0, 0, 0.5, 0.5); //下
-
this.makeB2BodyBox(10, 520, 5, 250, 0, 0, 0.5, 0.5); //左
-
this.makeB2BodyBox(10, 520, 595, 250, 0, 0, 0.5, 0.5); //右
-
}
-
-
-
//衝突のグループ分けした物体作成
-
private function makeGroup():void {
-
-
//表示用Sprite
-
var disp_box = new Sprite();
-
this.addChild(disp_box);
-
-
var disp_item = new Sprite();
-
this.addChild(disp_item);
-
-
-
var i:int;
-
var s_item_num:int = 3;
-
-
var item_size:int = 40;
-
var box_size:int = 100;
-
-
var category_bit:uint;
-
var mask_bit:uint;
-
-
-
//赤と赤、緑と緑、赤と緑は衝突
-
//壁とも衝突
-
-
//赤色
-
category_bit = 0x0002; // 0000 0000 0000 0010
-
mask_bit = 0x0007; // 0000 0000 0000 0111
-
-
var box_red:Sprite = new box_red_mc as Sprite;
-
this.setBox(box_size, box_size, 120, 150, box_red, disp_box, category_bit, mask_bit);
-
-
for (i = 0; i <s_item_num; i++) {
-
var kinoko_red:Sprite = new kinoko_red_mc as Sprite;
-
this.setCircle(item_size, Math.random() * 500, 20, kinoko_red, disp_item, category_bit, mask_bit);
-
}
-
-
//緑色
-
category_bit = 0x0004; // 0000 0000 0000 0100
-
mask_bit = 0x0007; // 0000 0000 0000 0111
-
-
var box_green:Sprite = new box_green_mc as Sprite;
-
this.setBox(box_size, box_size, 240, 350, box_green, disp_box, category_bit, mask_bit);
-
-
for (i = 0; i <s_item_num; i++) {
-
var kinoko_green:Sprite = new kinoko_green_mc as Sprite;
-
this.setCircle(item_size, Math.random() * 500, 20, kinoko_green, disp_item, category_bit, mask_bit);
-
}
-
-
-
//黄と黄は衝突
-
//壁とも衝突
-
-
//黄色
-
category_bit = 0x0011; // 0000 0000 0001 0001
-
mask_bit = 0x0011; // 0000 0000 0001 0001
-
-
var box_yellow:Sprite = new box_yellow_mc as Sprite;
-
this.setBox(box_size, box_size, 480, 350, box_yellow, disp_box, category_bit, mask_bit);
-
-
for (i = 0; i <s_item_num; i++) {
-
var kinoko_yellow:Sprite = new kinoko_yellow_mc as Sprite;
-
this.setCircle(item_size, Math.random() * 500, 20, kinoko_yellow, disp_item, category_bit, mask_bit);
-
}
-
-
-
//青は壁以外衝突しない(青と青も衝突しない)
-
-
//青色
-
category_bit = 0x0008; // 0000 0000 0000 1000
-
mask_bit = 0x0001; // 0000 0000 0000 0001
-
-
var box_blue:Sprite = new box_blue_mc as Sprite;
-
this.setBox(box_size, box_size, 360, 150, box_blue, disp_box, category_bit, mask_bit);
-
-
for (i = 0; i <s_item_num; i++) {
-
var kinoko_blue:Sprite = new kinoko_blue_mc as Sprite;
-
this.setCircle(item_size, Math.random() * 500, 20, kinoko_blue, disp_item, category_bit, mask_bit);
-
}
-
}
-
-
-
//四角い物体作成
-
private function setBox(w:Number, h:Number, x:Number, y:Number, disp_block:Sprite, disp_blocks:Sprite, category_bits:uint, mask_bit:uint):void {
-
-
disp_block.width = w;
-
disp_block.height = h;
-
-
var block:b2Body = this.makeB2BodyBox(w, h, x, y, 0, 0, 0.5, 0.5, disp_block, disp_blocks);
-
block.m_shapeList.m_filter.categoryBits = category_bits;
-
block.m_shapeList.m_filter.maskBits = mask_bit;
-
-
//フィルターの更新
-
this.m_b2_world.Refilter(block.m_shapeList);
-
}
-
-
-
//丸い物体作成
-
private function setCircle(size:Number, x:Number, y:Number, disp_block:Sprite, disp_blocks:Sprite, category_bits:uint, mask_bit:uint):void {
-
-
disp_block.width = size;
-
disp_block.height = size;
-
-
var block:b2Body = this.makeB2BodyCircle(size, x, y, 1, 0.5, 0.5, disp_block, disp_blocks);
-
block.m_shapeList.m_filter.categoryBits = category_bits;
-
block.m_shapeList.m_filter.maskBits = mask_bit;
-
-
//フィルターの更新
-
this.m_b2_world.Refilter(block.m_shapeList);
-
}
-
-
-
//演算
-
private function update(e:Event):void {
-
-
m_b2_world.Step(m_b2_timeStep, m_b2_iterations);
-
-
for (var bb:b2Body = m_b2_world.m_bodyList; bb; bb = bb.m_next){
-
-
if (bb.m_userData is Sprite){
-
bb.m_userData.x = bb.GetPosition().x * m_b2_physcale;
-
bb.m_userData.y = bb.GetPosition().y * m_b2_physcale;
-
bb.m_userData.rotation = bb.GetAngle() * (180 / Math.PI);
-
}
-
}
-
-
//衝突判定
-
for (var cbb:b2Contact = m_b2_world.m_contactList; cbb; cbb = cbb.m_next) {
-
-
var userdata1:MovieClip = cbb.m_shape1.m_body.m_userData as MovieClip;
-
if (userdata1 is Sprite) {
-
userdata1.gotoAndPlay("contact");
-
}
-
-
var userdata2:MovieClip = cbb.m_shape2.m_body.m_userData as MovieClip;
-
if (userdata2 is Sprite) {
-
userdata2.gotoAndPlay("contact");
-
}
-
}
-
-
//マウスジョイント
-
this.updateMouseWorld();
-
this.mouseDrag();
-
}
-
-
//マウスジョイント設定 ---------------------------------------------------------------------
-
//Box2D付属のGeneralのInputをほぼこコピペ
-
-
// world mouse position
-
private var m_mouseJoint:b2MouseJoint; //マウスジョイント
-
-
private var m_mouseXWorldPhys:Number; //メートル単位のマウス座標
-
private var m_mouseYWorldPhys:Number;
-
-
private var m_mouse_dowm:Boolean = false; //マウスボタン押されてるか
-
-
private function onMouseDown(e:Event):void {
-
this.m_mouse_dowm = true;
-
}
-
-
private function onMouseUp(e:Event):void {
-
this.m_mouse_dowm = false;
-
}
-
-
//マウス座標取得
-
public function updateMouseWorld():void{
-
//メートル単位のマウス座標に変換
-
m_mouseXWorldPhys = this.mouseX / this.m_b2_physcale;
-
m_mouseYWorldPhys = this.mouseY / this.m_b2_physcale;
-
}
-
-
//マウスドラッグ時
-
public function mouseDrag():void{
-
-
//マウスボタン押した(ジョイントまだ)
-
if (this.m_mouse_dowm && !this.m_mouseJoint){
-
-
var body:b2Body = GetBodyAtMouse(); //マウスの下の物体取得
-
-
if (body)
-
{
-
var md:b2MouseJointDef = new b2MouseJointDef();
-
md.body1 = this.m_b2_world.GetGroundBody();
-
md.body2 = body;
-
md.target.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
-
md.maxForce = 300000.0 * body.GetMass();
-
md.timeStep = this.m_b2_timeStep;
-
m_mouseJoint = this.m_b2_world.CreateJoint(md) as b2MouseJoint;
-
body.WakeUp();
-
}
-
}
-
-
-
//マウスボタン離した
-
if (!this.m_mouse_dowm){
-
if (this.m_mouseJoint)
-
{
-
this.m_b2_world.DestroyJoint(m_mouseJoint);
-
m_mouseJoint = null;
-
}
-
}
-
-
-
//マウス押されてる状態(ジョイントされてる)
-
if (this.m_mouse_dowm && this.m_mouseJoint)
-
{
-
var p2:b2Vec2 = new b2Vec2(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
-
m_mouseJoint.SetTarget(p2);
-
}
-
}
-
-
-
//マウスの下の物体取得
-
private var mousePVec:b2Vec2 = new b2Vec2();
-
public function GetBodyAtMouse(includeStatic:Boolean=false):b2Body{
-
// Make a small box.
-
mousePVec.Set(this.m_mouseXWorldPhys, this.m_mouseYWorldPhys);
-
var aabb:b2AABB = new b2AABB();
-
aabb.lowerBound.Set(this.m_mouseXWorldPhys - 0.001, this.m_mouseYWorldPhys - 0.001);
-
aabb.upperBound.Set(this.m_mouseXWorldPhys + 0.001, this.m_mouseYWorldPhys + 0.001);
-
-
// Query the world for overlapping shapes.
-
var k_maxCount:int = 10;
-
var shapes:Array = new Array();
-
var count:int = this.m_b2_world.Query(aabb, shapes, k_maxCount);
-
var body:b2Body = null;
-
for (var i:int = 0; i <count; ++i)
-
{
-
if (shapes[i].GetBody().IsStatic() == false || includeStatic)
-
{
-
var tShape:b2Shape = shapes[i] as b2Shape;
-
var inside:Boolean = tShape.TestPoint(tShape.GetBody().GetXForm(), mousePVec);
-
if (inside)
-
{
-
body = tShape.GetBody();
-
break;
-
}
-
}
-
}
-
return body;
-
}
-
-
}



冷吟閑酔 2009-06-18 06:12:20 (ピンバック)
[...] ライブラリ Box2DFlashAS3 衝突判定と衝突のグループ・カテゴリ分け http://hokori.net/2009/05/27/box2dflashas3_contact/ [...]
blog.alumican.net » Blog Archive » Box2DFlashAS3での衝突判定 2009-07-05 02:48:47 (ピンバック)
[...] 参考にさせていただいたページ 物理エンジンライブラリ Box2DFlashAS3 衝突判定と衝突のグループ・カテゴリ分け Box2D:衝突判定のグループ分け Posted in DEVELOPMENT [...]