DXRubyによるゲーム用のベクトルと行列の基礎

まあ、DXRuby1.1を出してから書け、という話ではあるのだが。
必要な機能の洗い出しや動作確認を兼ねているということで。


行列は数学的にはただの計算・表現方法の1つで、それそのものに意味があるものではない。
数学の本などを見ると、小難しい理屈がずらずら書いてあって、正直言ってよくわからない。
完全に理解して自由にいろんな計算に適用できるように、と考えると無茶だが、ゲーム開発に使う行列の使い方は数学的な行列のほんの一部で、行列の原理を理解する必要などない。
オブジェクト指向ならMatrixクラスに必要な計算を全部パッケージしてしまうから、どういう理屈でそうなるのかは知らなくてもよい。
現在3Dのゲームを作っている人のうち、数学的な行列を完全に理解してる人っていうのは、比率的にはずいぶん少ないんじゃないかと思う。
それぐらい、数学の行列は複雑・難解であり、ゲームで行列を利用するのは簡単ということだ。
DXRuby1.1にはゲーム用に使う行列の計算を、Matrixクラスという形で提供している。
これを使えば行列の原理や理屈は知らなくてもよい。これで何ができるか、どうやるのか、ということだけわかれば、ちょっと凝った表現が簡単にできるようになる。


ゲームに使う行列は、ベクトルとセットで使う。
ベクトルも数学的には難しいが、ゲームなので難しく考える必要はない。
行列よりかは使い道が多いからきちんと勉強してみると便利かもしれない。
DXRubyではVectorクラスという形で提供している。


で、ベクトルと行列で何ができるか、という話だが。
行列で表現できるのは「平行移動」「回転」「スケーリング」で、ベクトルは行列を適用するための座標の表現形式と言える。
一例をあげると、

x = 50
y = 100
v = Vector.new([x,y,0,1]) # ベクトル生成。xとyとzとおまじないの1
v = v * Matrix.createTransration(10,10,0) # 平行移動行列生成
x = v.x
y = v.y

行列を適用するのは*、掛け算のメソッドとなる。ベクトルは左、行列は右。結果はベクトルとなる。
Matrix.createTransrationは平行移動用の行列を生成する。
これを実行すると、xは60、yは110となる。
・・・?
普通に足せばいいんじゃない?
と思ったなら、それはまったくもって正しい。


座標の回転も行列でおこなうことができる。
行列を使わないと、

vx = x * cos( angle ) - y * sin( angle )
vy = x * sin( angle ) + y * cos( angle )

とかいう回転式を書くことになる。
DXRubyで提供している行列を使うと、この三角関数を駆逐することができる。まあ、代わりにベクトルと行列が入ってくるわけだが。

v = src_vec * Matrix.createRotationZ( angle )

んで、行列は結合することができる。
行列の結合はMatrixクラスの*メソッドでおこない、これによって2つの行列の効果を1つにまとめられる。

mat = Matrix.createRotationZ( angle ) * Matrix.createTransration( 10, 10, 10 )

このmatをベクトルに適用すると、座標0,0を中心にangleだけ回転し、10,10,10移動したベクトルが得られる。
ベクトルに複数の行列を連続で適用しても、行列をまとめて1個だけ適用しても、結果は同じだ。


簡単な応用を一つ。

require 'dxruby'

image = Image.new(50,50,[255,255,255])
s = 100
angle = 0
Window.loop do
  s += Input.x
  angle += 1
  vec = Vector.new([s, 0, 0, 1])
  for i in 0..7
    v = vec * Matrix.createRotationZ( 360 / 8 * i + angle ) * Matrix.createTransration( 320, 240, 0 )
    Window.draw( v.x, v.y, image )
  end
end

白い四角8個がぐるぐる回る。カーソルキーの左右で輪が大きくなったり小さくなったりする。


これだけだとインパクトが弱いが、DXRubyの擬似3D機能と組み合わせるとこんな感じになる。もともとはこういうことが簡単にやりたくて作った機能なのだ。

require 'dxruby'

image = Image.new(50,50,[255,255,255])
s = 100
angle = 0
Window.zn = -300
Window.loop do
  s += Input.x
  angle += 1
  vec = Vector.new([s, 0, -100, 1])
  for i in 0..15
    v = vec * Matrix.createRotationZ( 360 / 16 * i + angle ) *
              Matrix.createRotationX( 70 ) *
              Matrix.createTransration( 320, 100, -400 )
    Window.draw( v.x, v.y, image, v.z )
  end
end


さっきのプログラムに対して、数を増やしたり座標を調整したりしたが、一番のポイントはMatrix.createRotationX(70)だ。
これでx軸(画面の横方向の軸)を中心に70度回転して描画している。
つまり、輪っかが3次元的に傾いているのだ。


3次元の座標変換は非常にややこしく、三角関数を使ってやるのは大変なのだが、行列を使うととても手軽にできるようになる。
その手軽さとDXRuby1.1の擬似3Dの手軽さはなかなか相性がよい。
使う人たちが簡単に凝った表現ができるようになるといいなと思っている。