LoginSignup
11
11

More than 5 years have passed since last update.

Cocos2d-x mruby binding

Last updated at Posted at 2015-02-26

Cocos2d-xのmruby bindngを開発しています。
iOS、Androidである程度動くようになったので公開しました。
https://github.com/tkyaji/cocos2d-ruby

使い方

上記のgithubリポジトリからcloneしてください。

$ git clone https://github.com/tkyaji/cocos2d-ruby

あとは通常のCocos2d-xの使い方と同じです。
setup.pyスクリプトを実行し、環境変数をロードします。

$ cd cocos2d-ruby
$ python setup.py
$ source ~/.bash_profile

cocos newコマンドでプロジェクトを作ります。
-lオプションでrubyを指定すると、mruby bindingのプロジェクトができます。

$ cocos new MyGame -l ruby

なお、Cocos2d-x本家のリポジトリからforkしているので、C++やLuaも使用できます。

HelloWorld

生成したプロジェクトで、そのままcocos runを実行すると、HelloWorldのサンプルが起動します。

$ cocos run -p ios
$ cocos run -p android

中身はC++版のHelloWorldを、rubyで書き換えたものです。

main.rb
require "HelloWorld"

def main
  # size define
  resources = {
    small: {
      size: {w: 480, h: 320},
      directory: "res/iphone"
    },
    medium: {
      size: {w: 1024, h: 768},
      directory: "res/ipad"
    },
    large: {
      size: {w: 2048, h: 1536},
      directory: "res/ipadhd"
    }
  }
  design_resolution_size = resources[:small][:size]

  director = CC::Director.get_instance
  glview = director.get_open_glview
  if ! glview
    glview = CC::GLViewImpl.create("Ruby Empty Test")
    director.set_open_gl_view(glview)
  end

  glview.set_design_resolution_size(design_resolution_size[:w], design_resolution_size[:h], ResolutionPolicy.NO_BORDER)

  frame_size = glview.get_frame_size
  ssize = resources[:small][:size]
  msize = resources[:medium][:size]

  target_res = resources[:small]
  if frame_size.height > msize[:h]
    target_res = resources[:large]
  elsif frame_size.height > ssize[:h]
    target_res = resources[:medium]
  end
  target_size = target_res[:size]
  director.set_content_scale_factor([target_size[:h] / design_resolution_size[:h], target_size[:w] / design_resolution_size[:w]].min)

  futils = CC::FileUtils.get_instance
  futils.add_search_path(target_res[:directory])

  director.set_display_stats(true)
  director.set_animation_interval(1.0 / 60)

  scene = HelloWorld.scene
  director.run_with_scene(scene)
end

main
HelloWorld.rb
class HelloWorld < CC::Layer
  include CC

  TITLE_FONT_SIZE = 20;

  def self.scene
    # 'scene' is an autorelease object
    scene = Scene.create

    # 'layer' is an autorelease object
    layer = HelloWorld.create

    #add layer as a child to scene
    scene.add_child(layer)

    # return the scene
    return scene
  end

  def initialize
    init
  end

  def init

    director = Director.get_instance
    visible_size = director.get_visible_size
    visible_vec2 = Vec2.new(visible_size.width, visible_size.height)
    origin = director.get_visible_origin

    # 1. 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
    close_item = MenuItemImage.create("CloseNormal.png", "CloseSelected.png", Proc.new {|sender|
      p "Exit button clicked"
    })
    content_size = close_item.get_content_size
    content_vec2 = Vec2.new(content_size.width, content_size.height)
    close_item.set_position(origin + visible_vec2 - content_vec2 / 2)

    # create menu, it's an autorelease object
    menu = Menu.create(close_item)
    menu.set_position(Vec2.ZERO)
    self.add_child(menu, 1)

    # 2. add your codes below...

    # add a label shows "Hello World"
    # create and initialize a label

    label = Label::create_with_system_font("Hello World", "Arial", TITLE_FONT_SIZE)
    # position the label on the center of the screen
    label.set_position(origin.x + visible_size.width / 2, origin.y + visible_size.height - label.get_content_size.height)

    # add the label as a child to this layer
    self.add_child(label, 1)

    # add "HelloWorld" splash screen"
    sprite = Sprite.create("HelloWorld.png")

    # position the sprite on the center of the screen

    tmp_size = visible_size / 2
    sprite.set_position(Vec2.new(tmp_size.width, tmp_size.height) + origin)

    # add the sprite as a child to this layer
    self.add_child(sprite)
  end
end

main.rbに書いてある処理は、実際はAppDelegate.cppに書いた方が良いと思いますが、
サンプルなのであえてRuby側に書いています。

メソッド名はスネークケースに、isXXXXXX?に変換しています。
(binding-generator実行時の設定で、この変換は無効化して生成できます)

コールバックはProcで実装します。

メモリ管理

Lua Bindingでは、C++と同様、状況によってはretain、releaseでリファレンスカウンタを操作する必要があります。
mruby Bindingでは、retain、releaseは不要で、Ruby側で全て管理してくれるように実装しています。

具体的には、retain、releaseが呼び出された際に、mrubyでラップしたインスタンスだった場合、
C++のリファレンスカウンタ(RC)は増やさずに、代わりにRuby側に作ったRCをインクリメントしているイメージです。
Ruby側のRCが0になったタイミングで、C++側のRCをデクリメント(つまりC++の元のrelease処理)を実行し、
インスタンスがdeleteされるようにしています。
この辺の処理は、Ref.cppRubyEngine.cpp で実装しています。

正直、今の実装で問題ないかまだ少し心配なので、この場合にダメそうとか、ツッコミをもらえると嬉しいです

エディタ

LuaやJSの場合、Cocos Code IDEを使った効率の良い開発を行えますが、mruby bindingは当然対応していません。
しかしそれだと不便なので、mruby binding用のSublime Textプラグインを作りました。
CocosRubyEditor

これを使うと以下のように自動補完が行えます。

CocosRubyEditor.gif

Package Controlに登録したため、"CocosRubyEditor"で検索すると出て来るはずです。
定数等、現状では補完されないものがありますが、そのうち対応する予定です。

TODO

  • 未対応のクラスに対応する

  • cocos newでプロジェクトを作る時に、mrubyのgemを追加できるようにする

  • rubycompileコマンドの実装(バイトコード化と暗号化)

  • ruby-testプロジェクトの実装

  • ドキュメント生成する

  • ios / Android / Mac 以外のプラットフォームに対応


使ってみてもらえると嬉しいです。

以上。

11
11
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11