見出し画像

Unityやってみる25・「スクリプトによるダンジョン生成」の、カメラの位置を変更してみる

前回、「スクリプトによるダンジョン生成」の、復習をやってみましたが、ダンジョンの元データをいじって生成させた所、回転の方向がちょっと違うことがわかりましたね。今日は、なぜそうなるのかを調べて、どう修正するのがいいのかの、答えを見つけてみます。

まず、ダンジョンの元データがこれでしたね。

実行すると、こんな感じになりましたね。

データと実際のダンジョンの方向が、90度ずれているのが気になりますよね。実際は、こうなって欲しい所。なぜ、回転してしまうのでしょうか? 今日はその謎を、解決してみたいと思います。

で、どうやって調べるかを考えたのですが、まず、ダンジョン生成のために使っているTestScriptを手直しして、3Dオブジェクトがどの座標に、どういう方向で設置されているかを、全部コンソールに表示させてみます。そしてそれを、新規作成した別のプロジェクトに、手でひとつひとつ、置いていきます(汗。

ちょっと面倒な作業になりますけど、ゲーム内の座標系をちゃんと理解しておくと、あとあとすごく楽になります。やってみましょう。

まず前回作成したプロジェクトを開きます。ヒエラルキーパネルにはメインカメラと光源のみ。アセッツパネルに追加した、スクリプト(「緑色の#」)だけが存在する状態です。

そのスクリプトのアイコンを、ダブルクリックして開いてみます。

で、まず関数を一つ追加しました。下の画像の、背景色ブルーの部分です。スクリプトは後でまとめて掲載しますので、これを入力する必要はありません。

どういう関数かというと、GameObjectを渡されると、そのオブジェクトの名前と、座標と、回転と、スケールの拡大縮小の様子を、表示するというものです。オブジェクトをいじったり追加したら、この関数を呼び出すことで、すべてのオブジェクトの状態を、知ることが出来るのですね。

ただし、このスクリプト内で生成したオブジェクトは、その生成に使った変数名を、関数に渡せばいいのですが、「Main Camera」とか「Directional Light」などの、スクリプト内で変数として扱っていないオブジェクトの場合はどうやって関数に渡せばいいのでしょうか?

その答えはこれ。「GameObject.Find()」というメソッドを使うと、スクリプト外のオブジェクトを、直接参照することが出来るのですね。これは便利! これを知らないと、インスペクターパネルを使ってオブジェクトをスクリプトにアタッチするという、すごく特殊な操作が必要になります。「GameObject.Find()」、絶対に覚えておきましょう。

        my_debuglog(GameObject.Find("Main Camera"));
        my_debuglog(GameObject.Find("Directional Light"));

結果、全オブジェクトの状態の、コンソールへの表示機能を追加したスクリプトが出来ました。前回のスクリプトをこれで置き換えれば、そのまま動くと思います。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestScript : MonoBehaviour
{

    float num = 10f;

    string[] maze = new string[] {
        "■■■■■■■■■■",
        "■□□□□□□□□■",
        "■□■■■■■■□■",
        "■□■□□□□■□■",
        "■□■□□□□■□■",
        "■□■□□□□■□■",
        "■□■□□□□■■■",
        "■□■□□□■■■■",
        "■□□□□□■■■■",
        "■■■■■■■■■■",

    };

    //関数追加
    private void my_debuglog(GameObject obj)
    {
        Debug.Log(obj.name+": position.x=" + obj.transform.position.x +
                        " position.y = " + obj.transform.position.y +
                        " position.z = " + obj.transform.position.z +
                        " quaternion.x = " + obj.transform.rotation.x +
                        " quaternion.y = " + obj.transform.rotation.y +
                        " quaternion.z = " + obj.transform.rotation.z +
                        " scale.x = " + obj.transform.localScale.x +
                        " scale.y = " + obj.transform.localScale.y +
                        " scale.z = " + obj.transform.localScale.z
            );
    }

    private void make_ground(float num)
    {
        //Planeオブジェクトを追加して地面にする
        GameObject Plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
        Plane.transform.localScale = new Vector3(2f, 2f, 2f);

        //関数呼び出し追加
        my_debuglog(Plane);
    }

    private void move_camera()
    {
        //カメラの移動と回転
        transform.position = new Vector3(0, 10, -10);
        Quaternion newRotation = Quaternion.Euler(50f, 0f, 0f);
        transform.rotation = newRotation;

        //関数呼び出し追加
        my_debuglog(GameObject.Find("Main Camera"));
    }

    void SetBlock(float num, float x, float z, Color col)
    {
        GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);

        obj.transform.position = new Vector3((float)x * 2f - num + 1f, 0.5f, (float)z * 2f - num + 1f);

        obj.GetComponent<Renderer>().material.color = col;

        obj.transform.localScale = new Vector3(2f, 4f, 2f);

        //                Rigidbody rb = obj.AddComponent<Rigidbody>();

        //関数呼び出し追加
        my_debuglog(obj);

    }

    void Start()
    {
        make_ground(num);
        move_camera();

        Color col = new Color(0.3f, 0.3f, 0.3f);

        for (int i = 0; i < num; i++)
        {
            for (int j = 0; j < num; j++)
            {
              if (maze[i].Substring(j, 1) == "■")
                    {
                        SetBlock(num, i, j, col);
                }
            }
        }

        //関数呼び出し追加
        my_debuglog(GameObject.Find("Directional Light"));
    }
}

実行すると、コンソールにこのように情報が表示されます。

新規プロジェクトを作って、上記の情報に基づいて、カメラを移動し、地面を置いて、ブロックを3個だけ置いた状態です。元データの上を北とすると、そのデータに基づく壁を、南西から置いていってる感じですね。

X,Y,Z座標の、軸を確認すると、下記のようになっています。モデリングソフトによっては、高さがZになっていたりするのですが、Unityでは高さがYになっているので、私はよく混乱します。壁は、Xを左端に設定してZ軸方向に、手前から奥に向かって置いていくというプログラムになっていました。

カメラの位置は、ここです。四角い地面の手前の上空におかれて、地面の中央を見下ろしています。これによって、データでは一番上にある壁が、左に見えてしまっているのですね。まあ、それがあながち間違いとも言えないのですけど、直観的にわかりやすい方が、いいですね。

カメラの設定を確認してみます。Y(高さ)が10。これはいいですが、Z(東西)が-10となり、西側の上空にカメラが設置されています。これが問題ですね。あとRotation(回転)は、X軸を中心に右に90度回転。これによって、東方向を見下ろす形になっています。

つまりこうなっているわけですね。白い地面の中央が、座標(0,0)、ここから西に10移動した、座標(-10,0)の位置にカメラは浮いています。X軸は、上下に伸びる直線で、そのX軸を中心にプラス方向に回転させると、でカメラは50度、下に傾きます。つまり白い地面を見下ろす感じに傾くのですね。

壁が、北西から北東に置かれていっているとして、南側の上空のカメラから、北側を見下ろすようにするにはどうすればいいか。図にするとこうなりますね。ちょっと座標の表示がややこしいですが、X軸に+10移動することで、南に移動、そこでZ軸(東西に伸びる直線)に沿ってプラス側に50度回転させると、カメラは北側に50度傾き、地面を見下ろす形になります。

ただ、ちょっとまたややこしいのですけど、座標系には「グローバル座標」と「ローカル座標」というのがあって、カメラの「ローカル座標」で考えると、次のように考えるのが正解のようです。まずカメラを移動した後、ローカル座標のY軸に沿って-90度回転させる。次にカメラをローカル座標のX軸に沿って50度回転させる。うーん、ややこしいですね!

その設定にしてみました。

実行してみたら、こうなりました。うん、カメラは南側上空から見下ろし、壁が北西から北東に作られていく構図になっていますね。

では、元のプロジェクトに戻り、今わかったことを反映してみます。move_camera関数の、下記の水色の部分の設定を変更ですね。大した変更ではないため、ここはご自分でやってみてください。

結果、うまく出来ました! 元データと生成したダンジョンの方向が一致しているので、データの修正や、確認がしやすいですね。

以上で今回のお勉強は終了です。

もっと簡単に修正できると思っていましたけど、グローバル座標とローカル座標の理解ができてないと、難しいことがわかりました。スクリプトでシーンを作るとシーン作成は楽になりますが、オブジェクトの位置や回転の指定がすごく面倒になってしまうという大きなデメリットがありますね。

まあでも、スクリプトコピペだけで動作するサンプルというのはすごく価値があると思うので、私としては、今後も可能な限り、スクリプトのみで勉強を進めてみたいと思っています(汗。

いいなと思ったら応援しよう!