RGSS2を知る(2)
前回のんはあまりにしょんぼりだったので、もうひとつ行ってみよう。
RGSS2にはViewportクラスというものがある。
描画範囲を指定するためのクラスだが、Spriteを作るときに指定しないと全画面が描画範囲になる。
全画面範囲のデフォルトViewportとかあるのだろうか。
s = Sprite.new
p s.viewport
結果はnil。
viewportを指定しないSpriteは、どのViewportにも属さない、ということだ。
では次にSpriteオブジェクトを束縛するのは誰か。
newしただけで画面に描画されるSpriteは、initialize(もしくはallocate)の中で自分自身の参照をどこかに保存しないといけない。
Graphics.updateがSpriteオブジェクトを取得するために絶対必要な処理だ。
ただ、RGSS2はCなりC++なりで書かれた拡張ライブラリである。
RubyのSpriteオブジェクトを束縛しているのか、それともSpriteの内部情報へのポインタを持っているのか。
それによって、このようなコードの動作結果が変わる。
bitmap = Bitmap.new(100,100) bitmap.fill_rect(bitmap.rect, Color.new(255,255,255,255)) loop do Input.update break if Input.trigger?(Input::C) end a = Array.new(10000) do s = Sprite.new s.bitmap = bitmap s end loop do Input.update Graphics.update if Input.trigger?(Input::C) a=nil GC.start end end
ボタンを押すと大量のSpriteオブジェクトを生成して描画、もう一度押すとすべての参照を切ってGCする。
これを実行すると、Game.exeのメモリ使用量ははじめ16Mほどだったのが、ボタンを押すと31Mほどに膨れ上がる。
このとき、画面の左上には白い四角が描画される。
そして、もう一度押すと、時間をかけてメモリが減り始める。GCがSpriteオブジェクトを回収しているのだ。
回収が終わっても白い四角は残っていた。これについては後で。
ここから想像できる内部実装は、Sprite.newでSpriteオブジェクトを生成するが、RubyのSpriteオブジェクトを保持せず、内部構造体へのポインタをmallocしたメモリにでも格納している、ということだ。
だからRubyコードからの参照がなくなるとGCに回収される。
おそらくrelease関数で内部保持のポインタを削除するようになっているのだろう。
このような実装はDXRubyでもやっていて、Window.draw時にmallocしたメモリに描画情報を保存している。こっちは描画情報はメソッドで指定するからメモリにコピーしているが、RGSS2の場合はオブジェクトの構造体のポインタを保存して、Grahics.update時にリアルタイムにデータを参照・取得しているはずだ。
bitmap = Bitmap.new(100,100) bitmap.fill_rect(bitmap.rect, Color.new(255,255,255,255)) Sprite.new.bitmap = bitmap loop do Input.update Graphics.update if Input.trigger?(Input::C) GC.start end end
こちらはnewしっぱのSprite。
ボタンを押すと消えるかと思ったら、消えずに画面に残ったままだった。
さっきのでもそうだったが、これが消えないのはおそらく、描画するものがひとつも無いときに、DirectX9Deviceのbigen・endを呼び出さずにpresentしているのだろう。
これをすると、プライマリサーフェイスの内容を変更せずに画面を更新できる。DXRubyでもやっているが、RGSS2でやる理由は無い気もする。
ちなみに
bitmap = Bitmap.new(100,100) bitmap.fill_rect(bitmap.rect, Color.new(255,255,255,255)) Sprite.new.bitmap = bitmap a = Sprite.new a.bitmap = bitmap a.x = 200 loop do Input.update Graphics.update if Input.trigger?(Input::C) GC.start end end
このようにするときちんと左側の四角は消える。
なので、SpriteオブジェクトはRubyの変数なり配列なりできちんと束縛しておかないと、とりあえず描画されているように見えてもGCが動いたとき(つまり突然)に消えてしまうので注意が必要だ。