【Cocos2d-x】Squirrel Binding

#はじめに
@tkyajiと申します。12/23のCocos2d-x AdventCalendarを担当します。
（12/23中に投稿できなかったことをお詫びします）

現在Cocos2d-xのSquirrel Bindingを細々と開発中のため、本日はそれを紹介させていただこうと思います。
なお、Cocos2d-xのバージョンは3.3をベースにしています。

#Cocos2d-Squirrel
Cocos2d-xのSquirrel BindingをGithubで公開しています。
[cocos2d-squirrel](https://github.com/tkyaji/cocos2d-squirrel)

まだ HelloWorld が動く程度で、対応できていないところが多々あります。

色々書いていく前に、まずは Hello World のコードを見ていただきたいと思います。


```php:main.nut
// initialize director
local director = cc.Director.getInstance()

// turn on display FPS
director.setDisplayStats(true)
// set FPS. the default value is 1.0/60 if you don't call this
director.setAnimationInterval(1.0 / 60)
// create a scene. it's an autorelease object
dofile("src/HelloWorld.nut")
local scene = HelloWorld.createScene()
// run
director.runWithScene(scene)
```


```php:HelloWorld.nut
class HelloWorld extends cc.Layer {

    static function createScene() {
        // 'scene' is an autorelease object
        local scene = cc.Scene.create()
        // 'layer' is an autorelease object
        local layer = HelloWorld.create()
        // add layer as a child to scene
        scene.addChild(layer)
        // return the scene
        return scene
    }

    constructor() {
        this.init()
    }

    function init() {

        local visibleSize = cc.Director.getInstance().getVisibleSize()
        local origin = cc.Director.getInstance().getVisibleOrigin()

        /////////////////////////////
        // 2. add a menu item with "X" image, which is clicked to quit the program
        //    you may modify it.

        // add a "close" icon to exit the progress. it's an autorelease object
        local closeItem = cc.MenuItemImage.create("res/CloseNormal.png",
                                                  "res/CloseSelected.png",
                                                  HelloWorld.menuCloseCallback)
        closeItem.setPosition(cc.Vec2(origin.x + visibleSize.width - closeItem.getContentSize().width / 2,
                              origin.y + closeItem.getContentSize().height / 2))

        // create menu, it's an autorelease object
        local menu = cc.Menu.create(closeItem)
        menu.setPosition(cc.Vec2.ZERO)
        this.addChild(menu, 1)

        /////////////////////////////
        // 3. add your codes below...

        // add a label shows "Hello World"
        // create and initialize a label
        local label = cc.Label.createWithTTF("Hello World", "res/fonts/Marker Felt.ttf", 24)
        // position the label on the center of the screen
        label.setPosition(cc.Vec2(origin.x + visibleSize.width / 2,
                               origin.y + visibleSize.height - label.getContentSize().height))
        // add the label as a child to this layer
        this.addChild(label, 1)
        // add "HelloWorld" splash screen"
        local sprite = cc.Sprite.create("res/HelloWorld.png")
        // position the sprite on the center of the screen
        sprite.setPosition(cc.Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y))
        // add the sprite as a child to this layer
        this.addChild(sprite, 0)
    }

    function menuCloseCallback(pSender) {
        cc.Director.getInstance().end()
        exit(0)
    }
}
```

見ての通り、Squirrel Binding では、できるだけC++と同じ感じで書けるようにしています。


#そもそもSquirrelって何？
[Squirrel](http://www.squirrel-lang.org/)はドマイナーなスクリプト言語です。[TIOBEのプログラミング言語ランキング](http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html)では、常に100位圏外です。
しかし、非常に使いやすく、覚えやすい言語です。
Luaより遅く、サイズも多少大きくなりますが、その分機能が多く使い易いという感じです。
Luaと比べると、

* C/C++やJavaに近い構文
* データ型として配列がある（もちろんインデックスは0から）
* クラスがあって継承もできる
* テーブルがJSON風に書ける（キーは文字列にする必要がある）

といった特徴があり、Luaの使いづらいところを解消してくれます。
※元々SquirrelはLuaの不満を元に作られた言語と言われているようです。


#Squirrel Binding の特徴
Squirrel Bindingは、Lua Bindingと比較して、以下の特徴があります。

* クラス名や関数名は短縮せずにそのまま
* Vec2やSizeのようなデータクラスや構造体もClassとしてBinding
* std::functionはSquirrelの関数、std::vectorは配列、std::mapはテーブルにBinding
* retain / release を書かなくても良いように、メモリ管理を自動化（未対応）


#使い方
まずはgit cloneして、ファイルをまるごと持ってきます。
externalディレクトリも含めて全部入った状態になっているので、
あとは通常通りcocosコマンドを実行していくだけです。
**-l** オプションで **sq** を指定すると、Squirrelのプロジェクトになります。

```bash
$ git clone https://github.com/tkyaji/cocos2d-squirrel.git
$ cd cocos2d-squirrel
$ ./setup.py
$ source FILE_TO_SAVE_SYSTEM_VARIABLE
$ cocos new MyGame -p com.your_company.mygame -l sq -d NEW_PROJECTS_DIR
$ cd NEW_PROJECTS_DIR/MyGame
```

実行する際も、通常通り`cocos run`で実行可能です

```bash
cocos run -p ios
cocos run -p android
```

なお、ios / android 以外は未対応です。


#タッチイベント
タッチイベントは以下のように書きます。

```js:TouchEvent
local listener = cc.EventListenerTouchOneByOne.create()
listener.onTouchBegan = function(touch, event) {
    print("onTouchBegan")
    return true
}
listener.onTouchMoved = function(touch, event) {
    print("onTouchMoved")
}
listener.onTouchEnded = function(touch, event) {
    print("onTouchEnded")
}
listener.onTouchCancelled = function(touch, event) {
    print("onTouchCancelled")
}
local dispatcher = cc.Director.getInstance().getEventDispatcher()
dispatcher.addEventListenerWithSceneGraphPriority(listener, sprite)
```


#スケジュール（タイマー）
スケジュール（タイマー）処理は、以下のように書きます。

```js:Schedule
this.schedule(function(time) {
    print(time)
}, "update_key1")
```


#オブジェクト指向
Squirrelにはクラスがあります。継承もできます。（多重継承なし、抽象クラスなし）
これにより、上で書いた HelloWorld のサンプルのように、C++とほぼ同じように書くことができます。

```php
class HelloWorld extends cc.Layer
```
このように、Cocos2d-xの各クラスを継承することも可能です。
ただし、ここで１つ注意が必要です。継承しているのはC++のクラスではなく、
Bindingして生成したSquirrelのクラスだということです。
サンプルのHelloWorldクラスでは、initメソッドを定義していますが、
このメソッドはcreateメソッドの中からは呼ばれません。
initメソッドが呼び出されるまでの流れを書くとこんな感じです。

|No|HelloWorld<br>(squirrel)|Layer<br>(squirrel)|Layer<br>(C++)|comment|
|:---:|:---:|:---:|:---:|---|
|1|HelloWorld.create| | |HelloWorldクラスのcreateメソッドを呼ぶ|
|2| |Layer::create| |HelloWorldクラスにcreateメソッドは存在しないので、<br>親のLayerクラスのcreateを呼ぶ|
|3| | |Layer::create|LayerクラスはC++のLayerクラスをBindingしたものなので、<br>C++のLayerクラスを実行|
|4| | |Layer::init|C++のLayer::createメソッドの中で、this->initメソッドを実行|

Layer::initはC++の中で実行される処理のため、Squirrelの処理には関与しないのです。
そのため Squirrel Binding で create の初期化処理を書く場合は、constructorから呼び出されるようにしてください。
グルーコードの中で、create実行後にそのクラスのconstructorを実行するようにしています。


#メモリ管理について
ご存知の通り、Cocos2d-xでは、retain / releaseメソッドでリファレンスカウンタを増減し、
メモリ管理を行う必要があります。
そしてこれは Lua Binding でも必要な訳ですが、Luaでメモリ管理が必要というのはかなり残念です。

Squirrel Bindingでも、現在は retain / release が必要な状態となっていますが、
これは不要になるように対応する予定です。

余談ですが、Squirrelのインスタンスが破棄される際に、
それに紐づくC++のインスタンスを破棄する、ということは、簡単に実現可能です。
（Luaでも簡単にできる）
面倒なのはCocos2d-xのリファレンスカウンタで、うまく対応しないと、
SquirrelとC++で循環参照のような状態になります。


#おわりに
実用的なレベルになるのはまだ先になると思いますが、こんな感じで開発を進めています。
Squirrelって結構いいな、と思ってもらえると嬉しいです。

明日は@hachupさんです。
Cocos2d-xの海外情報ということで、なかなか見ることの無い貴重な情報が得られそうですね。

以上です。