cocos2d-x 3.2 + Box2D iOSでDebugDrawの実行まで

Cocoa部さんのcocos2d-xでBOX2Dを使う入門編( http://cocoabu.com/box2d-00 )
をやろうとしたらいきなりハマったのでメモ。

結論から書くとBox2Dを導入してDebugDrawを実行するステップは, 

1) cocos2d/extensions/cocos-ext.h 内のショートカットに

```cpp:cocos-ext.h
#ifndef __COCOS2D_EXT_H__
#define __COCOS2D_EXT_H__

#include "ExtensionMacros.h"

#include "GUI/CCControlExtension/CCControlExtensions.h"
#include "GUI/CCScrollView/CCScrollView.h"
#include "GUI/CCScrollView/CCTableView.h"
#include "GUI/CCEditBox/CCEditBox.h"
#include "../external/Box2D/Box2D.h" // ←Add!!

// Physics integration
#include "physics-nodes/CCPhysicsDebugNode.h"
#include "physics-nodes/CCPhysicsSprite.h"

#include "assets-manager/AssetsManager.h"

#endif /* __COCOS2D_EXT_H__ */
```

にBox2Dへのパスを追加
（これでextensionのヘッダファイルをインポートすればBox2Dも呼び出せるようになる）

2) GL-ESRender.h, GL-ESRender.cppをインポート
テストの中に入っているものでもいいし、私は
http://blog.csdn.net/tian2kong/article/details/20386213
から拝借しました。そのままClassesフォルダの中に入れるだけでよいです。

3) 使いたいSceneファイル内で呼び出す

```cpp:GameScene.h
#ifndef __GAMESCENE_SCENE_H__
#define __GAMESCENE_SCENE_H__

#include "cocos2d.h"
#include "extensions/cocos-ext.h"
#include "Box2D.h"
#include "GLES-Render.h"

#define PTM_RATIO 32.0 // 32px = 1m in Box2D

class GameScene : public cocos2d::Layer
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();
    virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) override;
    
    // implement the "static create()" method manually
    CREATE_FUNC(GameScene);
    
    
private:
    b2World* world;
    GLESDebugDraw* debugDraw;
    ~GameScene();
    
    void initPhysics();

    void update(float dt);
};

#endif // __GAME_SCENE_H__
```

```cpp:GameScene.cpp
#include "GameScene.h"
#include "Box2D/Box2D.h"
USING_NS_CC;

Scene* GameScene::createScene()
{
    auto scene = Scene::create();
    auto layer = GameScene::create();

    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool GameScene::init()
{
    //////////////////////////////
    // 1. super init first
    if (!Layer::init()){
        return false;
    }
    
    this->initPhysics();
    
   // Example box2d object to confirm debugDraw works
    b2BodyDef bodyDef;
    bodyDef.type = b2_dynamicBody;
    bodyDef.position.Set(200/PTM_RATIO, 300/PTM_RATIO);
    b2Body *body = world->CreateBody(&bodyDef);
    
    b2PolygonShape dynamicBox;
    dynamicBox.SetAsBox(50/PTM_RATIO, 50/PTM_RATIO);
    
    b2FixtureDef fixtureDef;
    fixtureDef.shape = &dynamicBox;
    fixtureDef.density = 1.0f;
    body->CreateFixture(&fixtureDef);

    this->scheduleUpdate();
    return true;
}

void GameScene::update(float dt) {
    int velocityIterations = 10;
    int positionIterations = 10;
    
    this->world->Step(dt, velocityIterations, positionIterations);
}

void GameScene::initPhysics()
{
    b2Vec2 gravity;
    gravity.Set(0.0f, 1.0f);
    this->world = new b2World(gravity);

    this->debugDraw = new GLESDebugDraw( PTM_RATIO );
    this->world->SetDebugDraw(debugDraw);
    
    uint32 flags = 0;
    flags += b2Draw::e_shapeBit;
    //        flags += b2Draw::e_jointBit;
    //        flags += b2Draw::e_aabbBit;
    //        flags += b2Draw::e_pairBit;
    //        flags += b2Draw::e_centerOfMassBit;
    this->debugDraw->SetFlags(flags);
//    this->addChild(B2DebugDrawLayer::create(this->world, PTM_RATIO), 9999);
    
}

void GameScene::draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t transformUpdated) {
    Layer::draw(renderer, transform, transformUpdated);
    Director* director = Director::getInstance();
        
    GL::enableVertexAttribs( cocos2d::GL::VERTEX_ATTRIB_FLAG_POSITION );
    director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
    world->DrawDebugData();
    director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}


GameScene::~GameScene() {
    delete this->debugDraw;
    this->debugDraw = NULL;
    
    delete this->world;
    this->world = NULL;
}
```

以上です。
DebugDrawを表示するためにSceneのdrawメソッドをオーバーライドしてやるところがミソです。
（オーバーライドすべきdrawメソッドの仕様が結構頻繁にわっていて、古い情報だとうまくいきません…）

![Screen Shot 2014-09-01 at 11.25.18 PM.png](https://qiita-image-store.s3.amazonaws.com/0/20470/aec3dead-e26b-6074-001b-1df840e5bf49.png)

うまくいくと、生成したBox2DのオブジェクトがDebugDrawで描画されているはずです。
\# 画像はiOSシミュレータ上のもの。

Box2D自体の使い方は変わってないはずなので, これでデバッグのやり方まで整えばあとは巷にある情報で勉強が進められるかな…。