0からスタート!ゲーム開発ブログ

ゲーム開発に関する様々な記事を更新します

【Unityゲーム開発の基本】Transformの階層構造を使ってみよう

はじめに

こんにちは、azarashin です。 以前の記事でTransform の使い方について紹介したかと思います。

wizardia.hateblo.jp

上記の記事では単一のオブジェクトを動かしていましたが、実際のゲームでは階層構造をもったオブジェクトを扱うことがあります。 本記事ではTransform を用いて階層構造をもったオブジェクトを制御する事例について紹介していきたいと思います。

エディタでオブジェクトに階層構造を持たせる

これは非常に簡単です。 まずエディタ上に設置済みのオブジェクトを選択します(今回はCube とします)。 そして、右クリック→3D Object -> Sphere のようにしてオブジェクトを作成すれば、Cube の下にSphere オブジェクトが生成されます。

Hierarchy ウィンドウでCube の下に少しだけ右側にずれたSphere が配置されれば、Cube とSphere との間に親子関係がせっていされていることになります

親子関係が設定されると何が起こるのか?

Hierarchy 上で見た目が変化することは分かりましたが、実際のゲームではどのような作用があるのでしょう? これはずばり「親の変化に合わせて子も変化する」ということです。

親に合わせて移動する

例えば親のオブジェクト(Cube)を動かしてみます。すると、子のオブジェクト(Sphere)も親のオブジェクトに連動して動くようになります。 一方、子のオブジェクト(Shpere)を動かしても親のオブジェクト(Cube)は動きません。 このように、階層構造を持たせたオブジェクトにおける依存関係には方向性があるので注意しましょう。

親に合わせて回転する

今度は親のオブジェクト(Cube)を回転させてみます。すると、子のオブジェクト(Sphere)も親のオブジェクトに連動して回転します。 移動の時と同様、子のオブジェクト(Shpere)を回転させても親のオブジェクト(Cube)は動きません。

回転の中心位置を変える

これはかなり大事なテクニックです。先ほどの例ですと直方体の中心位置が回転の中心となっています。 しかしながら、実際のゲームでは回転の中心がオブジェクトの中心とは限らず、オブジェクトの端を中心に回転させたいケースもでてきます。 回転の中心位置を変えるには以下のような流れでオブジェクトを配置していきます。

  1. 空のGameObject を作成する(Root)
  2. Rootが回転させたいオブジェクトの中心位置にくるように移動する
  3. RootにCubeをドラッグ&ドロップする
  4. Rootを回転させる

上記のような流れで回転の中心位置に空のオブジェクトを設置して親子関係を作り、 空のオブジェクトを回転させることで、元々親だったオブジェクト(Cube)の回転の中心位置をずらした状態で回転させることができます。

親に合わせて回転する(多階層)

今度はもっと深い階層を持たせて回転させてみます。 親のオブジェクトは子のオブジェクトに影響を与え、さらに孫・ひ孫のオブジェクトにも連鎖的に影響を与えていることがわかります。

この時気を付けないといけないのは、子のオブジェクトに対して伸縮させないようにすることです。 伸縮させてしまうと回転させたときに形が崩れてしまいます。

親に合わせて伸縮する

親のオブジェクト(Cube)を伸縮させてみます。すると、子のオブジェクト(Sphere)も親のオブジェクトに連動して伸縮します。

ローカル座標系

最上位のオブジェクトの位置や回転・伸縮状態はワールド座標系という座標系で扱われます。 この座標系においては、オブジェクトの見た目上の位置・回転・伸縮値がそのままPosition, Rotation, Scale に反映されます。

例えば、オブジェクトを右側に動かすと

positionも連動して変化します。

一方、子のオブジェクトの位置や回転・伸縮状態は親のオブジェクトを基準としたローカル座標系という座標系で扱われます。 この座標系においては、親のオブジェクトから見た相対的な位置・回転・伸縮値がPosition, Rotation, Scale に反映されます。 そのため、子のオブジェクトの見た目の位置が変化していたとしてもPosition, Rotation, Scale がそのまま変化するとは限りません。

例えば、

元の状態に対し、親オブジェクトと一緒に子オブジェクトを動かしたとしても

子オブジェクトのPosition は変化しません。 (親オブジェクトのPosition は変化しています。)

スクリプトを使う

今度はスクリプトを用いて姿勢を変化させてみます。 スクリプトを記述する時に気を付けないといけないのは座標系の区別で、座標系によって使用するメンバ変数が異なります。

種別 グローバル座標系 ローカル座標系
位置 position localPosition
回転 rotation localRotation
伸縮 lossyScale localScale

例えば以下のようなスクリプトを記述します。

    void Update()
    {
        float speed = 1.0f;
        float centerSize = 2.0f;
        float deltaSize = 1.0f; 
        float scale = centerSize + Mathf.Cos(Time.time * Mathf.PI * 2.0f * speed) * deltaSize;
        transform.position = new Vector3(scale, 1.0f, 1.0f);
    }

これを含むSampleObject クラスをSphere 部分に付与して再生してみます。 この時、親オブジェクト(Cube)を動かしてもSphere は同じ場所を振動しています。

一方、上記のスクリプトを以下のように変更してみます。 具体的にはtransform.position の部分をtransform.localPosition に変更しています。

    void Update()
    {
        float speed = 1.0f;
        float centerSize = 2.0f;
        float deltaSize = 1.0f; 
        float scale = centerSize + Mathf.Cos(Time.time * Mathf.PI * 2.0f * speed) * deltaSize;
        transform.localPosition = new Vector3(scale, 1.0f, 1.0f);
    }

このようにスクリプトを変更して再生すると、 親オブジェクト(Cube)を動かした時にSphere はCubeの位置に合わせて場所・姿勢を変更しながら振動するようになります。

このように、親オブジェクトの姿勢に連動させるかどうかによって使用するメンバ変数が変わってきますので注意しましょう。

終わりに

今回はTransform の階層構造の使い方について紹介しました。 階層構造を使いこなすことで、親オブジェクトと連動させて動きを変化させるだけでなく、 親オブジェクトが見えなくなったり消されたりした時に子オブジェクトも見えなくしたり消したりすることができるようになります。 ゲームが複雑になってくると階層構造を使うことが多くなってきますので、少しずつ身に着けていきましょう。