ゲームオブジェクトを一般化して管理しないことの妥当性について

なぜわざわざゲームオブジェクトクラスなんてつくって下方へ特殊化するクラスヒエラルキーをつくるんだろう。オブジェクト同士の連絡では、オブジェクトリストを持つ方は信号を伝達するだけで、ダウンキャストなりの特殊化はオブジェクト群のそれぞれの内側で行われるような運用をされる。か、もっと無茶をやる。なんでそういうことをやるのか。自然言語的にそうだから?慣習?リストを保持する方はゲームオブジェクトに新たなタイプが追加されても依存がないので編集する必要がない?こうすることでオブジェクト群から上をそっくり挿げ替えることも依存がないために可能になる?

しかし実際はオブジェクト生成部分が特殊化されたクラスに依存するし、群を走査する部分だけを挿げ替えるなんてありえない。そっちの反論はどうでもいいとして、ゲームルールはゲームオブジェクト同士の交互のやりとりを全体として眺めた状態を完全に規定する。プラグイン系のようなゆるい統合ではやっていけない。完全な記述が求められる。新たな種類のゲームオブジェクトが追加されるとしたら、全ての既存のゲームオブジェクトに対する反応が完全に新たにおこされてなくてはならない。ひとつのピースが残りの全部のピースとの接合部をもっている多次元ジグソーパズルだ。無機的な構造物と比べてここが全く異なっている。

ということで、オブジェクトリストを持つ層は一般化されたゲームオブジェクトのリストを一本持つのではなく、もっと特殊化したものを複数もっているべきだと主張する。例えばプレイヤー、敵キャラ、マップ上のトリガー、特殊なボス。という具合で。こうするとマップ上のトリガーが受け取ったオブジェクトなりIDを自分の内側でダウンキャストをしてプレイヤーじゃなかったら無視する、だのといった処理をしなくて済む。リストを持っている層がトリガーだけが持つ特殊なインターフェイスを使ってプレイヤーだけをトリガーへ渡して評価させればいい。「プレイヤーだけがマップ上のトリガを起動できる」というようなルールの断片がゲームオブジェクト内に分散されず、上層の走査部分にまとまることになる。

大規模アプリケーションプログラムの設計手法に毒されてこんなことにも気付かないでいたんですよ。まったくしようもない。こんなに長々考えぬかないと気がつかなかったようですよ。

構造化世界律を乱すもの

大抵の構造物はコンポジション構造で表現できるのだろう、車一台はどんどん分解していってネジ一個にすることができる。そしてそれはネジ一個がもつ属性とは無関係な設計図によってもとの車に戻ってくることができる。

なかなかそうはいかない場合もあって、設計図がつくれないとき、例えば複数のプラグインが参加して全体構造を形成する場合。これはジグソーパズルを一枚絵に切り目を入れて作るんじゃなくて、ピースひとつひとつから作っていくようなものだ。どうあがいてもピースにあたる同階層のオブジェクトが連絡しあわなくてはならくなり、構造化プログラミング原理主義者はここではまることになる。

これは独自のIDなど言語が本来持ってる型とは別の識別子を導入することで解決が図られる。これは動的で汚い。こういったIDを持つものは、IDの対象に依存している。依存しているがIDが本当に必要な対象を指しているという保証はない。なぜかというと、その参照それ自体が必要性そのものを構成しないから。ものによってはダブルディスパッチを使う方法もあるかもしれない。まあそんなことは実はどうでもいいんです。

問題にしたいのは、ジグソーパズルのピース同士が合致することが保証されてもそれだけでは全体の適正な形成を保証しないということ。例えばmozilla applicationのoverlayにはinsert-afterだのといった「このidをもつelementの直後に俺を挿入してくれ」みたいなattributeがあるが、そんなidをもつelementが存在しなければ意味をなさないし、そうしたところで全体としてみた場合のレイアウトには言及できない。

この問題を解消する方向へ向けるのがジグソーパズルでいうところの角や辺が平らであったりするような、全体の構造の形成に言及する足がかりとなるようなヒント(?)の存在である。firefoxのコンテクストメニューを例にとれば、様々なプラグインが勝手に挿入してくるアイテムの間に必ず存在するセパレータが用意されていて、ここを指定してinsert-afterすることである程度、実に完全ではないがある程度の全体構造への言及を可能にしている。

つづくかも…
ヒントについて考えたい。マクロとミクロの乖離とかそういう感じなんだろうか。わかりません。

雑記

ついに先日ブラウン管のテレビがぶっこわれたので、ラジオを聞いてたら最近本が売れないんでどうするね?というトピックについて、むしろ本を高くして金と時間がある人だけが見れるようにしてウンコみたいな新書がでないようにしろよとかそういうことをいっていて、どうせ本を読むことなんてそこまで余裕のある人達の道楽なんだよというようなことをいっていたような気がする。そしてある掲示板では、大学生が選んだ本のランキングにいまさら「幼年期の終り」が入っていたのを馬鹿にしてた人が自分のことをSFの古典をいちいち読むことができるほどの暇人であると卑下しておった。そういうものなのかもしれん。

あわせて読みたい

メモ

内乱記 (講談社学術文庫)

内乱記 (講談社学術文庫)

[新訳]ローマ帝国衰亡史 上 <普及版>

[新訳]ローマ帝国衰亡史 上 <普及版>

歴史 上 (岩波文庫 青 405-1)

歴史 上 (岩波文庫 青 405-1)

ゲルマーニア (岩波文庫 青 408-1)

ゲルマーニア (岩波文庫 青 408-1)

ローマ人の物語 (1) ― ローマは一日にして成らず(上) (新潮文庫)

ローマ人の物語 (1) ― ローマは一日にして成らず(上) (新潮文庫)

メモリリーク

行数やファイル名が報告されず、ゲームのプログラムなので何度目のアロケーションかという番号も一定せず、ゲームの動かし方とリークする量の変動を見て場所を特定することもままならず、リーク量は数十バイトで一定しているように見えたのでながらく放置していたメモリリークがあった。どうもプレイヤーを初期化せずに終了した場合はリークは検出されないのでそのあたりにあるものと踏んでいたのだが、先日これでダメならあきらめようと、ためしに一切のユーザーの入力を受け付けずプレイヤーの初期化まで自動的に進むようにプログラムを書き換えてみたところ、メモリリーク報告のアロケーションの通し番号が一定した。この番号を使ってブレイクしてみたところ、イベントリスナー的なクラスのメンバのstd::mapが削除されていないということがわかった。

原因は、イベントリスナーのインスタンスが渡された先が自分のデストラクタ内で保持しているすべてのインベントリスナーをdeleteするのだが、このとき過去の無理解か、見落としか、なぜだかvoid*としてやってしまっていて、そのためイベントリスナー本来のデストラクタが呼ばれず、呼ばれないがオブジェクト本体は消え、主人を失ったメンバだけがリークとして検出される、というものであった。イベントリスナーであるためほとんどの場合でメンバーを持たない。持っていてもイベントが発生しなければ量も変動しない、placement newを使ってるSTLの中なのでファイル名行番号が表示されない、など運悪く重なった要因が発見を困難にしていた。

影まとめ2

ESMについて。ぼかしたバッファに対して描画時のZ値との間に生じる差をある程度の範囲であると見積もってexp関数を使って無理矢理一定範囲に押し込めるように変換する、だけ。お手軽&高速&綺麗。ハードウェアサポートとか浮動少数点バッファがない貧乏人用(いい意味で)。調整次第。

VSMを整数バッファで近似してみた。ぼかす前の値をとっておいてぼかした後との差分の絶対値をだし、ちょいとスケールダウンしてσ^2として扱った。数学的な正当性は知りません。割と見られる範囲になったと思うけどもESMの前には霞むな。

影まとめ

Mount&Bladeでは木漏れ日の下をくぐるとちゃんと木漏れ日の影が、しかも輪郭をもたないぼやけた影が体に落ちる。木漏れ日の下を馬に乗ってパカパカとくぐりぬけたとき、どっかの美容外科の古いCMみたいなエロイ雰囲気になんだか感動してしまって、影界隈を調べてみた。なにかソフトシャドウを高速に描画する方法が発見されたのかなと思ったが違ったね。GeForceでサポートされてるシャドウマッピング用のハード機能を使ってるらしい。なにはともあれここへくるまで割と調べたりしたのでまとめておくことにする。

シャドウマッピング界隈はデプスバッファをどうやって効率よくびったり四角いテクスチャに収めるかとかライトスペース関連、とソフトシャドウとの2つのテーマについて様々な手法が提案されているようだ。どうやって収めるかは今はどうでもいい。後で広い地形を描画しなくちゃならなくなったら見ることにする。

PCFについて。とりあえず単純に考えれば影の輪郭をぼやけさせるために近傍の点を複数サンプリングして何個が影で何個が光があたってるかを数えて濃さを決めればいい、という考えに行き着く。だがデプスバッファの大きさに比べて描画する領域の方が解像度が高いのでどうしても影だけドット絵になってしまう。そこでUVから、マップのピクセル中心からどれだけ離れてるかを考慮してマップの同じ1ピクセルを参照した場合でも結果にグラデーションをつけたのがBillenear PCF。NVidiaはハードでこれをやってくれる。サンプルする点を半分ランダムにして結果にノイズを加えてバッファのピクセルの境界を誤魔化したのがIrregular PCF、UVの値に基づいてランダムなサンプル点のオフセット群を回転させて、隣の点は隣の点とあまり変わらないけど全体としてはランダムという微妙な要請を実現してる。おもしろいアイデア

VSMについて。サンプル結果をぼかさずに、デプスバッファ自体をぼかしちゃえばいいんじゃないの?というところから話をはじめる。デプスバッファ自体をぼかして、バッファのデプスとの差を結果のぼかしに使うと影を落とす面との距離がひらけばひらくほど差が大きくなって、現実と逆に遠いほど濃くて輪郭のはっきりした影になってしまう。VSMではデプス値の2乗も保存して一緒にぼかす。これを描画時に1乗の方を2乗したものと比べると、輪郭部分においてどれだけ大きなコントラストを補間したかが算出できる。これを使って実際のバッファのぼかしをリアリティのある数値へもっていく。描画時には一点をサンプルするだけでいいので軽い。浮動小数点テクスチャとその補間機能が必要。ハードでこれをサポートしているものはまだ少ない。事前計算できる場面なら普通に使える。非常に微細な差を調べるので浮動小数点の解像度がなくてはだめ。

はじめwikipediaにはトンデモ論文ばっかりがリストされててだまされた。NvidiaGDC関連からあたるべき。

しかし影のぼかしはDX10では標準になってるしNVidiaじゃハードウェア実装されてるし、ということを考えるとライトスペースの方が今世間的には重要なトピックなんだろうと思う。