フライトシミュレータ・・・・具体的にいうとエースコンバットや零式艦上戦闘機などのゲームをHSPで真似て作ってみたいと思うのは多分私だけではないはず・・



そこでHGIMG3を使ってやろうとするのだが、どうもsetangで指定するカメラの回転角度の計算でいつもつまずく。



早い話、視線を軸としてカメラを回転させたいのだ。

キー操作で説明すると、→←キーでローリングし、↓キーでピッチが上がり↑でピッチが下がるようなプログラムにしたいという訳である。


余談・・・

(実はHGIMG3のサンプルの中に飛行機ゲームがあるのだが、残念ながらそれは私の考えていたキーと回転の対応をしていなかった。逆にd3mのモジュールの中にこれだ、というサンプルを見つけた。しかしd3mとHGIMG3はまったく命令が違う上に、d3mの視点の回転命令が1つの命令にまとめられてしまって肝心な計算式がブラックボックスの中になっていた・・)



結果からいうと、setangの3つの角度だけではこの問題は解決できない!!


ではどうするかというと、新たに視点方向用の3つの方向ベクトルを作り、それで主な視点の計算を行うという方法をとらなければならない。


3つのベクトルというのは自分基準のx、y、z軸ベクトル、例えば飛行機でいうところの

 ・パイロットが見るような視線ベクトルと

 ・その法線である翼に平行なベクトル

 ・飛行機を真下から真上に見たときのベクトル

であり、この3つはそれぞれ直角同士でなければならないという制約がある。

補足・・・この3つのベクトルが視線方向を決めるのだから、逆に視線が回転すれば3つのベクトル全ての値が変わるというのは言うまでもない



主役を降ろされたsetangの3つの角度の使いどころは、本当に最後画面に出力するときにsetangでカメラ向きをセットするときだけ、なのである。


次に3つのベクトルでピッチやヨーやロールを取り扱う計算には、絶対クォータニオン(四元数)を知らなくては話が始まらない。

クオータニオンとは任意の軸に対してベクトルを回転させることができる計算方法である。

(計算には行列などがでてくる)



まぁクオータニオンの詳しい説明はこのブログでは端折らせてもらうとして、まとめると





ピッチ、ヨー、ロールのアルゴリズムの大まかな流れ・・・以下の例はロール

まず上記の3つのベクトルを作る

視線ベクトルを軸にして翼に平行なベクトルと機体に垂直なベクトルを回転する(ここでクオータニオンを使う)。

3つのベクトルをsetangの3つの値に変換(この変換が面倒)

setangでカメラ設定


という感じである。



ピッチのアルゴリズムは、翼に平行なベクトルを軸にして視線ベクトルと機体に垂直なベクトルを回転してやればいい。同じように機体に垂直ベクトルを軸にして視線ベクトルと翼ベクトルを回転させればヨーのアルゴリズムができる。



クオータニオンや座標変換については説明するのがすごく大変なので「いなえの鉛筆」さんなどのもっとわかりやすいサイトを見てもらって、このブログでは主に完成系のプログラムを公開することにする。





↓は上の3つの方向ベクトルsetangの3つの値stx,sty,stzに変換するプログラム(サブルーチン)

まず前提として3つの視点方向用ベクトルを

px py pz   (パイロットが見る視線ベクトル)

jx jy jz    (翼に平行なベクトル)

tx ty tz    (縦のベクトル)

とする。(これらは単位ベクトルでかつそれぞれが垂直である必要がある)


※ベクトル用の変数以外に勝手に使ってる変数は

a,c,d,x,y,z,w,ra,hx,hy,hz,stz1,stz2,q.0~q.3,r.0~r.3,rp.0~rp.3,rpq.0~rpq.3,π

配列は皆実数型でπ=3.1415・・・とする



*main

<クオータニオンの計算など>

・・・・・

・・・・・

gosub*henkan

setang HGOBJ_CAMERA,stx,sty,stz

・・・・・

・・・・・

goto*main




*henkan
if tx=0.0{
a=0.0
}else{
if (ty*jx-tx*jy)!0.0:a=(px*ty-py*tx)/(ty*jx-tx*jy)*jx/tx-px/tx:else:a=0.0
}
c=sqrt(1.0/(1.0+a*a))
d=c*a
hx=c*px+d*tx
if jx>0.0{
if hx<0.0:c=-c:d=-d
}else{
if hx>0.0:c=-c:d=-d
}
stx=atan(-d,c)
hz=c*pz+d*tz
hx=c*px+d*tx
hy=c*py+d*ty
sty=0.0
if (jz*hx-jx*hz)!0.0:d=hx/(jz*hx-jx*hz)
if hx!0.0:c=-d*jx/hx
sty=-atan(-d,c)
x=tx:y=ty:z=tz
ra=-stx/2.0
q=cos(ra):q.1=jx*sin(ra):q.2=jy*sin(ra):q.3=jz*sin(ra):r=cos(ra):r.1=-jx*sin(ra):r.2=-jy*sin(ra):r.3=-jz*sin(ra)
gosub*ks
hx=x:hy=y:hz=z

x=jx:y=jy:z=jz
ra=-sty/2.0
q=cos(ra):q.1=hx*sin(ra):q.2=hy*sin(ra):q.3=hz*sin(ra):r=cos(ra):r.1=-hx*sin(ra):r.2=-hy*sin(ra):r.3=-hz*sin(ra)
gosub*ks
c=hy
d=sqrt(1.0-c*c)
stz1=atan(d,c)
c=y
d=sqrt(1.0-c*c)
stz2=atan(d,c)
if absf(stz2-stz1-π/2.0)<0.0005:stz=-stz1
if absf(stz2+stz1-π*1.5)<0.0005:stz=-stz1
if absf(stz1-stz2-π/2.0)<0.0005:stz=-2.0*π+stz1
if absf(stz2+stz1-π/2.0)<0.0005:stz=-2.0*π+stz1
return





*ks
ddim rp,4;r*p
rp=-r.1*x-r.2*y-r.3*z
rp.1=r*x+r.2*z-r.3*y
rp.2=r*y+r.3*x-r.1*z
rp.3=r*z+r.1*y-r.2*x
ddim rpq,4;r*p*q
rpq=rp*q-q.1*rp.1-q.2*rp.2-q.3*rp.3
rpq.1=rp*q.1+q*rp.1+rp.2*q.3-rp.3*q.2
rpq.2=rp*q.2+q*rp.2+rp.3*q.1-rp.1*q.3
rpq.3=rp*q.3+q*rp.3+rp.1*q.2-rp.2*q.1
x=rpq.1
y=rpq.2
z=rpq.3
w=sqrt(x*x+y*y+z*z)
x=x/w
y=y/w
z=z/w
return





この、ベクトル→setang回転角変換プログラムは私がオリジナルで考えて、なおかつ途中から適当にやったら偶然できたプログラムなので実は自分でも良くわかってない。

とても見にくくて、スパゲッティになっているが、あしからず・・




次にベクトルの回転のみのプログラム(=クオータニオン)

※このプログラムだけではsetangの3つの値に変化はない

→←キーでローリング、↑↓キーでピッチ上下するというもの。


説明・・・・・回転させたいベクトルの値を x、 y、 z に代入し、軸となるベクトルは配列変数 q と r に sin(rad) を掛けて代入する。 配列の最初には cos(rad) をそれぞれ入れておくこと。 この状態で ↑ の *ks へ gosub すると回転計算結果が x、 y、 z に出力される。

またradにはベクトル回転角度を初期設定しておく


*main

stick key,$3ff
hgdraw
hgsync 16

if key&4:rad=0.02:gosub*k1
if key&1:rad=-0.02:gosub*k1
if key&8:rad=0.02:gosub*k2
if key&2:rad=-0.02::gosub*k2

<中略・・>

・・・・

・・・・

・・・・

goto*main



*k1
repeat 2
if cnt=0:x=jx:y=jy:z=jz
if cnt=1:x=tx:y=ty:z=tz
q=cos(rad):q.1=px*sin(rad):q.2=py*sin(rad):q.3=pz*sin(rad):r=cos(rad):r.1=-px*sin(rad):r.2=-py*sin(rad):r.3=-pz*sin(rad)
gosub*ks
if cnt=0:jx=x:jy=y:jz=z
if cnt=1:tx=x:ty=y:tz=z
loop
return

*k2
repeat 2
if cnt=0:x=px:y=py:z=pz
if cnt=1:x=tx:y=ty:z=tz
q=cos(rad):q.1=jx*sin(rad):q.2=jy*sin(rad):q.3=jz*sin(rad):r=cos(rad):r.1=-jx*sin(rad):r.2=-jy*sin(rad):r.3=-jz*sin(rad)
gosub*ks
if cnt=0:px=x:py=y:pz=z
if cnt=1:tx=x:ty=y:tz=z
loop

return


*ks

<上と同じなので中略>

return









一応この2つのプログラムをうまく駆使すれば、フライトシミュレータっぽくHGIMG3でも任意の軸回転ができるハズ!


ちなみに・・

初期設定で設定すべき

px py pz   (パイロットが見るような視線ベクトル)

jx jy jz    (翼に平行なベクトル)

tx ty tz    (機体に垂直なベクトル)

は、適当に決めていいのだが・・・

かならずベクトルは単位ベクトルになるように、また3つそれぞれが直角になるように設定お願いします。

そうじゃないと私のプログラムでは動きません・・・


また発見したバグは

px=0.0
py=0.0
pz=1.0

jx=1.0
jy=0.0
jz=0.0

tx=0.0
ty=1.0
tz=0.0
で初期設定すると、最初ピッチを上げようとすると画面がバグる

というものですが、すぐにローリング(左右キーを押す)すればバグは消えます。


初期設定では

px=sin(1)
py=0.0
pz=cos(1)

jx=cos(1)
jy=0.0
jz=-sin(1)

tx=0.0
ty=1.0
tz=0.0
をお勧めします。