いぬおさんのおもしろ数学実験室

おいしい紅茶でも飲みながら数学、物理、工学、プログラミング、そして読書を楽しみましょう

Unityで、バーチャルカメラをスクリプトでコントロール!

バーチャルカメラで「カメラワーク」を実現します。前回の記事より少しだけ難しいことをやります。前回の記事 ↓

www.omoshiro-suugaku.com

前回のようにカメラを動かすと、いわゆる3D酔いをしてしまうかも知れません。画面がぐらぐら揺れる感じのゲームだとホントに酔うんですよね……(酔ったことある!!)。動きを抑えればある程度防げるので、クエリちゃんがあっちを向いたりこっちを向いたり、要するに上下の回転軸の周りを回るときは舞台は動かさず、前方(クエリちゃんの近くの大きな矢印方向)に動かすときに必要分をぐるっと動かすようにしてみます。文字では分かりづらいです。動画をどうぞ。

drive.google.com

このように、クエリちゃんが正面(PCの画面の上方)を向いていないときに「↑」キーを押すと、ジワッと画面が回り、クエリちゃんは画面上方を向いて動くようにしたいのです。やはり前回と同じように、バーチャルカメラは用意しておき、カメラのコントロールスクリプトでやってみます。大体のロジックとしては上向き矢印キー(クエリちゃんを前方に動かす)を押した時刻とそのときのカメラの位置、角度を記録し、押してから現在までの時間を計算し、それに応じてカメラの位置と角度を滑らかに変化させるのです。今回の目的では、次のルーチンをUpdate()内で、「↑」を押したときだけコールするようにすればOKです。「↑」を何秒間押しているか、それをratioに入れるとよいです(押している時間 > 1秒 なら ratio = 1 とします)

    void setCameraPara(float ratio) {
        Vector3 p = getPos(go1);//クエリちゃんの位置
        Vector3 f = getForward(go1);//クエリちゃんの前方
        Vector3 u = getUp(go1);//クエリちゃんの上方
        Vector3 c1 = getPos(goBackCamera);//古いカメラ位置
        Vector3 c2 = p + 20 * u - 23 * f;//① 新しいカメラ位置
        Vector3 c = Vector3.Lerp(c1, c2, ratio);//② カメラの位置を補間
        setPos(goBackCamera, c);
        Vector3 dir = p - c2;//③ カメラからの視線はこの向き
        Quaternion rot1 = getRot(goBackCamera);//カメラの古い向き
        Quaternion rot2 = Quaternion.LookRotation(dir);//④ カメラの新しい向き
        setRot(goBackCamera,
                Quaternion.Slerp(rot1, rot2, ratio));//⑤ カメラの向きを補間
    }

go1は主人公(クエリちゃん)のゲームオブジェクト、goBackCameraは(クエリちゃんの後方少し上から撮影する)バーチャルカメラのゲームオブジェクトです。
goがゲームオブジェクトのとき、getPos(go)は go.transform.positionを、getRot(go)は go.transform.rotationを返すだけの関数です。setPos(go, p)はgoの位置をpに、setRot(go, q)はgoのrotationをqにセットします。getForward(go)はgo.transform.forwardを返す関数です。getUp(go)も同様です。どれもコードの見通しがよくなるように自分で定義した関数です。

クエリちゃんは空間内を動き回ります。①ではクエリちゃんの後方、少し上あたりにカメラをセットしたいので、その位置を計算しています。②は位置ベクトルc1, c2を結ぶ線分上で、ratio(0≦ratio≦1)に応じて内分点cを求めます。ratio = 0 ならc1、ratio = 1 ならc2となります。③は最終的にカメラからの視線はこのベクトルと同じ向きになって欲しい、そういうベクトルです。④でカメラにそちらを向かせるためのクオータニオンを求めています。回転後、つまり目標のクオータニオンです。⑤は②と同様、ratioに応じてrot1からrot2までの角を内分した角を計算しています。⑤はratio = 0 ならrot1、ratio = 1 ならrot2となります。

 

前の記事でも書きましたが、このくらいのことならスクリプトなしで、バーチャルカメラのパラメータを適切に定めるだけで実行できると思います。しかし、そう思っていろいろやってみたんですが、なかなか思いどおりのカメラワークになりませんでした。何だかわからないパラメータを相手に四苦八苦するのもつまらないので、もう自分でスクリプトを書いてみることに。それが今回の結果です。

Unityでは、オブジェクトの向きはインスペクタのrotationに入っています。入っているのはクオータニオンです。インスペクタではx、y、z軸周りの角度が見えますが、これはオイラー角というやつで、前に記事にしたこともありますが一筋縄ではいきません。これを見て単純に「あちらを向いている」などと判断できないのです。今回のスクリプトも前回と同様、オイラー角は扱わず、クオータニオンをいじっています。クオータニオンはそれぞれが空間内のとある向きを表しています。そういうわけで、この記事では「角」と「クオータニオン」の用語を何となくごっちゃに使っています。