MCL(106)

インデックスに戻る

前のMCLへ  次のMCLへ

▼ My Computer Life (106)

106. 3D格闘ゲーム作成(前編)

 前回書いた通り、仕事の閑散期に、職場や自宅などで、プログラム技術向上を目的とした何かしらのゲームを作ろうかと思った。技術向上が目的なので、どこかで見せたり配布したりするつもりは無かったし、完成させなくてもOKだった。
 何を作ろうかと考えて、まずは3Dをやろう、というのはすぐに思いついた。もう2Dのゲームは技術的は大体できるわ、ってなってたし。3Dと言っても、別になんでも良くて、レースでもシューティングでも問題なかったんだけど、なぜか当時「格闘ゲームを作ろう」って思って作り始めた。なんで格闘だったんだろう。今ちょっと思い出せない。会社で格ゲーが流行ってたからかなあ…?

 で、一番肝心の、キャラクターの3Dモデルは、当時開発中で、ボツになった(タイトルごとPendingになった)キャラがいたので、それを拝借した。どうせどこかに公開するわけではないし、私の勉強のためだけなので、問題はないという判断だ。
 今回作りたい格闘ゲームからすると、ただモデルがあればいいという物ではない。それに、ボーンとウェイト情報がないといけない。
 まず、3Dポリゴンモデルで、我々が目で見て判別できる表面部分のポリゴンは、サーフェス、あるいはメッシュなどと呼ばれる。で、格闘ゲームをするなら、腕や足など、関節が動いてくれないと困る。そこで、ボーン、つまり骨という、目に見えない別の情報が必要になる。ボーンは、概念的に、サーフェスの中、つまり骨として埋まっており、サーフェスのどの部分がどのボーンにくっついているか、という別情報を持っている。そこで例えば肘関節を曲げると、それに紐づいたサーフェスも移動する。これで、肘の曲げ伸ばしができる。この仕組みがないと、サーフェスの1ポリゴン1ポリゴンについて回転処理をかけなければならない。ボーンという概念を用いる事により、肘関節の角度という情報だけで多くのポリゴンを動かせる。
 で、初代バーチャファイターのような、四角四角したモデルであれば、サーフェスとボーンだけでも行ける。しかし、これを作った2004年当時はもう少し進んでいた。例えば肘や膝などが、曲がった時に関節部が丸くなる。四角い積み木が蝶番で曲がった風にはならない。これはどうしているかと言うと、肘周辺のサーフェスの頂点を操作している。つまり、ポリゴンの形を歪めている。これがウェイト情報で、サーフェスの各頂点に、どのボーンにどれくらい引っ張られるか、が重みづけとして定義される。肘周辺であれば、上腕部と前腕部のボーンに引っ張られ、より上腕に近い方が上腕部に引っ張られる、みたいな濃淡がつけられている。これをうまく設定すると、皮膚や服が曲がるように、ポリゴンの継ぎ目がなくグニャッと曲がる。
 なので、私はサーフェスと、ボーン情報と、ウェイト情報と、そしてテクスチャが一体になったモデルデータを拝借してきた。これでやりたい事ができる。

 ボーンとウェイトという話になると、とても難しいように聞こえる。確かに、実際、ウェイトに応じてサーフェスを変形させる仕組みを自分で作るとなると、かなり時間がかかる。しかしそこは便利なもので、DirectX9にはそれに対応する関数がある。下準備がちょっと難しいが、適切に投入できれば、きちんとウェイト付きのモデルの操作ができる(俗にワンスキンなどと呼んだりする)。

 さて、3D格闘を作るからには、3Dを表示しないといけない。私の場合、まずそこからだった。
 ざっくり、3D表示は、空間に置かれるモデルと、それをどう2D画面に変換するかという、つまりカメラと、そしてモデルの陰影を指定するためのライト、これらが理解できていれば、まず表示はできる。
 ただ、DirectX9はかなり高級化の進んだライブラリなので、大部分をラッピングしてくれる。細かいところは、結構DirectXが面倒を見てくれる。
 その代わりに、プログラム全体が、DirectXならではのお作法に従わないといけない。私はそこから勉強だった。更に言うと、そもそもWindowsのお作法に従わないといけない。キー入力とか、画面の再描画とか、ウィンドウがアクティブになったとか、そういうOSが送ってくる各種イベントに対して、何が起きるかというのを定義していかないといけない。初めてだったので、このへんも手探りだったねえ…。更に更に言うと、C++での経験自体がちょっと少なかった。自分でイチからクラス設計して作るのは初めてだったかも知れない。

 で、なんとかこう、カメラとライトを設定して、簡単な四角ポリゴンを表示するところから始めて、あとは.xファイルという、DirectX標準のモデルを読んで表示させるのもやった。それから、アニメーションもどうにかこうにか導入できた。
 その次に来るのは、先ほど書いた、会社で作ったキャラクターモデルを読み込む事である。これは、Mayaという3Dツールで作られている。だからMaya独自のモデルファイルとして出力されるのだが、このMayaにDirectX用のエクスポーターを組み込む事で、解決できた。
 次が辛い。キャラのモーションを読み込むというタスクだ。これもMaya独自のファイルで出力されるのだが、これをDirectXの形式で読むのはだいぶハードルが高い。細かい事を説明すると時間がかかるが、要はモーションのキーフレームの途中の補間が思ったように行かないのだ。

 これを回避する方法を色々考えて、結局、DirectX側でクォータニオンを導入する事にした。
 クォータニオンとは、四元数、あるいハミルトン数と呼ばれる、数学的な意味での「数」の一種である。高校あたりの数学で「虚数」というのを習うが、その虚数の拡張版である。虚数とは、我々が一般的に使う数である実数に対し、もう1つの軸である虚数軸を加えた数字である。だから、虚数は、実部と虚部という2つの数値の組み合わせで表現され、一般的には「A + Bi」のように書く。Aが実部で、Biが虚部だ。iはそれが虚部である事を示している。で、クォータニオン(四元数)は、その虚部が3つになったようなものだ。だから表記としては、「A + Bi + Cj + Dk」と書く。これで1つの四元数である。実部虚部合わせて4つあるから、四元数だ。
 で、四元数自体は数学的概念なので、だいぶ昔からあったと思うが、これがコンピュータの3D描画に使われるようになったのは、ちょうどこの頃からだったと思う。だからわりと最先端の技術だった。
 四元数という概念を持ち込むとどうなるかと言うと、3D空間での「回転」をうまく定義できる。もし使わない場合は、一般的にはオイラー角という方法で回転を表現する。これは、物体をX軸中心に何度、Y軸中心に何度、Z軸中心に何度と、XYZそれぞれの軸周りの回転角度を指定する。
 このオイラー角表記でも、一応3Dでの回転は表現できるし、実際Mayaが吐き出す.animというファイルは、これで回転を指定している。しかし、オイラー角にはいくつか欠点がある。まず、XYZのどの順番に回転させたか、という情報が必要になる。3次元では、どの順番で回転させたかで結果が異なるからだ。そしてもう一つ、ジンバルロックという問題も起きる。これは特定の角度で回転させた時に、XYZ軸のうち2軸が揃ってしまう現象である。いい感じに90度回転させたりするとそうなる。例えばX軸とY軸が揃ってしまった場合、どちらを回転させても同じ結果になる。となると、実質XとZの2軸でしか回転できなくなり(Y軸回転はX軸回転と同義なので)、以後表現できない角度ができてしまう。
 これを防ぐのが四元数だ。四元数を使うと、3次元空間の任意の軸、つまり3次元空間における直線と、その直線のまわりを何度回転したか、の2種の情報を1つの数値で表現できる。なんでそんな虚数の親分が、3次元の回転を表現する事になるのかは謎だが、そうなるんだから仕方がない。考え方としては、3次元空間での直線は3つの成分が必要だし、その直線周りを何度回ったかという情報も1つの成分だから、合計で4つの成分を持つ四元数がそれを表現可能なのは、理に適う。

 という要請に基づき、今回の格闘ゲームではクォータニオンを導入する事になったが、ここから先も結構大変だった。長くなったので以下次回。


前のMCLへ  次のMCLへ

戻る

TOM 2019