cocos2d-js(cocos2d-html5) でchipmunkを使う

Javascriptの物理シミュレーションライブラリでは chipmunkよりもbox2dが覇権っぽいけど、せっかく調べたりしてもったいないので書く。一応これ書いたら手元で作ってるゲームもbox2dのほうに移行しますね…。

感触としてはここのchipmunk testってとこで試せる。
http://www.cocos2d-x.org/html5-samples/samples/tests/index.html

今回は、github pagesにホストしてあるおもいっきりシンプルなアプリケーション http://iwag.github.io/simple-cocos2d-js/ を改造する。

-- 
その前にchipmunkについて。jsでのドキュメントはほぼない（[ある](http://www.cocos2d-x.org/docs/tutorial/parkour-game-with-javascript/chapter6/en)のはある）、cocos2d-xのが参考になるが  、ポート元のchipmunkのドキュメントが一番参考になった。
http://chipmunk-physics.net/release/ChipmunkLatest-Docs/ 

chipmunkに必要なのはspace, body, shapeで、ここに
http://www.cocos2d-x.org/wiki/Chipmunk?project_id=cocos2d-x#Bodies-and-Shapes に説明がある、訳。


--


自分のとこのディレクトリにchipmunk.jsをコピーする。
[simple-cocos2d-js]() はcocos2d-jsがminifyされて1ファイルになってる。


project.json のロードするモジュールにchipmunkを追加する。

```diff
-    "modules" : ["cocos2d"],
+    "modules" : ["cocos2d","chipmunk"],

    "jsList" : [
+       "chipmunk.js",
        "src/resource.js",
        "src/myApp.js"
    ]
```


Scene の初期化にでspace（シミュレーションする空間）を作成したり準備する。
updateでchipmunkの関数を呼ばないとシミュレーションされないので、scheduleUpdate()を呼ぶ。

```diff
var MyScene = cc.Scene.extend({
    onEnter:function () {
        this._super();

+        this.space = this.createPhysics_();

        this.layer = new MyLayer();
        this.layer.init(this.space);
        this.addChild(this.layer);

+        this.scheduleUpdate();
    },
```

createPyhiscs_ の実装はこんな感じ。
オブジェクトが飛んでいかないように壁を作るってのと重力を設定する。

```js
    createPhysics_: function () {
        var space = new cp.Space();

        var size = cc.director.getWinSize();
        // 壁を作る
        var walls = [
            new cp.SegmentShape(space.staticBody, cp.v(0, 0), cp.v(size.width, 0), 0),				// bottom
            new cp.SegmentShape(space.staticBody, cp.v(0, size.height), cp.v(size.width, size.height), 0),	// top
            new cp.SegmentShape(space.staticBody, cp.v(0, 0), cp.v(0, size.height), 0),				// left
            new cp.SegmentShape(space.staticBody, cp.v(size.width, 0), cp.v(size.width, size.height), 0)	// right
        ];
        for (var i = 0; i < walls.length; i++) {
            var shape = walls[i];
            shape.setElasticity(1.6);
            shape.setFriction(1.2);
            space.addStaticShape(shape);
        }

        space.gravity = cp.v(0, -100); // 下方向に重力を設定する
    },
```

ちなみに、setFrictionとかsetFlasticityの値を調整するとよく弾んだり弾まないようになったりする。

で、シミュレーションに重要なのはupdateで、space.stepで時間をちょっと進める。フレームの描画時に呼ばれるupdateで、stepを呼ぶ。

```js
    /** @override */
    update: function(dt) {
        this.space && this.space.step(dt);
    }
```

今回シミュレーションする対象はスプライトで、スプライトを作る箇所をこのように変える。
元はこういうの。

```js
    addSprite: function(position) {
        var sprite = cc.Sprite.create(s_Octcat);

        sprite.setScale(0.5);
        sprite.setPosition(position);
        this.addChild(sprite);
    }
```

まず、Spriteではなく、PhysicalSpriteに変更する。

        var sprite = cc.PhysicalSprite.create(s_Octcat);


このスプライトにBodyを持たせる。これが重さをもっている。作成したものは、spaceに追加してあげる。

```js
        var body = new cp.Body(100, cp.momentForBox(1, 64, 64));
        body.setPos(position);
        this.space_.addBody(body);
        sprite.setBody(body);
```

またPhysicalSpriteでは、setPositionできなくなって、boxでsetPosしてやる必要がある（場所とかの計算がchipmunkに委譲されるから？）。
次にshapeを作る必要がある。こっちも最後にspaceに追加する。

```js
        var shape = new cp.BoxShape(body, 64, 64);
        shape.setElasticity(0.5);
        shape.setFriction(0.5);
        this.space_.addShape(shape);
```

で、まとめるとこうなる。


```js
    addSprite: function(position) {
        var body = new cp.Body(100, cp.momentForBox(1, 64, 64));
        body.setPos(position);
        this.space_.addBody(body);
        var shape = new cp.BoxShape(body, 64, 64);
        shape.setElasticity(0.5);
        shape.setFriction(0.5);
        this.space_.addShape(shape);

        var sprite = cc.PhysicsSprite.create(s_Octcat);
        sprite.setBody(body);

        sprite.setScale(0.5);
        this.addChild(sprite);
    }
```

デバッグするときはこれをsetVisibule(true)するとシミュレーションに使用されてる箱が表示されるように成る。

```js
        this._debugNode = cc.PhysicsDebugNode.create(this.space_);
        this._debugNode.setVisible(false);
        this.addChild(this._debugNode);
```

一応当たり判定も。

shapeに当たり判定用の値をセットしてあげる。

```diff
    addSprite: function(position) {
        ...
        var shape = new cp.BoxShape(body, 64, 64);
+		this.shape_.setCollisionType(1);
```

これをMyScene#onEnterに書く。1と2の当たり判定用の関数をハンドラに追加する。


```js
        this.space.addCollisionHandler( 1, 2,
            this.collisionBegin.bind(this)
         );
	...
   }

    collisionBegin : function ( arbiter, space ) {
        var shapes = arbiter.getShapes();
        var collTypeA = shapes[0].collision_type;
        var collTypeB = shapes[1].collision_type;
		// なにかする
        return true;
    }

```

最後の方手抜きになってすいません…。