サンプルシューティングでやったことを少し

記録として、サンプルシューティングで修正したところとその意味を書いておく。


基本的な仕組みとして、弾とか敵とかはそれぞれのモジュールが管理する。
モジュールのインスタンス変数としてオブジェクト配列や当たり判定配列があって、モジュールのメソッド呼び出しで配列を返すように作ってある。
モジュールのメソッドには、全オブジェクトの移動や描画、フラグが立っているオブジェクトの削除といった機能がある。
削除っつーのは、移動→判定→削除→描画という流れで作ってあるからで、移動と判定をdelete_ifのループにすれば必要なくなるのだが、DXRubyExtensionを使っているのでそういうわけにもいかない。
CollisionBoxなどを継承して敵オブジェクトを作ればDXRubyExtension内で削除することもできるが、さすがにそれを強制するほどフレームワークにするつもりはない。
削除フェーズが必要になるという意味で考えれば、DXRubyExtensionにそういった機能があってもいいのかもしれない。
どうせrb_internでIDを取得するんだから、ループ中から呼び出すメソッド名を指定することができるとか、削除条件が入ったインスタンス変数名も後から指定できるとか、すればより汎用性も上がりそうだ。
閑話休題


やったことと言うと、移動フェーズで使う値のうち、initializeで計算できるものはしておく、とか。
例えば初期化時に角度とスピードを指定してずっと直進する弾だったら、xとyの増分は先に計算できる。
画面の左端にはみ出したチェックの値も、-image.widthとするとメソッド呼び出しが2回になるが、initializeでインスタンス変数に格納すれば0になる。
数が多いければ多いほど、こういう修正が効いてくる。
このレベルの微妙な話は、Cなんかでは数万回ループとかになってこないと出てこないのだが、Rubyは遅いから百回のループでも影響は大きい。
切り口を変えると、オブジェクト数分のループ内部でのキワドイ最適化で影響が出てくるほど、DXRubyではキャラを動かすことができる。
しかしライブラリが速いのはRuby負荷の割合が激増することに繋がるわけで、単純にそれだけで富豪ゲームプログラミングができるわけではないのだ。
ライブラリが速くなればなるほど、Ruby側での最適化を意識しないと、性能を発揮できないということになる。
まあ、それは極限レベルでの話であって、普通に使う分には問題はない。
俺がおかしなことをしているだけだ。


あとは、モジュールがメソッド経由で提供していた当たり判定オブジェクト配列を、グローバル定数に出した。
モジュールのメソッド呼び出し→インスタンス変数検索→配列pushという流れだったのが、グローバル定数の配列へpushするだけになる。
これもオブジェクト数分だけ実行されるから、結構効果が高い。
設計的に言えばモジュールに持たせるのが正しいわけだが、えてしてゲームプログラミングというものは設計の正しさより実行性能を取る。
規模の大きい業務系システムでは最初は性能を考えずにメンテしやすいようにプログラムを書いて、性能問題が出てきたらチューニングするという感じになるが、ゲームでは性能問題が出るのはわかりきっているから、そういうことには普通ならない。
業務系では性能を考えないといっても、設計段階ではきちんと考える。あくまでもコードの話だ。


結局のところ、その言語、環境で、可能な範囲の上限を狙うようなゲームを作ろうと思うと、ライブラリ側ではなくゲーム側の努力が必要だ。必要スペックの下限を狙う場合も同様だ。
これは可能な範囲が違うだけで、この点についてだけ述べればどの言語でも当てはまる。
ただ、ゲームをただ見る側、遊ぶ側にとっては、Rubyでそれを実現するためにどれほどの努力をしているのかなど関係なく、Cで作られたようなゲームと比較して速い遅いと考える。
そうなってくると、アクションゲームみたいなものをRubyで作ろうと思うこと自体が、限りなく酔狂なことなのかもしれない。
気楽に作れるはずのRubyを使っていて、遅いから苦労して最適化、とかになってしまったら、まさに本末転倒だ。
Rubyを使うからには、それで気楽に作れる程度のゲームを気楽に作る、というのが本来のあるべき姿なのだろう。
まあ、それを底上げしたい、という思いが根底にあるわけだが。