こんにちは。エンジニアの鷲見です。
今回はcocos2d Box2dについて書いてみようと思います。
Box2Dとは
ゲーム用の2D物理エンジンです。
Box2Dを使用することで衝突、摩擦、加速度、密度などさまざまなシミュレートが可能になります。
ここではcocos2d iOS with Box2dプロジェクトを作成し、そのテンプレートを見ていきます。
プロジェクト
プロジェクトからcocos2d iOS with Box2dを選択し、作成します。
作成されたプロジェクトのファイルの拡張子は.mではなく.mmになります。
というのもBox2DがC++で書かれているため、Objective-C++で記述する必要があるためです。
テンプレートをビルドすると以下のようなサンプルが実行可能です。
ではこのテンプレートのキモであるHelloWorldLayer.mmをのぞいてみましょう。
initPhysics
initPhysicsメソッド内でBox2Dを初期化しています。
まずは世界(物理ワールド)を作ります。
旧約聖書の創世記によると神は最初に光を創ったようですが、
ここで行うのは重力等の設定です。
b2Vec2 gravity; // 重力を設定する gravity.Set(0.0f, -10.0f); world = new b2World(gravity);
重力はb2Vec2型で設定します。座標系は左下が(0, 0)
ですので、値にはマイナスがついています。
// Do we want to let bodies sleep? world->SetAllowSleeping(true); world->SetContinuousPhysics(true);
SetAllowSleeping()
ボディをスリープするかを設定します。
trueを設定することで物体が静止したときに、演算を省略するようになります。
SetContinuousPhysics()
trueを設定することで物体が高速で衝突したときに、
貫通してしまわないように連続的な衝突検出を行うようになります。
次に地面を定義し、物体が移動できる範囲を制限しています。
// Define the ground body. b2BodyDef groundBodyDef; groundBodyDef.position.Set(0, 0); // bottom-left corner // Call the body factory which allocates memory for the ground body // from a pool and creates the ground box shape (also from a pool). // The body is also added to the world. b2Body* groundBody = world->CreateBody(&groundBodyDef); // Define the ground box shape. b2EdgeShape groundBox; // bottom groundBox.Set(b2Vec2(0,0), b2Vec2(s.width/PTM_RATIO,0)); groundBody->CreateFixture(&groundBox,0);
b2Body
剛体を作成するクラスです。
剛体とは外部からの力に対して変形しない物体です。
b2EdgeShape
2点を通る直線を作成するクラスです。
コードを見ると引数に2点を指定していることが分かります。
また、座標をPTM_RATIOで除算していますが、
PTM_RATIOはピクセルをメートルに変換するための定数です。
cocos2dではピクセルが長さの単位として扱われますが、
Box2Dではメートルが使われるためです。
同じ手順で上左右をつくります。
// top groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO)); groundBody->CreateFixture(&groundBox,0); // left groundBox.Set(b2Vec2(0,s.height/PTM_RATIO), b2Vec2(0,0)); groundBody->CreateFixture(&groundBox,0); // right groundBox.Set(b2Vec2(s.width/PTM_RATIO,s.height/PTM_RATIO), b2Vec2(s.width/PTM_RATIO,0)); groundBody->CreateFixture(&groundBox,0);
addNewSpriteAtPosition:
addNewSpriteAtPosition:メソッドで画像を追加しています。
CCNode *parent = [self getChildByTag:kTagParentNode]; //We have a 64x64 sprite sheet with 4 different 32x32 images. The following code is //just randomly picking one of the images int idx = (CCRANDOM_0_1() > .5 ? 0:1); int idy = (CCRANDOM_0_1() > .5 ? 0:1); PhysicsSprite *sprite = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)]; [parent addChild:sprite]; sprite.position = ccp( p.x, p.y);
Box2Dというよりはcocos2dのソースですね。
CCRANDOM_0_1は0から1までの数値をランダムに返すマクロです。
4つの画像をランダムに選択してノードに追加しています。
// Define the dynamic body. //Set up a 1m squared box in the physics world b2BodyDef bodyDef; bodyDef.type = b2_dynamicBody; bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO); b2Body *body = world->CreateBody(&bodyDef); // Define another box shape for our dynamic body. b2PolygonShape dynamicBox; dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box // Define the dynamic body fixture. b2FixtureDef fixtureDef; fixtureDef.shape = &dynamicBox; fixtureDef.density = 1.0f; // 密度 fixtureDef.friction = 0.3f; // 摩擦 body->CreateFixture(&fixtureDef); [sprite setPhysicsBody:body];
b2_dynamicBody
物体が静的(b2_staticBody)か動的(b2_dynamicBody)かを設定します。
静的にすると物体は固定されます。デフォルトは静的になっているようです。
SetAsBox
ボックスシェイプを作成します。
SetAsBoxのパラメータはボックスの中心から辺までの距離を指定します。
サンプルでは0.5となっていますので、一辺が1メートルのボックスシェイプとなります。
その他
質量は形状(shape)と密度(density)から自動的に算出されます。
またサンプルプログラムにはありませんが、b2FixtureDefのパラメータにはrestitution(反発)が存在します。
update
updateは毎フレームごとに呼ばれるメソッドです。
int32 velocityIterations = 8; int32 positionIterations = 1; // Instruct the world to perform a single step of simulation. It is // generally best to keep the time step and iterations fixed. world->Step(dt, velocityIterations, positionIterations);
上記2つのパラメータを設定することで、シミュレートの精度を調整できます。
値が高いほど精度が高くなりますが、CPUに負担がかかります。
いかがでしたでしょうか。
Box2Dを使用することで、簡単に物理演算を行うことができ、
いろいろなゲームが作れそうですね。