第30回 3D計算の基礎体力(代数幾何はだいすきかー?) 99/4/22
日本の高校の数学教育っちゅーのは、どうしてあんなに頭の悪いカリキュラムになっているのか、やねうらおの知りうるところではないが、今回は、3D計算の為の基礎体力として一次変換について考えてみることにしよう。
高校の代数幾何の授業では何故か、行列は2行2列のものしか扱わないのである。このカリキュラムを考えた人は、よほど2行2列がお好きなようである。この行列にあるベクトルを掛けることを「一次変換」なんて呼んでいたような気がする。ひょっとすると2行2列限定でなかったのかも知れない。そのへんの数学的な定義は知らないのである。(こんな私が高校の数学の教育免状持っててええんかいな...)
まあ、ある行列Aによる一次変換によって、原点pは、どこに行くかとゆーと、
Ap=(0,0)
なわけで、一次変換っちゅーのは、原点は原点に行くような変換、すなわち、拡大・縮小・回転・鏡像・射影とか、まー、その手の変換なわけよ。つまり、どう頑張っても平行移動的なことは出来ないのよ。んじゃまー、平行移動もできるように、一次変換したあと平行移動するよーな変換を考えたくなるのが人間の性ってやつよ。
f(p): Ap + s
変換fによって、座標pが、Ap(行列Aによる一次変換)されてから、ベクトルsの分だけ平行移動するわけ。こーゆーのアフィン(affine)変換とか何とか言ったような気がする。よーするに、平行移動つきの、回転・拡大・鏡像ができるわけ。なんか3D計算にいるような気がしてきたでしょ?(笑)
まあ、実際に実装するときは、2行2列の行列Aと2次元ベクトルsを別個に用意するのではなく、2行3列のaffine変換用行列(以下、affine行列と呼ぶ)を用意して、そいつとベクトルを掛算するオペレータを定義していくべきなんだろうけど。
んで、もういっこ、このaffine変換
g(p): Bp + t
があるとする。いま、fとgとの合成変換について考える。g・f(p)という、pをfで変換したあと、gで変換するやつを考えよう。たとえば、fが回転移動、gが平行移動であるなら、g・fは、回転させたあとに平行移動させる変換である。これは、どんな式になるかとゆーと、
g・f(p) : B(Ap + s) + t
= BAp + Bs + t
BAは行列と行列の掛算なので行列、Bsは行列とベクトルの掛算なのでベクトルである。つまり、結局のところ、この合成変換もaffine変換であって、affine行列で表すことが出来る。つまり、合成演算に関して、このクラスは閉じているとも言える。(この手の変換で、特に有用な性質だ...)
ここまでは、2次元での議論であったが、ベクトルを3次元にして、affine行列を3行4列にすれば、そのまま3次元でこの議論が通用する。これがどんな風に役に立つだろうか?
ふつー、3Dで人体モデリング等をやっていると、指は手に、手に腕という階層関係があったりする。腕の関節をぐりぐり動かすとそれが手に伝わり、それがまた指にまで伝播しなくてはならない。こういうのは、階層をツリー構造で表しておき、その接点での位相関係をaffine行列で持っておけば良い。
つまり、指の座標を求めるような場合、その親たる手と指との位相関係を表すaffine行列f,さらにその親たる手と腕との位相関係を表すaffine行列gを順番に掛ければ、その指と腕の座標系とを位相変換する行列が得られる。つまり、f・gに、基準として腕の一点を与えれば、それに対応する指の座標が求まるというわけである。
逆に指の座標を基準として腕の座標を考えなければならないような場合、このaffine変換の逆変換がわかれば便利である。と言っても、affine変換は定義から、
f(p): Ap + s
であるから、行列Aの逆行列A’を用いて逆変換は、
f’(p): A’(p−s)
= A’p − A’s
と表すことが出来る。(これもまた、affine変換である!)
階層構造を持ったパーツを扱う場合、あるパーツを移動・鏡像・回転させたとき、それに従属するパーツも同時に、移動・鏡像・回転しなければならない。このような場合、このaffine変換は有用である。
affine変換は、ミラー(鏡像)も出来るので、たとえば「左手」は、付け根の関節のaffine行列だけミラーするものにして、「右手」のデータに接続しておけば、「左手」のデータをオミットするようなことも出来る。(実際、この機能は、結構有用だ。データ量の面からだけではなく、同じパーツを一元管理出来るというメリットがある)
余談にはなるが、その昔、3Dの処理を、affine行列の代わりに4元数を用いて実装している人の話を聞いたことがある。簡単に原理を説明すると、複素数同士の掛算が複素平面での回転移動に相当することを利用している。つまり、3Dの座標系の変換は、4元数の掛算に相当するというわけだ。詳しいところまでは知らないが、僕が思うに、affine変換と同じだけの計算量が必要だろうし、式もaffine変換ほど明快でないような気がするのだが。
しかし、不勉強なもんで、このへんの詳しいことを包括的に書いている3Dの教科書というのを見たことがない。ひょっとしたら、そういう本は存在しないのかも知れない…。
第31回 3D計算に関するアフターフォロー(BM2000で...) 99/4/24
3D強化月間かのように、3Dの話をまじめにしたのにはわけがあって、実は、これから作ろうと思っているBM2000は3D対応にしようと思うからである。もー、あんなデータばっか肥大化するビットマップはやめて、3Dにするんや!!ゲームとしてどうこうやないんや!3Dでぐりぐり動かしたいだけなんや!!ぐりぐり動いてたら、それでええんや!それだけがすべてなんや!(うそです)
まあ、アホな叫びはそれくらいにして…あれからしばらく4元数について考えていたのですが、4元数..もっと一般に言って、多元数っちゅーのは、本当に有用なものなんでしょうか?(今回は、えらい謙虚な文体やな...そらそうさ。自信ぜんぜんないもん<だめじゃん)
たとえば、4元数の掛算は、4×4=16回の実数の掛算と3×4=12回の足し算に展開されるのですが、4×4のマトリクスと4次元のベクトルを掛けているのと同じではないかと。電気工学の世界で複素数は大活躍してたりしますけど、あれにしても別にマトリクスで解けるわけで、もっと大胆に言ってしまえば、たとえば複素四元数によるローレンツ変換にしても、マトリクスでやっているのと何ら変わりないのではないかと...?
ところが…DirectX5のDirect3Dには4元数(クォータニオン)を扱う専用のクラスが用意されていたりする。D3DRMQUATERNIONがそれである。どうも、球形線形補完(slerp:spherical liner interpolation)するとき、4元数を用いたほうが便利らしい。それ専用の関数D3DRMQuaternionSlerpっちゅーのが用意されている。球形線形補完っちゅーのはですな、ある動きからある動きへ移行するときに、その間のフレームを埋めなきゃならんのだけど、その補完方式のことである。
うーん。別にマトリクスつこてもslerpはできるような気がするねんけど...。確かに4元数ほど明快ではないかも。となると、4元数の導入には、それなりの意味があるわけで...俺、少しはまじめに勉強しよっ!と思う次第である。(これでは前回のフォローになってないと言う話もある..)
第32回 スーパープログラマになるには?(2日でPerlの巻) 99/4/26
「プログラムを勉強してるんですけど」とか、「C言語勉強中です」とか、「ゲーム作りたいと思ってんですけど」あるいは、「ゲーム作るには、何から手をつけたらいいんですかー」とか、その手の質問が絶えないんで、こいつのバシっとした解答を書きたいと思う。
やねうらおは、先日、わけあってPerlを勉強した。 以前、庄井さんにいただいた「Perlプログラミング」という600ページオーバーの本がテキストである。要した日数は、2日。時間にして、約12時間。Perlの勉強に費やしたのは、それだけである。これでどの程度Perlが身についたのかはよくわからないが、掲示板のCGIぐらいはすらすら読めるし、自分の手で改造もできる。
だからどうだと言うわけでもないが、「C言語勉強中です」とか言うのを見ると、いつまで(あるいは、いつから)勉強しとんねん!と突っ込みたくなる。そんなもんは、2日だ。それほど突っ込んだ内容は必要ないし、そういうのは実際にプログラムしながら学べば良いからして、基本的な部分は、2日あれば十分である。逆に、それ以上の期間が、必要だというのならば、それは勉強の仕方を根本的に間違っているのだと言いたい!
どうも、日本人は単一言語種族だからか、言語ひいては、コトバの学習方法というのを知らない。あるいは、忘れている。学校でも頭の悪くなるような英語や国語の授業しか実施されない。ここには、様々な問題がはらまれている。それは、歴史的な経緯であったり、授業時間の短さ、学生のやる気のなさ、日本の教育制度、果ては社会や経済構造に至るまで様々な問題が深く関与している。
しかし、そういった、どうでもええやん(現実的には、それがとても切実な問題であったにせよ)的な問題をここで議論したところで、何も始まらない!そういう問題をすべてフラットにして、方法論のみを検討しなくては何にも生まれてきやしないのだ。そういう一切のしがらみを捨て、純粋に学習とは何やねん?を追求してこそ、初めて生産的な所業に辿りつけるのである。
まず、600ページ強も、読むだけで3日掛かるという人もいるだろう。こういう人は、みな、どうも間違った国語教育を受けてきている。そもそも、そういう人たちに、3日掛かって読んだうち、何がわかったのか覚えている限りのことを紙に書き出せと言ってみる。やねうらおは、こういう意地悪が大好きなので、たまにそういうことを誰、彼なしにやらせたりする。そういう人は、決まって2ページか、3ページしか書けない。だから問う。あなたは…3日かかって、3ページを読んでいたのかと?もちろん、大半の人はNo!と答えるだろう。しかし、僕に言わせれば、これは明らかにYes!なのである。この人たちは、3日かけても、3ページしか読めていないのである…少なくとも、覚えていないのである。その3ページは、重要な部分なのか?というと、それは、確かに重要だという部分のみを書き出す人もいるし、まったくそうでない人もいる。これはなんやねん?あんたら、どんな国語教育を受けてきてん?ちゅーかな、いまの日本の国語教育って何教えてんねん?本の読み方も教えてくれへんのか?文章読んで、どこが重要なんかその見分けかたも教えてくれへんのか?そんな教育なら、やめちまえ!である。
もっと、極端に言えば、この人たちは、それが重要な部分なのかそうでないのかを判断するために9割以上の時間を割き、残りの1割の時間を記憶に費やしていたわけである。実際のところ記憶に費やした時間は、1割以下の人もいる。はっきり言わせてもらって、それで賢くなるはずがないし、そんな馬鹿な方法で勉強する限り、3日ごときで習得できようはずもないのである。3日で習得することに何ら意味があるとは思わないけど、2,3日で出来る人がいるのに、1年かかっても出来ない人がいるのは、そこなのである。
奇跡的なスピードでプログラミング言語を習得するための十分すぎるヒントは書いたつもりである。ここから何か得るものがあったというなら、これ幸いである。
第33回 再帰にまつわるエトセトラ...その1.(再帰系−非再帰系の変換) 99/4/30
典型的な再帰によるフォルダ検索は、以下のような感じであろう。プログラムは、Cだけでは表現しにくいので、ところどころPerl的な表記を採用している。
search_dir(dir){
// dir以下のフォルダを検索する
file_list = empty;
search_onedirectory(dir,2); // 2階層下まで
}
search_onedirectory(dirname,recursive_level){
if (recursive_level < 0) return ;
getfiles(dir,files); // filesにファイルリスト(full
path)取得
foreach file of files { // filesのそれぞれのfileについて
if (file == directory) {
search_onedirectory(file,recursive_level-1);
continue;
}
if (file == bmsfile) addlist(filelist,file);
}
}
この手の再帰構造のプログラムを書くコツは、
1.まず、あるひとつの部分を処理するモジュールを用意する。
2.そのモジュールは、必要に応じて自分自身を呼び出す。
3.そのモジュールに、再帰レベルが一定になるなどの再帰の終了判定を用意する。
4.あとは、変数を初期化し、そのモジュールを呼び出す呼び出し部を書く。
という過程が一般的である。この場合なら、モジュールは、「フォルダ名が与えられたら、そのフォルダからrecursive_levelの分だけsubフォルダを検索する」ものであって、recusive_levelが再帰の終了条件、「subフォルダを検索」の部分に再帰的呼び出しを使っている。
余談ではあるが、この再帰記述を非再帰に変換する技術は、非常に興味深い。実際、プログラム変換、アルゴリズム変換などと呼ばれる技術があって、自動プログラミングや、アルゴリズムの等価性の検証(の自動化)などの研究につながる。
実のところ、やねうらおも、高校時代にアルゴリズム変換と自動プログラミングにハマり、定理自動証明系や、プログラムの正当性の検証など謎な学問に湯水のごとく時間を費やした覚えがある。
しかーし!!この手の自動変換は、adhocに(ケースバイケースで)出来ることもあれば、出来ないこともある。一般的には不可能なのである。やねうらおが得た第一の結論とは、そういうものであった。
ところが!!さきほどのプログラムのような、典型的な再帰プログラムは、必ず非再帰形に変換できるのである。まあ、この手のプログラムに不慣れな人にとっては、至難な業かもしれないが...
search_dir(dir){
// dir以下のフォルダを検索する
file_list = empty;
recursive = 2;
tfiles = empty; // 処理すべきファイルのリスト
recursive_loop:
if recursive < 0 goto recursive_next;
getfiles(dir,files); // filesにファイルリスト(full
path)取得
foreach file of files {
file{recursive} = recursive; // perlの連想記憶ね
//
再帰レベルをファイルリストの各ファイル属性として書き込んでしまう!
}
addtfile(tfiles,files); // それらを処理すべきリスト表に登録!
recursive_next:
while (tfile = shift tfiles) { // tfilesが存在する間ループする。
// shiftにより、tfilesの先頭の要素が切り出されてtfileに入る。(cf.Perl)
if (tfile == directory) {
recursive = tfile{recursive} - 1;
dir = tfile;
goto recursive_loop;
}
if (tfile == bmsfile) addlist(filelist,tfile);
}
}
説明は面倒なので、割愛する。「なんかよくわかんないけど、tfilesみたいな変数でローカライズしてるだけでないの?」と思った人、それ、正解である(笑) しかし、非再帰形に変換するときに一番問題となるのは、まさにその、変数のローカライズの問題なのである。
第34回 再帰にまつわるエトセトラ...その2.(再帰系−非再帰系の変換) 99/5/1
ローカライズという言葉自体、あまり聞いたことがない人が大半かも知れないが、ローカル変数を(明示的に)生み出すことだと思うと良い。FortranやPerlなど、サブルーチンへの引数を参照で渡す言語だと、そのサブルーチン内部でうっかり変数を破壊してしまうと、親元の変数まで侵食されてしまうため、サブルーチン内で別の局所変数を生み出し、それにコピーしてから使う。そのことである。逆に言えば、関数呼び出しをinline展開(呼び出さずに内部に埋め込むこと)しようとするには、変数をローカライズすることがまず基本となる。再帰形から非再帰形への変換もまた同様なのである。
なーんだ。それなら、そんな面倒な作業なんてしなくたって、再帰を使って記述してしまえばいいじゃん。せっかく関数なんて便利な機構がコンパイラから提供されてるのに、使わない手はないじゃん。という話も聞こえてこなくはないが、待ってくれ!!面白いのは、ここからなんや。(笑)
非再帰形になおすメリットとして、劇的にスピードアップが計れる可能性があるということだ。これは、関数呼び出しのオーバーヘッドだけでなく、非再帰になった故に、ある種の最適化が適用できるかも知れないからだ。tail recursion(関数の末尾で再帰的呼び出しを行なっているようなもの)は、簡単に非再帰形になる有名な例であるが、これにより、飛躍的にスピードアップが計れることが少なくない。VC++6.0で試したところ、これは非再帰形に最適化するようである。
有名な例であるが、ハノイの塔の解法プログラムは一般には、再帰的に記述する。Prologの教科書なんか見れば最初の方にサンプルとして載っているので、詳しいことは割愛するが、普通、このプログラムを書くと3つの関数が再帰的に呼び出し合っている形になる。こいつをまあ、剰余系を利用して一つの関数にしたものが、以下のものである:
void movedisk(int n,int a,int b){
if (n>1) movedisk(n-1,a,6-a-b);
printf("move %d from %d to %d\n",n,a,b);
if (n>1) movedisk(n-1,6-a-b,b);
}
int main(){
movedisk(5,1,2); // 柱1から柱2に5段のハノイの塔を移動させる
}
確か、この解法は、Knuth先生の”離散系−非離散系の数学”(『Concrete Mathematics』:コンピュータの数学とかいうタイトルで訳本があったと思う)でも紹介されている。実は、この問題を非再帰的に解くことが出来る:
1.Aの上のをBに動かせるなら、動かして、5へ。でなければ2へ
2.Aの上のをCに動かせるなら、動かして、1へ。でなければ3へ
3.Bの上のをCに動かせるなら、動かして、1へ。でなければ4へ
4.Bの上のをAに動かせるなら、動かして、3へ。でなければ5へ
5.Cの上のをAに動かせるなら、動かして、3へ。でなければ6へ
6.Cの上のをBに動かせるなら、動かして、5へ。でなければ1へ
Aにあった塔がBに移動するかCに移動するかは段数のパリティ(偶数か奇数か)によるが、Aから2^n−1回で移動するはずである。これも剰余系を利用して、もっと単純な形に出来るのだが、それは割愛する。なぜ、このようなアルゴリズム変換が可能なのであるかはここでは説明しない。そもそも、やねうらお如きにそんなことは説明できないだろう。みなさんも、一生かけて、十分に苦しんでいただきたい。(ハマるんだって。これが!)
また、アルゴリズム変換そのものが、自動プログラミングへの可能性を示唆している。そして、もっと記述力のある抽象性の高いプログラミング言語で記述することが出来たならば、プログラマーの作業というのは、現在の何十分の一にもなるだろう。やねうらおにとって、OOPや関数型言語というのは、かなりダサイ部類だ。肉体労働にも近いものを感じる。しかし、しばらくは、それを避けて通れそうにもない。まあ、僕が生きているうちに、僕が求めているようなプログラミングパラダイムは生まれてこないんじゃないかということも薄々感じている。言っても仕方ないことだが、出来ることならもう少し後の時代に生まれてきたかった...。
第35回 不思議な縁(業界って狭いんか?) 99/5/3
なんか、virtualびっくるネットの過去ログ4で、「でーた少佐」(現・ぷらむ少佐)という人が書き込みをしているのだが、この人もプログラマだったりする。それも、相当に有名な人であったりする。その人の開発日記を引用する。(無断でゴメン)
1999/01/16 [ぷらむ少佐] |
と、とうとう、JPEG
Direct Annexがベクターのダウンロードランキングで 1位になりました!。ただし画像カテゴリー部門ですけどね。(^^; 登録したのは去年の11月の終わり、いつも上にはFLMASKがあったので 三日天下ならぬ一週間天下かもしれませんが、なんか非常に嬉しいです。 とりあえず、明日はメンバー集まって飲み会決定です。(^^ |
と言う人である。この人の開発日記に、私のことが書いてあるので、それも引用する。
1998/12/23 [ぷらむ少佐] |
今日あるユーザーの方から「こんなん知ってます?大流行っすよ。」と言われて、早速ダウンロードしたのがBM98というゲーム。とりあえず起動しようとダブルクリックしたが、NTでは起動しないので(DirectX5用のようだ)ヘルプを見てみる、、、、な、なんと作者はやねうらお氏ではないか。 やねうらお氏とは、パソ通全盛期以来ご無沙汰だったが、思わぬ所で目にして少々驚き。ホームページの方も見てみる。その文体は相変わらず読み手を強引に異次元へ誘うパワーに満ち溢れていて、思わずニヤリとしてしまった、、、ん?びっくるネット?おぉぉぉぉなんて懐かしいフレーズ。(分かる人だけ分かって)。とりあえず見てみたい人は↓こちらへ。 で、そのやねうらお氏が作ったソフトとなると、失礼ながら私と比べたら100倍以上プログラミング・センスがある彼の事だから、起動する前からそのレベルは何となく想像できる。が、DirectXのためだけに再起動して Win95を立ち上げるのもめんどくさいので、続けてホームページの方を眺めてみる。やはり、多くの処理系に精通しているだけあって、非常にフェアな(MS教の信者みたいなプログラマにとっては逆に感じるかもしれないが)内容が多くて楽しい。強烈な一人ツッコミは大阪人じゃないとついて行けないかもしれないけど、それでも昔に比べたらオブラートで二重に包んだようなものだ。;-) 何やら仮想CPUモデルを使ったゲームソフト用共通プラットフォームを構築中のようで、仕様も公開されるらしいから、そのうちこっそりと何かクソゲー作って送り付けてやろうかと思っていたりする。瀬野SIMとか。(分かる人...推定3人) |
うーん。誉めてんだか、けなされてんだかわからんけど(笑) まあ、覚えててくれたようで、何よりです。
しかし、最近になって、昔の知人がどんどん有名人になるので、少々驚いている。確かに、昔のパソ通って、遅い通信速度で、高い通信料払ってやってたわけだから、マニアックで、よほど何か思い入れがある人が多かったのは確かなのだろう。インターネットでは得難い快楽がそこにあったと言っても良い。パソ通全盛期。そんな時代に生まれてこれたことに感謝したい。(前回と言うてることちゃうやんけ!>俺)
第36回 掲示板をcrackする(良い子は真似しないでねん) 99/5/5
つい先日、K2の運営している非公開掲示板がcrackされた。crackと言っても、それほど悪質なものではなく、むかつく奴の書き込みを一掃してくれるという、なんとも親切なcrackerである。(笑) (まあ、crackという行為自体は、それほど誉められたもんではないんでしょうけれど...)
それでまー、しばらくパスワードがわからんようになって困ったのよー。K2も管理者として、立場ないし。んなら、いっちょcrackし返してやるかーとか思って、K2からmini BBSのソース送ってもらったの。これ、Perlで書いてあって、相当、短い。昔、ホストプログラムTurbo Pascalで書いてたことあるけど、その1/20以下。プリントアウトして4枚ほどしかないんで、通勤時間中に読んじゃえーってことで、その日のうちに解読。
どうも、unix系って、ファイルの暗号化のためにcryptって組み込み関数あるみたいだけど、Perlでは、このsalt(暗号化キー)に、2文字の文字列しか指定できんのねー。しかも、ソースを見ると、その2文字は固定と来ている。ちゅーことは、総当たりでcryptして、その暗号化された文字になればOKってことで、10分ほどで作ったナリよ(笑) (あとでわかった話ですが、パスワードの堅牢性を調べる管理者向けのツールとして、これと同じようなものがいろいろとついてました>LinuxをインストールしたCD)
しかし、その暗号化されたパスワードってどうして入手できたんだろう?と思ったら、拡張子cgiをdatに変えたら、あら不思議。(あら不思議やあらへんがな!もっとわからんよーにしとかな!>K2) うーん。なんちゅー、ええ加減な管理者や。(笑)
そんなわけで、対策としては、
1.どうせ、暗号化されたパスワードは何らかの方法で抜かれてしまうだろうけど、cgiファイルとは無縁の別名にしておき、グループも変更できれば変更しておいたほうが良い。
2.暗号化部分を自前で用意する。cryptしている部分を、crypt2とか勝手に変えて、暗号化部分を用意する。アスキーコードで1足すとか、そんなのでもやらないよりマシ。
とか。でも、この程度ではcrackしようと思えば、crackできてしまう。うーん。ひょっとして、やねうらおって、crackerの素質あるんちゃうか?
第37回 路頭に迷う若葉マークプログラマーたちに愛の手を(お勧めの言語について) 99/5/9
初心者にお勧めの言語としてC(C++含む)を奨めるような野郎は、撲殺してもええ!とか言う法律は出来んもんかなー。(出来るわけないか...)
いきなりこんなことを言うのは、先日、仲間内で、初心者はプログラミングについて何から勉強すべきかとかゆーことを話してたのよー。やっぱし、わけわからん制限とか、言語固有の問題とか、そういうヤラシイ部分がなくって、より一般的な議論の出来る言語がええんでないかーという共通見解に達し、そういう意味からすればCって、学習には最低の言語やねーとか言う結論になったわけ。
Cがいまのように糞でっかい言語仕様になったのは、まあ、歴史的な経緯もあり、仕方ない面もあるが、もう取り返しがつかないほど糞な言語になっていると言っても良い。それは、言語仕様の大きさ、役にも立たないライブラリに代表される。
Cは、もうJavaにリプレイスされたほうが、いくらすっきりするかわからない。ついでに、不安定きわまりないWindowsも、Linux + X Windowぐらいにリプレイスされれば、どれほど幸せになるかわからない。そもそも、はじめっからWindowsなんて存在しないほうが良かったのかも知れないし、C++も、Cとの互換性なんか無視して、もっと洗練された形で生まれ変わるべきだったのかも知れない。無論、そんなことを言ったところで何もはじまらない。すべては、起こらなかった<現実>に過ぎないのだから。
では、何を初心者に奨めるべきか?...お手軽なWinプログラムと言えば、Visual Basicあたりだろうか?速度面でもそれほど不満はないし、VBからDirectXを呼び出すためのモジュールも開発されており、手軽にゲーム作成も可能だろう。
それに引き替え、Visual C++は、やる気ないんかい?と思わずにはいられない。わかりにくいオンラインマニュアルは言うに及ばず、脳味噌腐ってんでないのー?と疑いたくなるMFC、だいたいこんな糞ライブラリ強要すなっちゅーねん。自分、何様のつもりやねん!!と言いたくなる。VBのほうが入門書籍がVCの3倍以上も売れている。そらなー、こんだけ使いやすさに差があったら誰もVC使わんのかもなぁ...。
言うまでもないことだが、本来Cではグラフィックライブラリなんて、言語セット(set:集合)の中には含まれていないのである。もってのほかなのである。たとえば、3Dグラフィックが必要ならOpenGL(やMesa)を呼べば良いのだから。(他のプラットフォームでもOpenGLが実装されている必要はあるが) 逆に、どの言語からでもOpenGLが呼び出すことが出来、かつOpenGLは、どのプラットホームでも同様の挙動をするというのが理想である。ActiveXの狙いは、まさに、そこだったのだと思う。マイクロソフトの計画としては、UnixベースにさえもActiveXテクノロジを持ち込むというところまで話が進んでいたように思う。まあ、なんだかんだ言って遅れ遅れになるのだろうけれど、阿呆なことしかしてくれないマイクロソフトにしては、花まるな発想だったと言えよう。
このように、グラフィックは、グラフィック、音楽は音楽で、プラットホームを超えて同じ仕様のライブラリをどの言語からでも利用できるような手段が提供されているかどうかが重要なのである。よって、そこさえ守られているならば、言語に必要な機能は、ミニマム(最小限)で良いのだ。どの言語からでも同じ機能を利用できれば、言語間の(くだらない)格差が縮まる。数値演算だからFortranにしようだとか、会計・データベース処理だからCOBOLにしようだとか、そういう馬鹿な選択をしなくて良くなる。数学関数、文字列操作や連想記憶、n進木、リスト処理、正規表現パターンマッチング、メモリ管理、3Dグラフィック、音源再生、ネットワーク機能等々は、すべてライブラリとして提供されるべき機能だと僕は考えている。それを言語仕様のなかに含めるのは、言語の肥大化を招くだけであって、とてもナンセンスな話なのだ。
Javaを生み出したSunがお馬鹿だなーと思うのは、まさに、そこであって、どうしてJava固有のライブラリとして言語セットに含めてしまうのか。わざわざいろんなプラットホームに移植している意味がないのではないか。僕は、「ひとたび書けば、どこでもそのプログラムが動くこと」がそれほど重要なことだとは思わない。重要なのは、「ひとたび覚えれば、そのノウハウがどこででも通用すること」である。時代に求められているのは、Javaで書けば、どこでも動くよーなんかでは決してないのである!それはJavaを使いなさいと強要しているだけであって、いまさらそんな理由のためだけにJavaに乗り換えられないし、開発環境の切り替えやら、言語の修得やら、容易ならぬ開発投資が必要になるのは目に見えている。いまから新規に始めるプロジェクトならともかく、過去の資産を引きずって生活している企業にとって、そんなリスキーな選択は出来ない。Sunは、まるごと開発環境をJavaにしなさいと言って聞かない。僕に言わせれば、それはわがままだとしか言いようがない。まともなライブラリも用意されておらず、馬鹿遅いJava(下手するとVisual Basicの数十倍遅いことすらある)では、大規模な開発なんて怖くて出来っちゅーのがどうしてわからんかなー。マイクロソフトがJavaから直接WindowsのAPIを呼び出せるJDirectとか言う機能つけて問題になってたけど(よく知らない。ごめん。JDirectゆうたら、日本直販のテレビショッピングしか思いつかんのよ>私)、あれにしても、Javaの標準ライブラリでは全然間に合わないから、そうせざるを得なかったとゆーのがなんでわからんのかなー。
そんなわけで、言語間のきっちりとした呼び出し規約あるいはバイナリ間の呼び出し規約(ActiveXでも良い)と、各プラットホームで動作するきっちり公準化されたライブラリが必要なのであって、言語そのものは何でも良いのである。開発環境なんてのも、本来は、付随的なものである。しかし、実際のところ、これもまた、起こり得なかった<現実>に過ぎない。将来的に、そういう日が来ることもあまり期待は出来ないだろう。
まあ、僕がこの場で何と言おうと、SunはJavaについて、そういう実装しか出来なかったのだし、C言語もまたしかりである。実際のところ、Cは不必要と思えるような意見をもがぶがぶと実装し、瓦礫(がれき)の山と化した。ホビイストにとって、こんなアホな言語をまじめに相手するには値しないのである。ただし、依然として、ある程度のスピードと複雑さが要求されるシビアなプログラミングの世界において、Cがスタンダードであることには変わりない。そこで、ゲームを作るためのC言語学習法は、これだ。
1.DirectXを使うことを前提として進める。出来れば、人の作ったライブラリをそのまま使わせてもらうほうが、話が早い。(el/easylink libraryとか)
2.よって、入出力系の関数printfとか何とかは、一切不要。
3.ポインタ・参照・構造体・クラス等は、最初は、とりあえず使わずに済ます。(必要に迫られてからで良い)
うーん。そしたら、命令は、if,while,for,return,break,gotoと、関数の呼び出し、変数の宣言ぐらいしか残らないはず。それさえも覚えられないのなら、プログラミングなんて、やめちまえーである。どう?わかりやすいでしょ?