FiberはRubyのコルーチン的な機能
どのスクリプトを組み込むか決める時に個人的に気にしているのがコルーチンを使えるかどうかだったりする。ライブラリのサイズや実行速度ももちろん重要ではあるけれども、せっかくなら楽に記述できるにこしたことはない。
Rubyでのコルーチンに相当するものがFiberという機能。mrubyでも使えるようなので少し試してみる。
やりたい事は以下の3つ
1.単純な中断、復帰
毎フレーム順番に処理を実行していく関数をC言語で書こうとするとswitch-caseだらけになってしまう。これをもっと簡単に直線的に書きたい。
処理1 yield // 中断、次のフレームで呼ばれたら続きから 処理2 yield // 中断、次のフレームで呼ばれたら続きから
2.関数呼び出しした先でも中断、復帰
ゲームを作っていて何かを待つ処理というのはかなり多い。アニメーションの終了待ち、会話の終了待ち、サウンドの再生待ち、エフェクトの再生待ち、画面の暗転待ち、単純に間を開けるために10フレーム待ち、などなど例を上げればたくさん出てくる。
これらは至る所に出てくる処理なので毎度毎度同じことは書きたくない。理想は待ち用の関数内で待ち処理が終わるまで帰ってきて欲しくない。
アニメーション再生関数() アニメーション終了待ち関数() // 再生終了までこの中でyieldし続けて欲しい // アニメーション再生終了したら次はここから 会話開始関数() 会話終了待ち関数() // 会話が終わるまでこの中でyieldし続けて欲しい // 会話が終了したら次はここから
3.C言語側からでも制御
mruby側からだけしか処理の中断、復帰できないのならあまり意味がないのでもちろんC言語側からでも制御できるようにしたい。
mruby-fiber指定してmrubyをビルド
mrubyでは拡張機能(mrbgems)はライブラリのコンパイル時に指定して一つのバイナリにする仕組みをとっている。これはファイル読み込みが無い環境へも対応するためらしい。
Fiberを使うためのmruby-fiberもこのmrbgemsに用意されているのでbuild_confib.rbのクロスコンパイラ設定にmruby-fiberを指定する。
conf.gem :core => "mruby-fiber"
iOS用の全体としてはこんな感じ。
MRuby::CrossBuild.new('ios') do |conf| toolchain :clang SDK_PATH = "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.1.sdk" ARCH = "-arch arm64 -arch armv7 -arch armv7s" conf.cc.flags << "#{ARCH}" conf.cc.include_paths << "#{SDK_PATH}/usr/include" conf.linker.flags << "#{ARCH}" conf.linker.library_paths << "#{SDK_PATH}/usr/lib" conf.bins = [] conf.gem :core => "mruby-print" conf.gem :core => "mruby-math" conf.gem :core => "mruby-enum-ext" conf.gem :core => "mruby-fiber" end
出来上がったlibmruby.aをリンクして準備完了。
実行テスト
通常のRubyと同じ様にFiber.newにブロックを渡してFiber.yieldで中断、Fiber.resumeで処理を復帰できる。違う部分はビルド時に組み込まれているのでFiberをrequireする必要がないことくらい。
# test.rb # 指定フレーム数だけ待機する関数 def WaitFunc(time) time.times{|x| puts "wait... " + x.to_s Fiber.yield } end # ファイバー作成 fiber = Fiber.new do # yieldで処理を中断できる puts "update 1" Fiber.yield puts "update 2" Fiber.yield # 5フレーム待機する WaitFunc 5 puts "update end" end puts "--- Fiber Test Begein ---" # ファイバーが生きている間更新する frame = 0 while fiber.alive? do puts ">frame" + frame.to_s fiber.resume frame += 1 end puts "--- Fiber Test End ---"
このRubyのコードをiOS上で実行してみる。
#include <mruby.h> #include <mruby/compile.h> // mrubyのテスト static void mruby_test() { NSString* res = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"rb"]; const char* rubyCode = [[NSString stringWithContentsOfFile:res encoding:NSUTF8StringEncoding error:nil] UTF8String]; mrb_state* mrb = mrb_open(); mrb_load_string(mrb, rubyCode); mrb_close(mrb); }
実行結果はこんな感じ。ちゃんと処理が中断、復帰できている。
--- Fiber Test Begein --- >frame0 update 1 >frame1 update 2 >frame2 wait... 0 >frame3 wait... 1 >frame4 wait... 2 >frame5 wait... 3 >frame6 wait... 4 >frame7 update end --- Fiber Test End ---
復帰処理をC言語側から行う。
上のテストではスクリプト側で復帰処理を行っていたが、C言語側から制御したい場合もあるので今度はそのテストを。
まず、直にファイバーをC言語側から呼び出しても期待した通りの結果にはならなかった。
とりあえずファイバーを関数でラップして使用することにしてみた。
mruby側にファイバーを作成する関数と復帰させる関数を用意する。
# test.rb # ファイバー作成 def create_func() puts "create_func()" return Fiber.new {do_something} end # ファイバーをリジューム def do_func(fiber) puts "do_func()" fiber.resume if fiber.alive? return fiber.alive? end # 指定フレーム数だけ待機する関数 def WaitFunc(time) time.times{|x| puts "wait... " + x.to_s Fiber.yield } end # 適当な処理 def do_something() # yieldで処理を中断できる puts "update 1" Fiber.yield puts "update 2" Fiber.yield # 5フレーム待機する WaitFunc 5 puts "update end" end
C言語側からmrubyの処理を制御する。
#include <mruby.h> #include <mruby/compile.h> // mrubyのテスト static void mruby_test() { NSString* res = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"rb"]; const char* rubyCode = [[NSString stringWithContentsOfFile:res encoding:NSUTF8StringEncoding error:nil] UTF8String]; mrb_state* mrb = mrb_open(); mrb_load_string(mrb, rubyCode); mrb_sym create_func = mrb_intern_cstr(mrb, "create_func"); mrb_sym do_func = mrb_intern_cstr(mrb, "do_func"); mrb_value top_self = mrb_top_self(mrb); mrb_value fiber = mrb_funcall_argv(mrb, top_self, create_func, 0, NULL); while (true) { mrb_value alive = mrb_funcall_argv(mrb, top_self, do_func, 1, &fiber); if (!mrb_test(alive)) { break; } } mrb_close(mrb); }
実行結果は期待通り。
create_func() do_func() update 1 do_func() update 2 do_func() wait... 0 do_func() wait... 1 do_func() wait... 2 do_func() wait... 3 do_func() wait... 4 do_func() update end
まつもとゆきひろ直伝 組込Ruby「mruby」のすべて 総集編
- 作者: まつもとゆきひろ
- 出版社/メーカー: 日経BP社
- 発売日: 2014/01/31
- メディア: Kindle版
- この商品を含むブログを見る
- 作者: 高橋征義,後藤裕蔵,まつもとゆきひろ
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2010/03/31
- メディア: 単行本
- 購入: 15人 クリック: 394回
- この商品を含むブログ (81件) を見る