Miyako + irb で遊ぶ
Ruby関西#18にてサイロス誠さんと「Miyako + irb」についてお話をしたことは昨日の日記に書いたとおりです。
ここでは、実際に遊んでみたときのノートを残しておきます。
Miyakoのインストール
MiyakoはRuby/SDLのインストールが必須になります。ボク自身は下記の順番にインストールしました。
- ActiveScriptRubyのインストール
- MyGame(Windows用のRuby/SDL同梱版)のインストール
- http://dgames.jp/ja/projects/mygame/
- 上記HPにあるように、zipを展開して、install_mygame.rb をダブルクリックするだけで、自動的にRuby/SDLがインストールされます。
- 複数のRubyがインストールされている場合には、それだけというわけにもいきませんが・・・
- Miyakoのインストール
- http://www.twin.ne.jp/~cyross/Miyako/
- Miyako_v1.0_with_rskit.zip をダウンロードしてみると、少し使い勝手が悪そうでしたので、Miyako_v1.0.zip を使いました。
- インストール方法は、Miyako_v1.0.zip を展開し、miyako.rb, miyako_ext.rb を RUBY_ROOT\lib\ruby\site_ruby\1.8 に配置するだけです。
- Miyakoのドキュメントは、個人が作成したとはにわかに信じられないほど充実していますので、他の展開済ファイルもディレクトリごとどこか判りやすい場所に置いておくと良さそうです。
- irb上で、下記コマンドを実行し、trueが返ってきたら、インストール成功です。
irb(main):001:0> require 'miyako'
Miyakoを試す
まず、irbを起動し、先ほどと同じように
require 'miyako'
を実行します。このとき、"SDL_app"というWindowが生成されるのが判ります。
次に、Miyakoのサンプルから sample\anime\anime_sample.rb をエディタで開きます。
内容を見てみると、Miyakoでは
- Miyakoモジュールのインスタンス変数を作成し、
- Miyako.update, Miyako::Input.update, Miyako::Screen.update を実行することで、
- 画面の描画内容を更新
しているようです。
とりあえず、Miyakoモジュール内で作業が閉じていることが判りましたので、
下記のステートメントを実行します。
irb(main):002:0> irb Miyako irb#1(Miyako):001:0>
慣れないと不思議かも知れませんが、これだけでMiyakoモジュール内に
入れたことになります。以下のような実験をしてみましょう。
irb#1(Miyako):002:0> instance_variables => [] irb#1(Miyako):003:0> @hoge = nil => nil irb#1(Miyako):004:0> instance_variables => ["@hoge"] irb#1(Miyako):005:0> exit => #<IRB::Irb: @signal_status=:IN_EVAL, @scanner=#<RubyLex:0x2930010>, @context=#<IRB::Context:0x290d30>> irb(main):003:0> Miyako.instance_variables => ["@hoge"] irb(main):004:0> irb Miyako irb#1(Miyako):001:0>
これは、「Miyakoモジュール内でインスタンス変数を@hogeを作成し、Miyakoモジュール外から
Miyakoモジュール内のインスタンス変数@hogeの存在を確認」しています。
モジュール内のインスタンス変数のため、Miyako内でクラスメソッドを用意すると、このhogeにアクセスできます。
irb#1(Miyako):001:0> def self.show_hoge irb#1(Miyako):002:1> @hoge irb#1(Miyako):003:1> end => nil irb#1(Miyako):004:0> exit => #<IRB::Irb: @signal_status=:IN_EVAL, @scanner=#<RubyLex:0x2930010>, @context=#<IRB::Context:0x2930d30>> irb(main):011:0> Miyako.show_hoge => nil irb(main):012:0>
次に、先ほどのanime_sample.rbを見ていきます。
8行目に"module Miyako" と書かれておりますので、9行目以降を
irbで実行すれば、期待として同じように動作するはずです。
1行ずつ貼り付けていくと、こんな感じで実行されていきます。
irb#1(Miyako):002:0> w = 0.1 => 0.1 irb#1(Miyako):003:0> irb#1(Miyako):004:0* @spr1 = Sprite.new("anim_sprite_01.png") => #<Miyako::Sprite:0x2e54064 ...> irb#1(Miyako):005:0> @spr1.oh = @spr1.ow => 32 irb#1(Miyako):006:0> irb#1(Miyako):007:0* @as1 = SpriteAnimation.new(@spr1, w) => #<Miyako::SpriteAnimation:0x2e4dc00 ...> irb#1(Miyako):008:0> @as1.show => true irb#1(Miyako):009:0> @as1.start => true irb#1(Miyako):010:0> irb#1(Miyako):011:0* @spr2 = Sprite.new("anim_sprite_02.png") => #<Miyako::Sprite:0x2e46810 ...> irb#1(Miyako):012:0> @spr2.ow = 32 => 32 irb#1(Miyako):013:0> @spr2.oh = 32 => 32 irb#1(Miyako):014:0> @spr2.y = 32 => 32 irb#1(Miyako):015:0> irb#1(Miyako):016:0* @as2 = SpriteAnimation.new(@spr2, w) => #<Miyako::SpriteAnimation:0x2e3c8b0 ...> irb#1(Miyako):017:0> @as2.show => true irb#1(Miyako):018:0> @as2.start => true irb#1(Miyako):019:0>
ここまでは画面に変化はありませんね。Miyako.updateを実行しなくては"SDL_app"の画面はいつまでも真っ黒なまま(むしろそれ以下)の状態です。
ここで、Miyako.updateを実行してみます。
irb#1(Miyako):020:0* update => nil
一瞬、更新されるようですが、その後すぐに反応しなくなってしまうようです。
次に、anime_sample.rb の27行目からのloop処理ですが、この処理を
クラスメソッドとして定義します。
irb#1(Miyako):028:0> def self.go_loop irb#1(Miyako):029:1> loop do irb#1(Miyako):030:2* Input.update irb#1(Miyako):031:2> irb#1(Miyako):032:2* if Input.quit? || Input.pushed_any?(:esc) irb#1(Miyako):033:3> break irb#1(Miyako):034:3> end irb#1(Miyako):035:2> if Input.pushed_any?(:btn1) irb#1(Miyako):036:3> @as1.toggle_visible irb#1(Miyako):037:3> @as2.toggle_visible irb#1(Miyako):038:3> end irb#1(Miyako):039:2> if Input.pushed_any?(:btn2) irb#1(Miyako):040:3> @as1.toggle_exec irb#1(Miyako):041:3> @as2.toggle_exec irb#1(Miyako):042:3> end irb#1(Miyako):043:2> @as2.move_character(Input.pushedAmount[0]) irb#1(Miyako):044:2> @as1.update irb#1(Miyako):045:2> @as2.update irb#1(Miyako):046:2> Screen.update irb#1(Miyako):047:2> end irb#1(Miyako):048:1> end => nil irb#1(Miyako):049:0> go_loop
無事にアプリケーションとして動作していることを確認できるでしょう。
:btn1, :btn2, :esc は、それぞれキーボード入力の z,x,ESC を意味しています。
Miyakoとirbをもっと近づける
さて、ここからが本題です。
irbから動作中のSDLアプリケーションをリアルタイムに動かして
みたいものです。スレッドを使って遊んでみましょう。
irb#1(Miyako):051:0> t = Thread.new{ go_loop } => #<Thread:0x2edad6c run> irb#1(Miyako):052:0> t.join IRB::Abort: abort then interrupt!! from C:/usr/ruby-1.8/lib/ruby/1.8/irb.rb:81 irb#1(Miyako):053:0> t.join 5 => nil irb#1(Miyako):054:0>
052行目では、スレッドをtに無理矢理変更し、それをCtrl+Cで強制停止させています。
ここで、SDLアプリケーション上で、ESCキーを入力してしまうと、go_loopの処理が
終了し、スレッドtが死んでしまいますので、注意が必要です。
053行目では、5秒間のlimitを与えて、スレッドをtに変更しています。
さて、この状態で、
irb#1(Miyako):056:0> sleep 5
としてやると、メインスレッドは5秒間休止状態となり、その間実行待ち状態だったスレッドtが実行されます。
5秒間が経過すると、SDLアプリケーション側は休止し、irb側がキー入力待ち(実行状態)になります。
ここで、irb上にてBackSpaceキーなどを連打すると、やっぱりSDLアプリケーションが動くことが観測されます。
要するに、キーアサインを受け付けたタイミングで、メインスレッドが実行状態から実行可能状態に遷移し、スレッドtが実行可能→実行→実行可能状態に遷移しているようです。
当たり前のこととはいえ、実物を目の当たりにすると感慨深いモノです。(^^;
最後に、下記のようなコードを書いて実験してみました。
# anime_sample_ext.rb module Miyako def self.u(&block) begin yield update end end def self.go_loop1 _go_loop 1 end def self.go_loop2 _go_loop 2 end def self._go_loop(n) loop do Input.update if Input.quit? || Input.pushed_any?(:esc) break end if Input.pushed_any?(:btn1) @as1.toggle_visible if n == 1 @as2.toggle_visible if n == 2 end if Input.pushed_any?(:btn2) @as1.toggle_exec if n == 1 @as2.toggle_exec if n == 2 end @as2.move_character(Input.pushedAmount[0]) if n == 2 @as1.update if n == 1 @as2.update if n == 2 Screen.update end end end # Miyako
Miyako.u はブロックの中身を全部評価した後で、Miyako.updateを実行することを保証するだけです。
この状態で、2つのスレッドを同時に扱ってみます。
irb#1(Miyako):059:0> Thread.kill(t) => #<Thread:0x2edad6c dead> irb#1(Miyako):060:0> Thread.list => [#<Thread:0x294c74c sleep>, #<Thread:0x2e5e690 run>] irb#1(Miyako):102:0* t1 = Thread.new{ go_loop1 } => #<Thread:0x2e7b268 run> irb#1(Miyako):103:0> t2 = Thread.new{ go_loop2 } => #<Thread:0x2e7609c run> irb#1(Miyako):104:0> Thread.list => [#<Thread:0x2e7609c run>, #<Thread:0x2e7b268 run>, #<Thread:0x294c74c sleep>, #<Thread:0x2e5e690 run>] irb#1(Miyako):105:0> sleep 5 => 5
sleepしているスレッドは、irb(main)のものです。現在はirb#1(Miyako)がrunの状態にあります。
スレッドt1,t2に分けて実行してみると、先ほどのスレッドtの時よりも高速にアニメーションしているような気がしますが・・・(^^;
この状態では、
t1.join
t2.join
[t1,t2].each{|tt| tt.join}
のいずれを実行しても、t1,t2が交互に実行されます。メインスレッドであるはずのirbには来ないのか?と考えてみると、Ctrl+cでInterruptできていることが、実行状態にある何よりの証明であることに気づく次第です。
とりあえず、これだけやればインパクトのあるプレゼンをirbだけで実現できるのかな?と思います。
それ以上のことをしたいのであれば、dRuby とかサーバーとかを立てて、外部からMiyako〜SDLに命令を投げることになるでしょう。