HSP

HSP3.5 HGIMG4 で物理サイコロもどき

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
z07dice1.png
転がる最中も出目候補を表示
実行結果2
z07dice2.png
出目が確定したら赤色で表示

なおサンプルコード内の qrot 関数は位置ベクトルをクォータニオン回転する関数なので、オブジェクトの向いている方へ延長するベクトルの計算などの他の目的にも応用して使えます。

注意

このサンプルでは物理サイコロの出目を判定していますが、初期状態やパラメータの都合で1~6までがきれいな乱数で出るわけではありませんのでそれを理解した上でご使用ください。このまま使用する場合はもっと派手に回転にするようにパラメータ設定値を調整して出目が適切にばらけるようにするなど対処するといいと思います。