だいたい47度

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

PageTop

Box2dの衝突可否制御 categoryBitsとgroupIndex

物理エンジンbox2dにおいて、物体同士の衝突可否を制御したい時があります。物体Aは壁にはぶつかって欲しいけど物体Bにはぶつかってほしくない、のような。

その実装方法として、カテゴリビットによる方法とグループインデックスによる方法(およびその組み合わせ)が選べます。僕は、基本的にはカテゴリビットによる制御を行い、必要な時はgroupIndexを使うのがよいように思います。

カテゴリビット
カテゴリビットは、それぞれのfixtureに設定できる衝突種類のカテゴリのことです。そのカテゴリの物体にぶつかるかどうかはマスクビットで制御できます。

ややこしいので例を使って説明します。

物体A、B、Cについて、それぞれにカテゴリビットを振ってあげます。

typedef enum{
  kCategoryBitTypeA = 0x0001,
  kCategoryBitTypeB = 0x0002,
  kCategoryBitTypeC = 0x0004,
}CategoryBits;

...
//物体Aのfixturedef (B、Cも同様に設定)
fixtureDef.filter.categoryBits = kCategoryBitTypeA;

カテゴリビットは16種類まで設定出来ます。具体的には、0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000の16種類です。0と1の表記にするとわかりやすいですが、これらは1を一つだけ含むような数値です(0x0040 = 0000|0000|0100|0000)。

上記では、A、B、Cそれぞれのカテゴリビットを0x0001, 0x0002, 0x0004としました。enumで共通ファイルに記述しておき、それぞれのfixturedef.filter.categoryBitsに登録してあげています。

次にマスクビットの登録をします。物体Aは、他の物体A、物体Bとはぶつかりますが、物体Cとはぶつからないとしましょう。

//物体Aのfixturedef
fixtureDef.filter.maskBits = kCategoryBitTypeA + kCategoryBitTypeB;

マスクビットには、衝突対象のカテゴリビットの総和を登録することで、どのカテゴリビットの物体と衝突するかを指定します。

上記の例で言うと、値的には0x0001 + 0x0002 = 0x0003がmaskBitsに登録されています。これは01で表記すれば、0000|0000|0000|0011となり、AとBにふられたフラグが立っていることが分かります。そのため、AおよびBと衝突するfixtureと扱われます。0x0004のフラグは立っていないのでCとはぶつかりません。

maskBitsの値は、fixtureDef.filter.maskBits=0x0003と最初から指定することもできますが、上記のように足し算で記述しておくと修正が入った時にも簡単にできます(ベタ書きしていた場合、フラグのビットを修正したりしたら悲惨な目に遭いますね)。

なお、b2EdgeShapeを使っている場合は以下のように書きます。特に変わったところはないですが、b2EdgeShapeを使用する際はfixture設定を無視することが多いので、一瞬どこに書くのか迷うかもしれません。

//fixturedefを使わない場合
b2EdgeShape groundBox;
groundBox.Set(......);
groundBody->CreateFixture(&groundBox, 0);

//fixturedefを使う場合
b2EdgeShape groundBox;
b2FixtureDef fixtureDef;
fixtureDef.filter.categoryBits = kCategoryBitGround;
fixtureDef.filter.maskBits = ......;
fixtureDef.shape = &groundBox;
groundBox.Set(......);
groundBody->CreateFixture(&fixtureDef);

デフォルトの値としては、categoryBits = 0x0001が振られ、maskBits = 0xFFFFが振られています。maskBits = 0xFFFF = 1111|1111|1111|1111のため、あらゆるものと衝突するのですね。

グループインデックス
グループインデックスもカテゴリビット同様、fixturedefのfilterに設定して使います。

fixtureDef.filter.groupIndex = -1;

groupIndexに負の値を与えると、同じgroupIndexがふられた物体とは衝突しなくなります。またgroupIndexが正の物体は必ず衝突するようになります。

グループインデックスの初期値は0に設定されています。

比較
グループインデックスは、カテゴリビットより単純に実装できそうですが、実装できない状況があります。たとえば、「物体Aは物体B、Cとぶつからないようにしたいが、BとC同士はぶつかるようにしたい」という状況はgroupIndexだけでは実装できません。AとBとCに同じgroupIndexを振っては、BとCが衝突しなくなるからです。一方、カテゴリビットなら、maskBitを調整してあげるだけでOKです。

なお、グループインデックスはカテゴリビットよりも優先されます。カテゴリビットとマスクビットを使って衝突するように指定した物体同士でも、おなじgroupIndexを振られると衝突しなくなります。グループインデックスが初期値である0以外の値の場合は、常にグループインデックスがカテゴリビットより優先されるので注意する必要があります。

以上のことから、僕は、基本的にはカテゴリビットによる制御を行い、必要な時はgroupIndexを使うのがよいように考えています。衝突可否制御がほとんどない場合にも、将来の拡張性を考えるとカテゴリビットを使っておくのが安心な気もします。

Learning Cocos2D: A Hands-On Guide to Building iOS Games with Cocos2D, Box2D, and ChipmunkLearning Cocos2D: A Hands-On Guide to Building iOS Games with Cocos2D, Box2D, and Chipmunk
(2011/07/07)
Rod Wenderlich, Ray Strougo

商品詳細を見る
関連記事
スポンサーサイト

PageTop

コメント


管理者にだけ表示を許可する
 

上記について

突然失礼いたします。
Box2dの衝突可否制御について調べており、このページにたどり着き、
大変勉強になりました。

そこでどうしても解らないことがあり、よろしければご教授ください。

たぬきちさんが上記で書かれている、
【物体Aは物体B、Cとぶつからないようにしたいが、BとC同士はぶつかるようにしたい】
これはマスクビットで出来そうなのですが、
その際、物体Aは、物体B,Cとは物理的にぶつからないのですが、
衝突しているという判定は出来るのでしょうか。
いわゆるhittestobjectのような感じです。

物理的には衝突しないのですが、重なっているという判定をしたいなと考えており、そして悩んでおります。

よろしくお願いいたします。



masa | URL | 2014-01-05(Sun)15:53 [編集]


No title

masaさん、こんにちは。
コメントありがとうございます。

物体AとBがBox2D上は衝突しないようにマスクビットを設定しておき、しかしAとBが画面上重なっていることを感知したいということでよいでしょうか?
その場合は、PhysicsSpriteはCCSpriteの子クラスなので、各フレームでAとBのCGRectIntersectsRectを確認すればよいかと思います(質問内容がそういうことではなかったらすみません)。

衝突感知については下記記事ももしかしたら役に立つかもです。
http://tanukichi566.blog.fc2.com/blog-entry-53.html

たぬきち | URL | 2014-01-05(Sun)22:03 [編集]


No title

おはようございます。
早速のお返事ありがとうございます!
まだ一見しかしておりませんが、これから熟読させて頂きます。
取り急ぎお礼をさせて頂きます。

私も引き続き調べていたのですが、
http://www.box2dflash.org/docs/2.0.2/manual#Sensors
この辺りも使えるのかな?と模索中です。

ありがとうございました。

masa | URL | 2014-01-06(Mon)10:10 [編集]


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。