【cocos2dx v3.0 beta2】【Box2D】Box2Dを使わずにすんげー簡単に物理演算エンジンの恩恵を得る方法 前編


物理演算エンジンについて

cocos2d-xを使用してゲームを開発していると、
Box2DやChipmunkという言葉をよく耳にするかと思います。

両方共、古典力学的な法則をシュミレーションするゲーム用2D物理演算エンジンです。
記述されている言語に大きな違いがあり、

Box2D = C++
Chipmunk = C言語

となっています。

どちらかと言うとBox2Dの方が日本では人気なのでは無いでしょうか。
よって、まずはBox2Dでの実装例をご紹介します。

その後、Box2Dを使わずにすんげー簡単な方法を説明します。
(長くなってしまったので2つに分割します)

cocos2d-x 3.0のBox2Dの実装

cocos2d-x 3.0 beta2で実装するとこのようなコードになります。

Box2DScene.h
#import "cocos2d.h"
#import "Box2D/Box2D.h"
#import "cocos-ext.h"

USING_NS_CC;
USING_NS_CC_EXT;


class Box2DScene : public Layer{
private:
    b2World* world;

    Box2DScene();
    ~Box2DScene();

    static Box2DScene* scene();

    void createWorld();
    void createSprite();

    virtual bool init();
    virtual void update(float delta);
public:
    static Scene* createScene();
    CREATE_FUNC(Box2DScene);

};
Box2DScene.cpp

#import "Box2dScene.h"
#import "GameConfig.h"

Box2DScene::Box2DScene(){}
Box2DScene::~Box2DScene(){}

Scene* Box2DScene::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();

    // 'layer' is an autorelease object
    auto layer = Box2DScene::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

bool Box2DScene::init(){

    createWorld();
    createSprite();
    scheduleUpdate();

    return true;
}

/**
 * 物理演算の影響を受ける世界全体を生成します。
 */
void Box2DScene::createWorld(){
    // 構造体
    b2Vec2 gravity;
    gravity.Set(0, -10);

    world = new b2World(gravity); // 物理演算の全体を管理するワールドを作る
}

/**
 * scheduleUpdate()で呼ばれます。
 * 物理演算の影響で世界の状態は刻一刻と変化するので、その命令を飛ばす。
 * これを行わないと物理演算の影響が行われない。
 */
void Box2DScene::update(float delta){
    // 物理シミュレーションの正確さを決定するパラメータ
    // (まずはこの値にして、ゲームを見ながら調整を行うと良いらしい)
    int velocityIterations = 8;
    int positionIterations = 1;

    // worldを更新する
    world->Step(delta, velocityIterations, positionIterations);
}

void Box2DScene::createSprite(){
    Size winSize = Director::getInstance()->getWinSize();

    /**
     *
     剛体は次の手順で作成する。剛体とは物理エンジンの影響をうけることが出来る物質のこと

      1. 位置・減衰率などで剛体を定義する。
      2. 剛体を作成するためにワールドを使用する。
      3. 図形・摩擦・密度などから装備を定義する。
      4. 剛体から装備を作成する。
     */
    b2Body* body; // 剛体。rigid body。
    b2BodyDef bodyDef; // 剛体の定義
    b2CircleShape shape; // 装備の形状
    b2FixtureDef fixtureDef; // 装備の形状定義

    /* 画像の表示(後でも大丈夫だけど、大きさを取るためにここで) */
    PhysicsSprite* physicsSprite = PhysicsSprite::create("peri1.png");

    // 1. 位置・減衰率などで剛体を定義する。
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set((winSize.width / 2) / PTM_RATIO, (winSize.height) / PTM_RATIO);
    bodyDef.userData = physicsSprite; // 画像をこの剛体に紐付ける

    // 2. 剛体を作成するためにワールドを使用する。
    body = world->CreateBody(&bodyDef);

    // 3. 図形・摩擦・密度などから装備を定義する。
    shape.m_radius = physicsSprite->getContentSize().width / PTM_RATIO; // 形状の大きさ
    fixtureDef.shape = &shape; // 形状を紐付ける
    fixtureDef.density = 1; // 密度
    fixtureDef.friction = 0.9; // 摩擦

    // 4. 剛体から装備を作成する。
    body->CreateFixture(&fixtureDef);

    /** PhysicsSpriteに各種情報をセットする */
    this->addChild(physicsSprite);
    physicsSprite->setB2Body(body);
    physicsSprite->setPTMRatio(PTM_RATIO);
    physicsSprite->setPosition(Point(winSize.width / 2, winSize.height / 2));
}

参考書などを読むとPhysicsSpriteを自作することもありますが、
ここでは、extensions/physics-nodes/CCPhysicsSpriteを使用しています。

以上のようなソースになります。実行してみます。

(写真では分かりづらいですが、落ちてます!)
ソースを見れば分かる通り、地上などを実装しておりませんので、このままではどこまでも落ち続けますが、
ちゃんとBox2Dの恩恵を受けて落下することが確認出来ました。

補足

cocos-extのPhysicsSpriteを使用する時、setPositionなどをsetB2Body前に行うと実行時に落ちます。
ソースを見てみると

PhysicsSprite.cpp
void PhysicsSprite::setPosition(const Point &pos)
{
#if CC_ENABLE_CHIPMUNK_INTEGRATION

    cpVect cpPos = cpv(pos.x, pos.y);
    cpBodySetPos(_CPBody, cpPos);

#elif CC_ENABLE_BOX2D_INTEGRATION

    float angle = _pB2Body->GetAngle();
    _pB2Body->SetTransform(b2Vec2(pos.x / _PTMRatio, pos.y / _PTMRatio), angle);
#endif
}

このように_pB2Bodyを利用しているためです。

また、setPTMRatioを設定しないと画像が表示されません。ご注意を。

実行時のパラメータにCC_ENABLE_BOX2D_INTEGRATION=1を加えるのも忘れずに!
(デフォルトではCC_ENABLE_CHIPMUNK_INTEGRATION=1になっているので、Box2Dを利用する場合はこれを消してください。)

後編につづく!