getquat と setquat
HSP3.5 HGIMG4 では、getquat, setquat によりクォータニオンでのオブジェクト姿勢の取得または設定ができるようになりました。これによって物理演算の結果を他のオブジェクトの姿勢として適用するのが容易になりました。
今回はこの getquat, setquat を含めて物理サイコロもどきを作ってみます。
サンプルコードは以下のようになります。
#include "hgimg4.as"
#module
#deffunc qmul array a1, array a2, array a3, local l, local lre1, local lre2
// a1 と a2 のクォータニオン積を a3 に格納する
lre1 = a1.3
lre2 = a2.3
l.0 = a1.0, a1.1, a1.2
fvinner l, a2.0, a2.1, a2.2
a3.3 = lre1 * lre2 - l.0
l.0 = a1.0
fvouter l, a2.0, a2.1, a2.2
a3.0 = l.0 + lre2 * a1.0 + lre1 * a2.0
a3.1 = l.1 + lre2 * a1.1 + lre1 * a2.1
a3.2 = l.2 + lre2 * a1.2 + lre1 * a2.2
return
#deffunc qrot array ap, array aq, array ar, local p, local f, local c
// 座標 ap を クォータニオン aq で回転して ar に格納する
p.0 = ap.0, ap.1, ap.2, 0.0
ddim f,4
qmul aq, p, f
c.0 = -aq.0, -aq.1, -aq.2, aq.3
qmul f, c, ar
return
#global
#const EG_CALC GPDRAW_OPT_OBJUPDATE
#const EG_DRAW2D (GPDRAW_OPT_DRAW2D | GPDRAW_OPT_DRAW2D_LATE)
#const EG_DRAW3D (GPDRAW_OPT_DRAWSCENE | GPDRAW_OPT_DRAWSCENE_LATE)
#const DELTA 0.0001
randomize
gpreset
setcls CLSMODE_NONE
// 回転用障害物
gpbox id, 2, 0x333333
setpos id, 7.5, 5, -3.5 + double(rnd(100)) * 0.01
gppbind id, 0
// アヒルと箱
gpload id, "res/duck"
setscale id, 0.8, 0.8, 0.8
duck_id = id
gpbox id, 2, 0xcccc00
setpos id, 6, 9, -2
gppbind id, 1.0, 0.5
gppset id, GPPSET_DAMPING, 0.0, 0.0
gppset id, GPPSET_FRICTION, 0.5, 0.75
setalpha id, 128
box_id = id
// 床
gpfloor id, 40,40, 0x6666cc
gppbind id, 0, 0.5
gppset id, GPPSET_FRICTION, 0.5, 0.75
setangr GPOBJ_LIGHT, -96, 32, 0
setpos GPOBJ_CAMERA, 2, 21, 12
gplookat GPOBJ_CAMERA, 0, 3, 0
sdim faces,256,7
faces.0 = "none"
faces.1 = "head"
faces.6 = "foot"
faces.2 = "tail"
faces.5 = "chest"
faces.3 = "left"
faces.4 = "right"
ddim scores,7
// クォータニオン用変数
ddim q,4
ddim result,4
fix = 0
getreq prets, SYSREQ_TIMER
pre.0 = -999.0, -999.0, -999.0
repeat
getreq ts, SYSREQ_TIMER
getreq fps, SYSREQ_FPS
redraw 0
color 0,0,0x33
boxf
gpdraw EG_CALC
// 箱の状態をアヒルに反映
getpos box_id, x,y,z
setpos duck_id, x,y,z
getquat box_id, q.0, q.1, q.2, q.3
setquat duck_id, q.0, q.1, q.2, q.3
// 3つの基底を回転し (0,1,0) と内積を取る
p.0 = 1.0, 0.0, 0.0
qrot p, q, result
scores.5 = result.1
scores.2 = -result.1
p.0 = 0.0, 1.0, 0.0
qrot p, q, result
scores.1 = result.1
scores.6 = -result.1
p.0 = 0.0, 0.0, 1.0
qrot p, q, result
scores.4 = result.1
scores.3 = -result.1
gpdraw EG_DRAW2D | EG_DRAW3D
pos 16,16
color 224,224,224
font "",36
mes strf("%2d [fps]", fps)
max_score = -2.0
max_fix = 0
repeat 6,1
mes strf("%d:",cnt) + faces.cnt + ": " + scores.cnt
if scores.cnt > max_score {
max_score = scores.cnt
max_fix = cnt
}
loop
// 動きチェック
if fix == 0 {
diff = powf(x - pre.0, 2.0) + powf(y - pre.1, 2.0) + powf(z - pre.2, 2.0)
if diff < DELTA {
// 1.0秒止まったら確定とする
if ts - prets >= 1000 {
fix = 1
}
} else {
prets = ts
}
// 前回の位置を覚えておく
pre.0 = x,y,z
} else {
color 224,0,0
}
// 現在の目
gpcnvaxis x2,y2,z2, x,y,z
pos x2 - 9, y2 - 72
mes max_fix
redraw 1
await 1000/60
loop
サイコロの目を貼るのが面倒だったので代わりにアヒルの向きでサイコロの目の代わりにしました。
目 | アヒルの向き |
---|---|
1 | 頭上から |
2 | しっぽ |
3 | 左羽 |
4 | 右羽 |
5 | 正面 |
6 | 足 |
実行結果1 |
---|
転がる最中も出目候補を表示 |
実行結果2 |
---|
出目が確定したら赤色で表示 |
なおサンプルコード内の qrot 関数は位置ベクトルをクォータニオン回転する関数なので、オブジェクトの向いている方へ延長するベクトルの計算などの他の目的にも応用して使えます。
注意
このサンプルでは物理サイコロの出目を判定していますが、初期状態やパラメータの都合で1~6までがきれいな乱数で出るわけではありませんのでそれを理解した上でご使用ください。このまま使用する場合はもっと派手に回転にするようにパラメータ設定値を調整して出目が適切にばらけるようにするなど対処するといいと思います。