cocos2d-x を mruby で使うフレームワーク cor-engine の紹介


cocos2d-x と mruby とその他私が使う上で便利なものをまとめたフレームワークをこちらに公開しています。

https://github.com/rmake/cor-engine

たとえば、スプライトを出すコードを以下のように書けます。

```
# visible_sizeは画面の大きさ
director = Director.get_instance
visible_size = director.get_visible_size

# nd3.png画像からspriteを作成する
sprite = Sprite.create "nd3.png"

# 画面の中心に配置
sprite.set_position visible_size.width / 2, visible_size.height / 2

# Layerのノードに追加することで表示される
Project.get_current_layer.add_child sprite

```

こちらのリリースからWindows用バイナリが入っているcor_engine_release_win.zipをダウンロードして

[https://github.com/rmake/cor-engine/releases/tag/release20141206](https://github.com/rmake/cor-engine/releases/tag/release20141206)

以下のファイルを書き換えるのがお試しとしては簡単かと思います。

cocos2dx_simples/sprite_one/start.rb

変更したら

cocos2dx_simples/sprite_one/cor_lib_test_main.exe

を実行してください。

## mruby binding を作るときにやっていること

clangを使って以下の様な感じのコマンドを実行すると

```
clang++ -Xclang -ast-dump -fsyntax-only -std=c++11 -pg -Wall ****.cpp
```

c++の構文木の情報を取得できるので、
それを以下のrubyスクリプトで

https://github.com/rmake/cor-engine/blob/master/libraries/scripts/mruby_interface_gen.rb

bindingのためのc++コードを作ります。

## 現状でのbindingの生成ルール

cocos2d::Sprite などcocos2d-xのクラスはそのままmruby側でも同じ名前で使えます。  
ただ、mruby binding generatorが完全でないこともあって、全てではありません。  
mruby側では現状 cocos2d:: という感じのネームスペースは省略できます。  

メンバ変数およびメンバ関数は、 camel case (createWithTexture みたいな感じ) から  
snake case (create_with_texture みたいな感じ)に名前が変更されます。  
クラスは使用できてもメンバの中には使用できないものもあります。

cocos2d-x の API の機能については C++ のものを参照してください。  
http://www.cocos2d-x.org/reference/native-cpp/V3.2/index.html  
だいたいは同じ感じで使えるかと思います。

現状、定数やenumなど変換できていないものなどもあります。  
それらを使う場合は直接値を指定してください。  
そのほか、デフォルト引数など幾つか未対応のものがります。

## binding 生成ルールを幾つか紹介

### ruby の block と C++ の std::function の対応

行ったり来たりしながらアニメーションを切り替える例

```
# 移動とアニメーションの切り替え
move_sequence = [
  CallFunc.create {
    if animation_action && animation_action.valid?
      sprite.stop_action animation_action
    end
    animation_action = sprite.run_action(Animate.create(left_animation))
  },
  MoveBy.create(2, Vec2.create(100, 0)),
  CallFunc.create {
    if animation_action && animation_action.valid?
      sprite.stop_action animation_action
    end
    animation_action = sprite.run_action(Animate.create(right_animation))
  },
  MoveBy.create(2, Vec2.create(-100, 0)),
]

# 移動の開始、ずっと続ける
move_action = sprite.run_action RepeatForever.create(Sequence.create(move_sequence))

```

cocos2d-xのCallFuncの定義を引用しておきます。

```
class CC_DLL CallFunc : public ActionInstant //<NSCopying>
{
public:
    //...
    static CallFunc * create(const std::function<void()>& func);


```

関数の最後にstd::functionを引数とする関数がある場合はそのままblockで渡せるように。
それ以外の場合はProc.newで書きます。

```
listener = EventListenerTouchOneByOne::create

listener.on_touch_began = Proc.new do |t, e|
  # do some thing
end
```

### std::shared_ptr, cocos2d::Refなどの扱い

mrubyからの参照は基本的に弱参照で扱いますので、

- cocos2d::Ref のpointer ならば Project.get_current_layer などに add_child
- std::shared_ptr で管理するものは、 Project::get_table_sp に set

などのことをおこなわなければ、しばらくすると(1 frame 後とかに)開放されてしまいます。

これは mruby と C++ で gabage collection の扱いが違うため、
こんなかんじになっています。


## デバッグ用の出力

mrubyコード中で
```
Logger.debug "Hello, World."
```
などとするとログに"Hello, World."と出力されます。  
現在ログは標準出力に出ていますので、  
以下の内容のコマンドを打つか、  
```
cor_lib_test_main.exe > out.log
```
上の内容をテキストファイルに書いて、debug.batというなまえにして  
エクスプローラー上でダブルクリックするれば、  
out.log というファイルに"Hello, World."と出力されます。  
debug.bat はバッチファイルですので、拡張子が bat でさえあれば自由な名前で構いません。  

いろいろなrubyのコードを書いてみて実験してみるのもいいかもしれません。

```
a = 1 + 1
Logger.debug "ans = #{a}"
```

```
a = 1 + 1
Logger.debug a.to_s
```

```
a = [1, 2, 3]
Logger.debug a
```

という具合です。

色々整理が必要な感じですが良かったら試してみてください。