【Cocos2d-x】Script Bindingの作り方（その1）

#はじめに
@tkyajiと申します。Advent Calendar初参加です。
Cocos2d-xに、Script bindingを追加する際の実装方法を書いていきたいと思います。
Cocos2d-xのバージョンは3.2を使っています。

なお、bindings-generatorを使用する際に必要となる準備は、昨年のAdvent Calendarで
giginetさんがとてもわかりやすい記事を書いてくれているので、
その辺りの説明は省きます（助かった）
[【cocos2d-x 3.0】binding-generatorでScript Bindingを自動化する方法](http://qiita.com/giginet/items/6dd0c2e178ec8ef44927)

以下の説明の中で、追加するスクリプト言語を"Lang"と記述しています。
例えばLuaなら"Lang"は"Lua"に読み替えてください。

#おおまなか手順
1. ScriptEngineProtocolを継承したLangEngineクラスを作る
1. C++の型とLang言語の型を変換するための関数を書く
1. bindings-generatorにターゲットとなる言語を追加
1. bindings-generatorで、グルーコード生成ロジックをひたすら書いていく
1. 一通りできたら、cocosコマンドに対応する

だいたいこんな感じです。bindings-generatorとcocosコマンドの実装はPythonで書きます。
cocosコマンドに対応するのはおそらく最後になるので、まずはLua Bindingのプロジェクトを作り、
その中で実装していくのが良いと思います。

#LangEngineクラスを作る
Luaの場合は`LuaEngine`、JSの場合は`ScriptingCore`にあたるEngineクラスを作成します。
`cocos2d::ScriptEngineProtocol`を継承し、以下のvirtual関数をオーバーライドします。

* getScriptType

>スクリプト言語ごとの定数を返却します。
`ScriptEngineProtocol`に`ccScriptType`というenumがあるので、ここに`kScriptTypeLang`定数を 追加し、
それを返却します。

* removeScriptObjectByObject

>Refクラスのデストラクタから呼ばれます。
autorelease等でC++上のインスタンスが死ぬ際に、Lang言語上のインスタンスも消す、
というような処理を実装します。
なお、Refクラスには`_ID`, `_scriptObject`というフィールドがあり、
ここにLang言語上のインスタンス情報を設定することで、C++のインスタンスとLang言語のインスタンスとを
紐づけることができます。

* executeString

>指定したLang言語の文字列を読み込んで実行します。

* executeScriptFile

>指定したLang言語のスクリプトファイルを読み込んで実行します。
基本的にはこのメソッドで*src*ディレクトリ配下のスクリプトファイルを実行していくので、
最初に実装が必要なメソッドになります。

####executeGlobalFunction
>指定した名前のLang言語上のグローバル関数を実行します。

####sendEvent
>各種イベント（メニュボタン押下、タッチ、スケジュール等々）発火時に呼び出されます。
引数の`ScriptEvent`でイベントの種類を判定します。
C++とは違う方法でイベント登録をしている場合は、このメソッドの中で登録した処理を呼び出します。
例えばLuaでは`registerScriptTapHandler`というメソッドでコールバックを登録していますので、
この場合、`sendEvent`の中で`registerScriptTapHandler`で登録したLuaの関数を実行します。
逆に言うと、C++と同様に`EventDispatcher`にイベント登録している場合は、
`sendEvent`では実装不要となります。

####handleAssert
>CCASERTから呼び出されます。ここではエラーログの出力と、Lang言語のエラー処理を実装します。
例えばLang言語上で例外を発生させる等です。

####parseConfig
>CocoStudioとの連携用？


#C++ ⇔ Lang言語間の型変換関数を実装
Luaの場合は`LuaBasicConversions`, JSの場合は`js_manual_conversions`にあたるソースです。
C++の型と、Lang言語の型を変換する関数を作っていきます。
例えばLuaの場合、Vec2を変換する関数を以下のように定義しています。

```cpp
// c++ -> lua
void vec2_to_luaval(lua_State* L,const cocos2d::Vec2& vec2)
{
    if (NULL  == L)
        return;
    lua_newtable(L);                                    /* L: table */
    lua_pushstring(L, "x");                             /* L: table key */
    lua_pushnumber(L, (lua_Number) vec2.x);               /* L: table key value*/
    lua_rawset(L, -3);                                  /* table[key] = value, L: table */
    lua_pushstring(L, "y");                             /* L: table key */
    lua_pushnumber(L, (lua_Number) vec2.y);               /* L: table key value*/
    lua_rawset(L, -3);
}

// lua -> c++
bool luaval_to_vec2(lua_State* L,int lo,cocos2d::Vec2* outValue)
{
    if (nullptr == L || nullptr == outValue)
        return false;
    
    bool ok = true;
    
    tolua_Error tolua_err;
    if (!tolua_istable(L, lo, 0, &tolua_err) )
    {
#if COCOS2D_DEBUG >=1
        luaval_to_native_err(L,"#ferror:",&tolua_err);
#endif
        ok = false;
    }
    
    if (ok)
    {
        lua_pushstring(L, "x");
        lua_gettable(L, lo);
        outValue->x = lua_isnil(L, -1) ? 0 : lua_tonumber(L, -1);
        lua_pop(L, 1);
        
        lua_pushstring(L, "y");
        lua_gettable(L, lo);
        outValue->y = lua_isnil(L, -1) ? 0 : lua_tonumber(L, -1);
        lua_pop(L, 1);
    }
    return ok;
}
```

これらの変換関数は、bindings-generatorの実装時に使います。
bindings-generatorを実装しながら、必要になった型を随時追加すれば良いので、最初に全て作る必要はありません。
また、上記のような変換関数は作らないで実装することも可能ですが、大抵の場合は作った方が楽だと思います。

とりあえず、本日はここまでです。
明日は*bindings-generator*の実装周りを説明したいと思います。

[【Cocos2d-x】Script Bindingの作り方（その2）](http://qiita.com/tkyaji/items/8b55c2db5315def03cd8)