読者です 読者をやめる 読者になる 読者になる

やっさんの雑記

プログラミングでやってみたこととか

Box2DのポリゴンをC++から使うときの注意点

こんばんはおはようございますこんにちは。

今回は、実験の自由課題でBox2Dを使って物理シミュレーションをしたのですが、そのときにちょっとハマった点を書きます。

Box2Dとは

ハマったところを書く前に、とりあえずBox2Dの簡単な紹介です。

Box2D(http://box2d.org/)は、2次元平面上での物理シミュレーションをするためのライブラリです。zlibライセンスで配布されています。

2013/12/02現在の最新版はBox2D 2.3.0です。

もともとはC++用のライブラリですが、JavaとかActionScriptとか.NETとか向けに有志によって移植されたものもあるようです。

ポリゴンの座標系

さてここがハマったところです。

ちなみに、コーディング中はずっと、Box2Dを公式サイトから落としてくるときについてくるドキュメントはもちろんのことながら、それに加えてnitoyon氏のてっく煮ブログの記事を参考にしてました(情報が古いししかもC++じゃなくてActionScriptですが)。

ポリゴンを生成するとき、次のようなコードを書いたとします。

b2Vec2 grabity(0.0f, 9.8f);   // 重力加速度9.8
b2World world(grabity);
b2BodyDef polygonDef;
polygonDef.type = b2_dynamicBody;
polygonDef->positon.Set(0.0f, 0.0f); // 位置を原点にセット
auto polygonBody = world.CreateBody(&polygonDef);
const int verticesCount = 5;   // 頂点数
b2Vec2 vertices[verticesCount];
vertices[0].Set(1.0f, 1.0f);
vertices[1].Set(2.0f, 1.0f);
vertices[2].Set(3.0f, 2.0f);
vertices[3].Set(2.0f, 3.0f);
vertices[4].Set(1.0f, 2.0f);
b2PolygonShape polygonShape;
polygonShape.Set(vertices, verticesCount);
b2MassData polygonMassData;
polygonShape.ComputeMass(&polygonMassData, 1.0f);  // 密度は1.0
b2FixtureDef polygonFixture;
polygonFixture.friction = 0.3f;    // 摩擦係数0.3
polygonFixture.restitution = 0.7;  // 反発係数0.7
polygonFixture.density = 1.0f;     // 密度1.0 上で設定してるから多分書かなくていい
polygonFixture.shape = &polygonShape;
polygonBody->CreateFixture(&polygonFixture);
polygonBody->SetMassData(&polygonMassData);

これでworldにポリゴンを設定できたと思います(実際に実験課題で書いたソースコードは該当部分が都合上断片化してたりしてもしかすると少し足りなかったりコンパイル通らなかったりするかもしれませんが、ご容赦ください)。

さて、参考にしたと上で書いたブログ(上のリンクとは同じブログ内の別のページだったかもしれませんが)には、「polygonBody->GetPositon()して得られるのは重心の座標」みたいなこと書いてあったように思います(書いてなかったらすみません)。

しかし実際に動かしてみると、次のようになりました。

  • polygonBody->SetPosition()したときに設定する座標は、そのポリゴンの位置を参照するときに使う基準の座標
  • 当たり判定は普通に、polygonShape.Setで設定した座標を基準として行う
  • polygonBody->GetPosition()で返ってくる座標は、polygonShape.Setで設定したポリゴンの重心の座標が平行移動したぶんだけ、polygonBody->SetPosition()で設定した座標を平行移動させたもの

要するに、polygonBody->SetPosition()だとかpolygonBody->GetPosition()だとかは重心云々とはそんなに関係ないようです。

実験課題ではこれまわりのデバッグに一番時間をとられました。似たものがたくさんでてきて非常にややこしい。

ポリゴンの頂点数

Box2Dの公式ドキュメントには「ポリゴンの頂点数はb2_maxPolygonVertices(デフォルトでは8)以下じゃないとダメ」とか書いてありますが、高精度なポリゴン計算をしようとしてこの値を動的に書き換えようとしても、b2_maxPolygonVerticesはなんと次のように定義されてるので動的には書き換えられません。

#define b2_maxPolygonVertices 8

また、余裕をもって大きな値にしようとしてもメモリ制限か何かでそれはできないようです。

1つの物体に対して複数の図形を登録して、登録した図形があわさった形を作ることはできるようなので、頂点数が多い図形やそもそも凸でない図形を扱いたいときはおとなしくポリゴンの三角形分割とかを考えましょう(上のてっく煮ブログの記事に、八角形に分割するActionScriptのコードが載っているので、それを真似してC++で書けばできるんじゃないかなあと思います)。

その他

この記事を書いてて気づいたんですが、Box2D 2.3.0から、ポリゴンの頂点を指定するときに反時計回りの凸包じゃないといけない云々は考えなくてもよくなったようですね。頂点を指定するときに自動的に凸包を計算して反時計回りに頂点が並ぶよう修正してくれるようです。それ以前のバージョンでは凸包を計算する(座標直打ちのときはその頂点が本当に凸包になっているかどうか計算する)必要があって、また時計回りには頂点が並ばないよう注意する必要があったので、かなり楽になったようですね。

また、ある程度以上近いポリゴンの頂点は併合されてしまうようになったらしいので、あまりに小さいポリゴンを作りたいときなどには注意が必要です。

最後に

実験課題ではそんなに重くなるような計算をさせたわけではないので、計算が高速かどうかはよくわかりませんが、2Dシミュレーション向けには結構使えそうですね。

Web上に日本語資料があまりないのが難点ですが。