画面外判定を考える

キャラのオブジェクトを配列で管理しているとして、キャラが画面外に出たときに配列から削除したい場合、以下のようなコードをよく使う。

@objects = @@screen.check(@objects)

ここでいう@objectsはオブジェクトの配列で、@@screenは衝突判定範囲を画面サイズに指定した画面外判定専用のSpriteオブジェクトだ。Sprite#checkはselfと引数の衝突判定をして衝突しているオブジェクトの配列を返すから、単純に考えれば非常に簡単で便利な手である。
しかしこの方法には3つほど問題がある。
(a) Sprite#collision_enable=falseとしたオブジェクトは画面内にいても削除されてしまう。
(b) 配列がArrayを継承したサブクラスだったとしてもArrayが返ってしまう。
(c) 衝突判定範囲と画像は必ずしも一致しない。
どれも割と厄介な問題である。


とりあえず上記判定コードを使うことを前提として考えると、(a)については衝突判定が必要な配列と不必要な配列に分けて管理すると解決できる。めんどいけども。(c)に関しては画面外判定用のSpriteをオブジェクトに持たせるとかそんな感じでどうにかできそうだ。しかし(b)に関しては不可能である。っていうか、(b)のような問題は配列を破壊的更新するぐらいしか手がなく、きちんとオブジェクトを生成する汎用的な手段は無い。
たとえばDXRubyにメソッドを追加するとして、どのような機能を提供すればいいのかというと、これは結構難問である。
画面外に出たオブジェクトを始末するという話なのだから、配列をいじるのではなくてSprite#vanishを呼ぶというのは手段として有効だ。Sprite#collision_enable=false対策は衝突判定処理を使わなければ問題ない。これで(a)と(b)は解決する。(c)についても衝突判定範囲を使わず画像のサイズで判定するようにすればよい。画面の範囲はSprite#targetで指定されたオブジェクトから取得するとよさそうだ。
判定処理は4点全部が画面外に出たという判定でいいような気もするが、画像が回転する可能性を考慮すると斜めになった画像が画面のカドにちょこっと入るというパターンで消えてしまうのでよろしくない。スクリーン矩形との真面目な衝突判定をすると回転した矩形の判定になるので作るのがめんどいし、何かしら細工して対処したいところ。
画面外判定メソッドを提供するとなると、もう少し考えないといけない。使える状況が限られすぎていては使い道が乏しくなってしまう。画面外に出たからといってすぐに消えるオブジェクトばかりではない。例えば回転運動する場合、画面外に出てから戻ってくることもありえるし、見えないところでも少しは生き残っていて欲しいものも作るものによってはあるだろう。そうすると、Sprite#targetで取得できる描画先のサイズだけではなく、独自にもうちょっと広い範囲を指定できるような機能が必要になる。判定メソッドを例えばSprite#within_range?という名前にした場合に、通常はそれでいいが、引数に[x1,y1,x2,y2]の範囲配列を渡せばその範囲で判定してくれるとかそういう感じになっているのが望ましい。
画面外に出ている時間がある程度長くなったら消すとかできるといいが、それは作るのが大変なので却下か。
まあ、そんなことをつらつら考えつつ、とりあえずRubyで実装して考えてみようか。とか思ってるところ。