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

#物理演算エンジンについて
cocos2d-xを使用してゲームを開発していると、
Box2DやChipmunkという言葉をよく耳にするかと思います。

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

Box2D = C++
Chipmunk = C言語

となっています。

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

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

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

```cpp: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);
    
};
```

```cpp: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を使用しています。

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

![cocoperi1.png](https://qiita-image-store.s3.amazonaws.com/0/26453/8edbb410-936d-8b8f-d9ba-23a28465f476.png)

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

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

```cpp: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を利用する場合はこれを消してください。）

後編につづく！