【Unity】transformを使いこなす!座標・回転など自在に操作しよう!

今回はUnityのtransformについて見ていきます。

Unityで物を作る上で、最も重要な物の一つが座標の操作です。

例えば赤い帽子をかぶったヒゲの人が主人公の某アクションゲーム。

めまぐるしく、キャラクターが動き回りますよね。

コントローラーを操作すれば主人公は右へ左へ動きます。

こんな風にゲーム中の物を自在に動かすには、座標を保持しているtransformをマスターしなければなりません。

今回はそんな重要なtransformを一緒に学んでいきましょう。

ではこの記事では、具体的に以下のような部分を見ていくことにします。

・transformとは?
・transformをエディタ上で触ってみよう!
・transformをプログラムで操作しよう!

Unityの使用者でも、よく悩まされる、ローカル座標系・ワールド座標系についても説明しています。

座標がなぜかずれるなどの問題に悩まされている人もぜひ参考にしてみてください!

では、最後までお付き合い頂けますと幸いです。

目次

transformとは?

まずは公式リファレンスの説明を見てみましょう。

オブジェクトの位置、回転、スケールを扱うクラス

シーン内のすべてのオブジェクトは Transform を持ちます。
Transform はオブジェクトの位置、回転、スケールを格納し、操作するために使用されます。
すべての Transform は親を持ち、階層的に位置、回転、スケールを適用することができます。
これはヒエラルキーウインドウで階層を見ることができます。

参照元: https://docs.unity3d.com/ja/current/ScriptReference/Transform.html

なるほど…なかなか難しいことが書いてありますね…

しかし、確かに重要なことも書いてあるのがわかります。

リファレンスを簡単にまとめると以下の3つのことがわかりました。

①シーン内のすべてのオブジェクトがtransformを保持している。
②位置、回転、スケールを保持している。
③他のオブジェクトとの親子関係を持っている。

どうやらすべてのオブジェクトがTransformを保持しており、そしてオブジェクトの状態をいろいろと保持してくれているようですね。

では次にそれぞれを噛み砕いて、どういうことなのか見ていきましょう。

すべてのオブジェクトがTransformを保持している

unity-transform-0

Unity上で何かオブジェクトを作成すれば、必ずInspector画面上に、座標などを操作する部分が付いてきますよね?

よくよく見てみると、「Transform」としっかり書いてありました!

普段何気なく使っていて気付かない人もいるかもしれませんが、Transformはどんなオブジェクトを作っても自動的に付いていることがわかると思います。

位置、回転、スケールを保持している

unity-transform-1

・Position(位置)
・Rotation(回転)
・Scale(スケール)

これらの要素を確かに保持していることが確認できます。

他のオブジェクトとの親子関係を持っている

unity-transform-説明(軽量化・圧縮)

画面のように【親にしたいオブジェクト】へ【子にしたいオブジェクト】を投げ込むと親子関係を持たせることができます。

またこの時、親のオブジェクトを操作すると、子供も合わせて操作されます。

unity-transform-操作(軽量化・圧縮)

確かに親を動かすと子供も付いてきていますね!

この親子関係をうまく利用すれば、例えば「【主人公オブジェクト】に、【剣オブジェクト】を持たせる」なんてことも簡単にできます。

transformを編集しよう!

先ほどまでの内容で、transform自体については理解が深まったと思います。

ではここからは実際にtransformの使い方について見ていきましょう。

transformをエディタ上で触ってみよう!

まずはtransformのエディタ上での操作方法をまとめました。

エディタ上でtransformを操作するには以下のツールたちを使います。

unity-transform-5

画面左上にいつも表示されているボタンたちですね!

それでは一つずつ見ていきましょう。

MoveTool

unity-transform-8-0

ボタンをクリックするとオブジェクトの座標を操作するモードが起動します。

(キーボードWボタンでも呼び出せます。)

オブジェクトを選択した上で、このモードになっていると、以下のようにオブジェクトから矢印ができているはずです。

unity-transform-6

この矢印を、マウスで引っ張るとオブジェクトの位置を操作できます。

RotateTool

unity-transform-8-1

ボタンをクリックするとオブジェクトの座標を操作するモードが起動します。

(キーボードEボタンでも呼び出せます。)

このモードになっていると、以下のようにオブジェクトが線で囲われます。

unity-transform-8

この線をマウスで引っ張るとオブジェクトを回転させることができます。

ScaleTool

unity-transform-8-2

ボタンをクリックするとオブジェクトの座標を操作するモードが起動します。

(キーボードRボタンでも呼び出せます。)

このモードになっていると、以下のようにオブジェクトに四角の刺さった棒のようなものが表示されます。

unity-transform-10

この四角部分をマウスで引っ張るとオブジェクトのサイズを変更が可能です。

RectTool

unity-transform-8-3
次のボタンはRectToolです。

これについては主に2D空間上でのサイズ操作に使用するツールですので今回は割愛します。

最後のTool

unity-transform-8-4

これは使ってみればすぐにわかりますが、MoveTool・RotateTool・ScaleToolを同時に扱えるToolです。

unity-transform-11

画面がごちゃごちゃとしてしまうため、個別のToolを使うか、これを使うかは人によるかと思います。

それぞれ使いやすいものを使用しましょう!

transformをプログラムで操作しよう!

もちろん、transformはプログラム上からでも操作できます。

プログラムからの操作方法も見ていきましょう!

下準備

まずは今回使用するオブジェクトを準備しましょう。

何かオブジェクトを作成し、新たにスクリプトをAddComponentしましょう。

unity-transform-12

上の画像は、以下の流れで作った例です。

①Hierarchy上で「右クリック→3DObject→Cube」とCubeオブジェクトを作り選択し。

②「AddComponent ボタンをクリック→NewScript→MainCharacterという名前でCreateAndAdd」

スクリプトがついたオブジェクトの作り方がわからない人は参考にしてみてください。

自身のtransformを取得する

例えば、自身のtransformを取得するには、以下のようなスクリプトを書きましょう。

public class MainCharacter : MonoBehaviour {
    void Update () {

        // Transformも自身のコンポーネントの一つなので、GetComponentで取得できる
        // 自身のTransformを取得して、myTransform1と定義した変数へ入れる例
        Transform myTransform1 = this.gameObject.GetComponent<Transform> ();

        // 省略して以下のように取得することもできます
        // 自身のTransformを取得して、myTransform2と定義した変数へ入れる例
        Transform myTransform2 = this.transform;
    }
}

このような記述で、自分のtransformを取得できます。

あとは取得したtransfromに対して、操作をすればオブジェクトの移動・回転・拡縮がおこなわれるわけですね。

また余計なアクセスが少ないため、処理速度的には「this.transform;」を使用したほうが早いです。

基本的には「this.transform;」を使用しましょう。

親子関係

座標などに入る前に、まずは親子関係について見ていきましょう。

実は親子関係もtransformが管理しています。

public class MainCharacter : MonoBehaviour 
{
    GameObject obj; // 親にしたいオブジェクトを入れておく

    void Update () {
        // transformを取得
        Transform myTransform = this.transform;

        // objを親として設定
        myTransform.parent = obj.transform;
    }
}

parentは親という意味です。

上記のようにparentへ別オブジェクトのtransformを入れることによって、プログラム上からでも、先の説明で出てきたような親子関係を設定できるわけです!

ローカル座標・ワールド座標について

親子関係が発生した時によく混乱する点として、ローカル座標・ワールド座標があります。

座標系にローカル座標系・ワールド座標系といった「二つの座標系の概念」が発生するのです。

二つの違いを簡単に説明すると以下のようになります。

ワールド座標とは、3D空間全体の中心を中心座標(x=0,y=0,z=0)として自分がどこにいるのかを表す座標。

ローカル座標とは、親のいる座標を中心座標(x=0,y=0,z=0)とした座標。

例えば先ほど出てきた「【主人公オブジェクト】に、【剣オブジェクト】を持たせる」状況をイメージしてください。

この時【主人公オブジェクト】も【剣オブジェクト】も、座標(x=0,y=0,z=0)にあることとします。

その状況で【主人公オブジェクト】の座標をX軸方向に10に変更すると、親子関係として付属している【剣オブジェクト】も一緒にX軸方向へ10移動されることになります。

unity-transform-操作(軽量化・圧縮)

この時【剣オブジェクト】の座標は以下のようになります。

ワールド座標上では、3D空間全体の中心を中心座標(x=0,y=0,z=0)としているため、X座標は10

ローカル座標上では、親のいる座標を中心座標(x=0,y=0,z=0)としているため、X座標は0

二つもあるので混乱してしまいますね・・・

しかし重要な点でもあるので、transformをいじる場合は常に気にしていきましょう!

座標関係

では取得したtransformを使ってオブジェクトの座標周りを操作してみましょう。

座標の取得
public class MainCharacter : MonoBehaviour {
    void Update() {

        // transformを取得
        Transform myTransform = this.transform;

        // ワールド座標を基準に、座標を取得
        Vector3 worldPos = myTransform.position;
        worldPos.x;    // ワールド座標を基準にした、x座標が入っている変数
        worldPos.y;    // ワールド座標を基準にした、y座標が入っている変数
        worldPos.z;    // ワールド座標を基準にした、z座標が入っている変数

        // ローカル座標を基準に、座標を取得
        Vector3 localPos = myTransform.localPosition;
        localPos.x;    // ローカル座標を基準にした、x座標が入っている変数
        localPos.y;    // ローカル座標を基準にした、y座標が入っている変数
        localPos.z;    // ローカル座標を基準にした、z座標が入っている変数
    }
}

このようにtransformのpositionやlocalPositionを使用し、Vector3型で座標を取得できます。

※このプログラムは、取得しただけで変数を使用していないためエラーが出るので注意してください。

座標の変更

そして以下のように、取得した座標を修正し再度代入すると、オブジェクトの座標を変更ができます。

public class MainCharacter : MonoBehaviour {
    void Update() {

        // transformを取得
        Transform myTransform = this.transform;

        // ワールド座標を基準に、座標を取得
        Vector3 worldPos = myTransform.position;
        worldPos.x = 1.0f;    // ワールド座標を基準にした、x座標を1に変更
        worldPos.y = 1.0f;    // ワールド座標を基準にした、y座標を1に変更
        worldPos.z = 1.0f;    // ワールド座標を基準にした、z座標を1に変更
        myTransform.position = worldPos;  // ワールド座標での座標を設定

        // ローカル座標での座標を取得
        Vector3 localPos = myTransform.localPosition;
        localPos.x = 1.0f;    // ローカル座標を基準にした、x座標を1に変更
        localPos.y = 1.0f;    // ローカル座標を基準にした、y座標を1に変更
        localPos.z = 1.0f;    // ローカル座標を基準にした、z座標を1に変更
        myTransform.localPosition = localPos; // ローカル座標での座標を設定

        // ワールド座標を基準に、現在の座標からの相対的な、移動方法
        myTransform.Translate (1.0f, 1.0f, 1.0f, Space.World);

        // ローカル座標を基準に、現在の座標からの相対的な、移動方法
        myTransform.Translate (1.0f, 1.0f, 1.0f);
    }
}

「直接【myTransform.position.x = 1.0f;】としてはダメなのか?」と疑問に思うかもしれませんが、アクセス修飾子の設定により、直接変更はできないため注意が必要です。

※アクセス修飾子がわからない人も、直接はアクセスできないのだと覚えておいてもらえれば構いません。

また最後のようにTranslateメソッドを使用すると、現在の位置から、指定した量の移動も可能です。

回転させる

次に回転を見ていきましょう。

※回転に関してはQuaternion型として取得することもできますが、今回は初心者向けに、分かりやすいVector3で取得・操作する方法で説明していきます。

回転角度の取得
public class MainCharacter : MonoBehaviour {
    void Update () {
        // transformを取得
        Transform myTransform = this.transform;

        // ワールド座標を基準に、回転を取得
        Vector3 worldAngle = myTransform.eulerAngles;
        worldAngle.x; // ワールド座標を基準にした、x軸を軸にした回転角度
        worldAngle.y; // ワールド座標を基準にした、y軸を軸にした回転角度
        worldAngle.z; // ワールド座標を基準にした、z軸を軸にした回転角度

        // ローカル座標を基準に、回転を取得
        Vector3 localAngle = myTransform.localEulerAngles;
        localAngle.x; // ローカル座標を基準にした、x軸を軸にした回転角度
        localAngle.y; // ローカル座標を基準にした、y軸を軸にした回転角度
        localAngle.z; // ローカル座標を基準にした、z軸を軸にした回転角度
    }
}

eulerAngles・localEulerAngles関数によりそれぞれ角度を取得できます。

回転角度の変更
public class MainCharacter : MonoBehaviour {
    void Update () {
        // transformを取得
        Transform myTransform = this.transform;

        // ワールド座標を基準に、回転を取得
        Vector3 worldAngle = myTransform.eulerAngles;
        worldAngle.x = 10.0f; // ワールド座標を基準に、x軸を軸にした回転を10度に変更
        worldAngle.y = 10.0f; // ワールド座標を基準に、y軸を軸にした回転を10度に変更
        worldAngle.z = 10.0f; // ワールド座標を基準に、z軸を軸にした回転を10度に変更
        myTransform.eulerAngles = worldAngle; // 回転角度を設定

        // ローカル座標を基準に、回転を取得
        Vector3 localAngle = myTransform.localEulerAngles;
        localAngle.x = 10.0f; // ローカル座標を基準に、x軸を軸にした回転を10度に変更
        localAngle.y = 10.0f; // ローカル座標を基準に、y軸を軸にした回転を10度に変更
        localAngle.z = 10.0f; // ローカル座標を基準に、z軸を軸にした回転を10度に変更
        myTransform.localEulerAngles = localAngle; // 回転角度を設定

        // ワールド座標を基準に、現在の回転量からの相対的な、回転方法
        myTransform.Rotate (10.0f, 10.0f, 10.0f, Space.World);

        // ローカル座標を基準に、現在の回転量からの相対的な、回転方法
        myTransform.Rotate (10.0f, 10.0f, 10.0f);
    }
}

eulerAngles・localEulerAngles関数に代入し、変更も行えます。

またRotate関数を使用し、現在の回転量からの相対的な回転も行えます。

サイズを変える

最後にサイズについてです。

サイズの取得
public class MainCharacter : MonoBehaviour {
    void Update () {
        // transformを取得
        Transform myTransform = this.transform;

        // ワールド座標を基準にした、サイズを取得
        Vector3 lossyScale = myTransform.lossyScale;
        lossyScale.x; // ワールド座標を基準にした、x軸方向へのサイズ
        lossyScale.y; // ワールド座標を基準にした、y軸方向へのサイズ
        lossyScale.z; // ワールド座標を基準にした、z軸方向へのサイズ

        // ローカル座標を基準にした、サイズを取得
        Vector3 localScale = myTransform.localScale;
        localScale.x; // ローカル座標を基準にした、x軸方向へのサイズ
        localScale.y; // ローカル座標を基準にした、y軸方向へのサイズ
        localScale.z; // ローカル座標を基準にした、z軸方向へのサイズ
    }
}

ここで注意しなければならないのは、今までの移動・回転と違い「lossyScale・localScale」と関数名が対になっていないことです。

サイズの変更
public class MainCharacter : MonoBehaviour {
    void Update () {
        // transformを取得
        Transform myTransform = this.transform;

        // ローカル座標を基準にした、サイズを取得
        Vector3 localScale = myTransform.localScale;
        localScale.x = 2.0f; // ローカル座標を基準にした、x軸方向へ2倍のサイズ変更
        localScale.y = 2.0f; // ローカル座標を基準にした、y軸方向へ2倍のサイズ変更
        localScale.z = 2.0f; // ローカル座標を基準にした、z軸方向へ2倍のサイズ変更
        myTransform.localScale = localScale;

    }
}

変更の仕方は移動・回転と同じですね!

代入することでサイズの変更が行えます。

ただし、ここで一つ注意点があります。

ワールド座標を基準にしたサイズ変更が簡単には行えません。

lossyScaleへ代入ができないのです!

実はlossyScaleとは、単純に値を取得・代入できる関数ではなく、ワールド座標基準でのサイズの近似値を算出しているだけの関数なんです。

そのため代入ができないので注意してください。

また対応する場合は、親オブジェクトのサイズを取得してきて自力で計算する必要がありますが、初心者には難しいため、いったんここでの説明は割愛します。

まとめ

いかがでしたでしょうか。

transformは、基本的にほぼすべてのオブジェクトについてくるコンポーネントです。

これを使いこなせれば、Unity上で出来ることが大幅に増えますよ。

今回説明した内容はまだまだ初心者向けの最低限の物です。
まだまだ奥が深いので、引き続き学んでマスターしていきましょう!

この記事を書いた人

学生時代を含めると、かれこれ10年以上プログラマーとして過ごしています。
様々な言語や環境、プロジェクトに関わってきましたので、より実践的な記事をみなさんにお届きるよう情報発信していきます!

目次