[cocos2d-x v3] 物理エンジンを使おう

前回は、Pepperで検索して保存した画像をメールに添付して送信するする方法についてご紹介しました。
今回は、cocos2d-xで物理エンジンを使用する方法をご紹介します。

v2系統のcocos2d-xで物理エンジンを使用するには、extensionとして用意されていたBox2dもしくはchipmunkのライブラリを使用しなければいけませんでしたが、v3になってからはcocos2d-xフレームワークが物理エンジン(※1)の処理を内包するようになったため、cocosライクな書き方で非常に簡単に物理オブジェクトを扱うことが可能となりました。
※1.chipmunkに統一されました。

さっそく使い方をみていきましょう。

まずはworldの生成です。
通常、cocos2d-xでSceneを作成するときはcreateScene関数を用意し、その中でSceneクラスが持つcreate関数によってsceneを生成していますが、物理オブジェクトを使用する時は Sceneクラスが持つcreateWithPhysics関数を用いてSceneを生成します。

Scene* PhysicsSample::createScene()
{
    auto scene = Scene::createWithPhysics();

    auto world = scene->getPhysicsWorld();
    world->setGravity( Vec2( 0, -2000 ) );
    world->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);

    auto layer = PhysicsSample::create();
    scene->addChild(layer);

    return scene;
}

setGravity関数で重力を設定します。 デフォルト値はY軸方向に-98です。
setDebugDrawMask関数でデバッグモードを有効にすると、物理オブジェクトの領域を可視化することができます。
Sprite同士はぶつかっているのに何も起こらない時は、物理オブジェクトがずれて設定されている可能性があるため、開発中は積極的に使用した方がいいと思います。

■デバッグモード無

■デバッグモード有

次に、物理オブジェクトをもつSpriteの生成です。

通常と同じく、Spriteを生成します。
その後、PhysicsBodyクラスのインスタンスを生成し、大きさや質量といったパラメータを設定し、最後にSpriteに生成した物理オブジェクトを適応します。

    auto sprite1 = Sprite::create("chara0.png");
    auto sprite1Body = PhysicsBody::createBox( sprite1->getContentSize(), PhysicsMaterial( 1.0f, 0.1f, 0.8f ) );
    sprite1Body->setMass( 1 );
    sprite1Body->setRotationEnable(false);
    sprite1->setPhysicsBody( sprite1Body );
    sprite1->setPosition( Vec2( visibleSize.width / 2, visibleSize.height / 3 ) );
    sprite1->setName("sprite1");
    this->addChild( sprite1, 0, 0 );

createBox関数の第1引数で物理オブジェクトの大きさを設定し、第2引数で「密度・反発係数・摩擦係数」の3つを設定しています。
今回作成している物理オブジェクトは、生成したSpriteのコンテンツサイズを設定しているため、Spriteと同じ大きさとなります。
setMass関数では、重さを設定しており、処理の中で物体に力を加えた時の、動きかたに影響してきます。
大きい値を設定する程、物体に動く量は少なくなります。
setRotationEnable関数では、オブジェクトの回転有無を設定します。
今回はfalseを設定しているので、物理オブジェクトの回転はありません。
横スクロールアクションゲームを作成する際は、キャラクターの物理オブジェクトにこの設定を行うことで、常に直立状態を保つことが出来ます。

ここまでで、物理オブジェクトを持つworldとSpriteの生成が終わりました。
ですが、このままではせっかく生成した物理オブジェクトは起動と同時に下に落ちて、最終的にはフレーム合うとしてしまうので、Spriteを受けるための床を生成します。

    auto floor = Sprite::create( "floor.png" );
    auto floorBody = PhysicsBody::createBox( floor->getContentSize() );
    floorBody->setDynamic(false);
    floor->setPhysicsBody( floorBody );
    floor->setPosition( Vec2( visibleSize.width * 0.5, visibleSize.height * 0.1 ) );
    floor->setName("floor");
    addChild( floor );

先ほどは使用していない、setDynamic関数というものが出てきました。
この関数は、物理オブジェクトを動体として扱うかどうかの設定です。
物理オブジェクトには、大きく分けて静体と動体があります。
静体とは、一切の物理的な力を受け付けないオブジェクトです。 worldに設定した重力の影響も受けなければ、他のオブジェクトとぶつかったときに動くことも有りません。
動体とは、生体とは逆で、重力の影響でオブジェクトが勝手に落ちていったり、力が加わった時その力の方向へオブジェクトが移動します。
今回作成した、床は1つめに作成したSpriteを受ける目的で作成したため、静体として扱うためにsetDynamicの設定をfalseにしています。

setDynamic関数と似た働きをするもので、setGravityEnableという関数も有ります。
こちらも重力の影響を無効にするという点ではsetDynamic関数と同じですが、setDynamic関数と違うところは、静体ではなく動体であるという点です。
つまり、他オブジェクトから力が加わったときは、力の方向へ移動することになります。
「一見静止しているように見えるが、キャラクターが乗るととたんに動いてしまう」といった簡単なトラップのようなものを作成したいときには、便利かもしれません。

これで、基本的な物理オブジェクトは生成することが出来るようになりました。
最後は、オブジェクトの衝突イベントの検知です。
敵オブジェクトを用意したゲームの場合、「敵にぶつかってやられる」「敵をつぶして倒す」といったイベント処理をしたい時に、必要になってきます。

このような衝突イベントを取得するには、タッチイベントと同じくコールバック関数(衝突検知イベント用リスナー)をイベントリスナーへ登録をし、物理オブジェクトの衝突ビットマスク設定を行います。
●リスナー登録

//init関数内
//イベントリスナーの登録
auto contactListener = EventListenerPhysicsContact::create();
contactListener->onContactBegin = CC_CALLBACK_1( PhysicsSample::onContactBegin, this );
this->getEventDispatcher()->addEventListenerWithSceneGraphPriority( contactListener, this );


bool PhysicsSample::onContactBegin( PhysicsContact& constact )
{
    CCLOG("衝突しました");

    //衝突物Aの物理オブジェクト取得
    auto shapeA = constact.getShapeA()->getBody();
    //衝突物Bの物理オブジェクト取得
    auto shapeB = constact.getShapeB()->getBody();
    //衝突物AのNode取得
    auto nodeA = shapeA->getNode();
    //衝突物BのNode取得
    auto nodeB = shapeB->getNode();

    return true;
}

引数はPhysicsContact型となっており、中には衝突オブジェクトの情報や、イベントの情報が格納されています。

●衝突ビットマスク設定

    sprite1Body->setCategoryBitmask( 0x01 );
    sprite1Body->setContactTestBitmask( 0x01 );

   sprite2Body->setCategoryBitmask( 0x01 );
    sprite2Body->setContactTestBitmask( 0x04 );

setCategoryBitmask関数で自身のビットマスク値を設定します。
setContactTestBitmask関数では、自身が衝突を検知する対象のビットマスク値を設定します。
上記を例にすると、sprite1Bodyを持つオブジェクトは、0x01( 0001 )のカテゴリ値を持つオブジェクトとの衝突を検知し、sprite2Bodyは0x04( 0100 )のカテゴリ値を持つオブジェクトとの衝突を検知します。

setContactTestBitmask設定は、衝突オブジェクト双方に設定する必要が有り、どちらかの設定が不十分だと衝突イベントのコールバックは実行されないのでご注意ください。

以上で、cocos2d-xで物理オブジェクトを使用する方法のご紹介は終了です。
最後までご覧頂き、ありがとうございました。


弊社では全国各地の請負い(ご自宅)で作業協力頂ける、フリーランスエンジニアの方を常時探しております。
ご興味ある方は、お気軽にお問い合わせ下さい。


コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

*