ゲームプログラミングになぜ範囲付きswitch文が必要と考えるのか?
2chで範囲指定付きのswitch文はどうでもいいことと言われた。
しかし、私はどうでも良いとは思わなかった。fiberはどうでもいいと言わないのに。いや重要なんだよと思っていた。でも、うまく説明できなかった。で、説明を考えてみました。
私はゲームのプログラムをする上で重要な沢山のキャラクターの動きを記述するのには少なくとも3つの考え方があるのだと思う。状態の遷移として考える考え方と、動作の連続として考える考えかたと、タイムテーブルで考える考え方だ。状態の遷移として考える場合は、c言語の構文で書くことが出来る。動作の連続として考えるやり方はthreadや、無駄を無くしたfiber,microThreadさらに、状態の遷移に書き換える、やねうらお氏のcontinuation corutineがある。
http://d.hatena.ne.jp/ABA/20031212#p2
http://d.hatena.ne.jp/shinichiro_h/20040106
http://d.hatena.ne.jp/yaneurao/20040524
では、タイムテーブルで考える考え方とはどういった考え方なのか?それは、アニメーションツールでキャラクターの動作を作っていくような考え方だ。「何フレーム目にはこのような動作をさせ、何フレーム目にはあのような動作をさせたいなぁ。」と考える考えかただ。
具体的例としては、格闘技ゲームのキャラクターの動作を考える場合などは何フレーム目であるかということが、とても重要だ。
X68000用のキャラクターを自分で追加することができるStreet Fighter XVIのソースはまさに「何フレームから何フレームは」というif文の嵐だった。あれから10年ほどたっているから現状のプログラミングは変っているのかもしれないが、、、。
それを無駄なく書ける構文があれば、開発効率を上げることができる。
こういう物の考えかたをしているときに、状態遷移として書き直したり、動作の連続として書き直せというのは、アプリケーションを昔のflashのアクションスクリプトで書き直せと言われているくらい苦痛なものだと思うのだ。flashで作ったアプリケーションをJavaのプログラムに落とせと言われているようなものだ。
こう書くのが無駄なくていいと思う。
timetable(i){ key 1:/* 1〜10 */break; key 11:/* 11〜20 */break; key 21:/*その他*/break; }
timetableがswitchみたいな構文で、keyがswitchのcaseにあたる。
これは、以下のif文に置き換えるのが無駄がほとんどなく効率的だ。
テーブルジャンプできないので、キーが1000個あったら、1000回if文実行しなきゃいけないっていう問題はあるが。
if(i<1){ goto sonota; }if(i<11){/* 1〜10の処理 */ }else if(i<21){/*11〜21の処理 */ }else{ sonota: /*その他の処理*/ }
gotoが嫌ならこんなかんじ。
for(;;){ if(i>=1){ }if(i<11){/* 1〜10の処理 */ }else if(i<21){/*11〜21の処理 */ } break; } /*その他の処理*/ break; }
ただ、この書き方には問題を感じる部分がある。処理が大幅に長くなった場合に、key 1の次のkeyが何かを探すのが面倒になるという点だ。それを考えると、こう書くほうが良いことになり、
timetable(i){ key 1..10:/* 1〜10 */break; key 11..20:/* 11〜20 */break; key 21.. :/*その他*/break; }
これは、switchに範囲指定の構文を増やせばキーワードを増やさずに解決できることになる。
switch(i){ case 1..10:/* 1〜10 */break; case 11..20:/* 11〜20 */break; case 21.. :/*その他*/break; }
範囲指定のswitch文はこう展開できる。
bool checkRange(int n,int start,int end) { if(n>=start && n
これなら、考えどおりかけるだろう。例え、ソースが長くなっても、ここからここと書くのだから間違いない。だから、構文で持つ必要などないだろう。となるかもしれない。けど、昔の私は思いつくことが出来なかった。頭悪いんだな。そして、最初の書き方が気に食わなくていろいろ悩んだ。構文にそういう構文があればいいのにと思った。この書き方は、関数呼び出しのオーバーヘッドがあるし、2回値を評価しなくてはいけない。最初のif文より非効率的だ。関数呼び出しはinline展開すればいい。cならdefineマクロにしておけばいい。でも、2回評価しなきゃならないのを回避することは出来ない。これは良いコードを出力する(つまり、小さくて高速なものを出力する)上で重要な問題だ。
これを解決するには構文を追加してコンパイラで最適化するしかない。
if文の連続は沢山あった場合の問題は、やねうらお氏のほげほげソリューション(kaskal)よろしくの状態の遷移に書き換えることで最適化できる。状態の遷移に書き換えるアルゴリズムは汎用的にするとなると難しいだろう。でも、人はそれをやってのける。状態の遷移に書き換えるプログラムが難しいというのであれば、それは、タイムテーブルで考えている人がそれだけ難しいことを考えないと、状態の遷移に書き換えることが出来ないと言うことを証明することにもなる。
汎用的な言語にこのような構文は要らないかもしれない。
でも、Rubyには範囲指定付きのswitchがある。
追記: ABA氏のところで、
にもかかわらず、実際にはあんまり使われていない感じがするのは、世の中のゲーム製作者があまりにフレーム単位に分割した書き方に精通してしまっていて、fiber的な手続き一発の記述に逆に面食らったりするからだろうか。
と書いてあるので、フレーム単位に分割した書き方ってのがどういったものなのか知らないから、知りたいところです。
http://d.hatena.ne.jp/ABA/20031212#p2