【Unity】取得したスクリーン座標をワールド座標に変換する方法

スクリーン座標をワールド座標に変換 Unity

こんにちは、ともくんのゲーム作り部屋にようこそ!

このページでは、

「スクリーン座標をどうやってワールド座標に変換すればいいの?」

「マウスのある位置のワールド座標が知りたい!」

というお悩みの方に向けた内容となっています。

Unityでは、カメラで実際に画面に表示されている平面上のスクリーン座標と、オブジェクトなどが配置されているワールド座標というものが分かれています。

そのため、ゲーム画面上でのマウス位置の座標はスクリーン座標で取得できますが、そこにあるオブジェクトを選択するような場合に、スクリーン座標をワールド座標に変換するといった必要がでてきます。

このスクリーン座標をワールド座標に変換する際に、Cameraコンポーネントで定義されるScreenToWorldPointというメソッドが使われ、反対にワールド座標をスクリーン座標に変換する際は、WorldToScreenPointというメソッドが使われます。

そこで、このページでは、Unityでスクリーン座標をワールド座標に変換する方法と、反対にワールド座標をスクリーン座標に変換する方法までをまとめていきます。

この記事を書いた人

ゲーム作りを学び始めた一児のパパです。
このブログは、子供から「ゲームを作ってみたい!」と言われ、非プログラマーでゲーム作りをしたことない僕が、ゲーム作りの本を読んで独学でゲーム開発を学んでいるブログです。
同じように初めてゲーム作りをしている方と一緒に学んでいけるようなブログに出来たらいいなと思っています。
また、「このコードはおかしい」とか「もっと良い書き方があるよ!」などあれば、どんどん指摘して頂けると助かります。

スクリーン座標をワールド座標に変換する方法

まずは、スクリーン座標をワールド座標に変換する方法を紹介していきます。

ちなみに、スクリーン座標とは、実際にゲームとして表示されている画面の座標のことで、プレイヤーがクリックしたりタップしたりする場所を表していて、左下を原点(0, 0)とした平面座標となっています。

Unityエディター上でのゲームビューの画面における座標が、このスクリーン座標にあたります。

一方で、ワールド座標とは、オブジェクトが実際に配置されている場所の座標のことで、空間の真ん中を原点(0, 0, 0)として3D座標で表されます。

Unityエディター上で、それぞれのオブジェクトのインスペクターウィンドウにあるTransformののpositionの値が、このワールド空間における座標にあたります。

ScreenToWorldPointメソッドで変換処理を行う

スクリーン座標をワールド座標に変換する場合、その画面におけるCameraコンポーネントで定義されているScreenToWorldPointメソッドを使って変換することができます。

このScreenToWorldPointメソッドは、以下のように記述して使います。

Camera.ScreenToWorldPoint(position);

position:スクリーン座標(Vector3型)

ScreenToWorldPointの引数に、スクリーン座標をVector3型で指定することで、ワールド座標に変換されてVector3型で取得することができます。

例えば、スクリーン座標におけるマウスカーソルの位置をワールド座標に変換してみます。

using UnityEngine;

public class Test : MonoBehaviour
{
    Vector3 mousePos;   // マウスのスクリーン座標を取得する変数
    Vector3 screenPos;  // マウスをずらしたスクリーン座標
    Vector3 worldPos;   // 変換したワールド座標を入れる変数

    void Update()
    {
        if (mousePos != Input.mousePosition)
        {
            mousePos = Input.mousePosition; // マウスのスクリーン座標を取得
            screenPos = mousePos + new Vector3(0, 0, 10f);  // スクリーン座標をカメラからずらす処理
            worldPos = Camera.main.ScreenToWorldPoint(screenPos);   // スクリーン座標からワールド座標に変換する処理
            Debug.Log(worldPos);
        }
    }
}

マウスカーソルの位置を取得する場合は、13行目でInputクラスで定義されているmousePositionの変数を使うことで、スクリーン座標をVector3型で取得することができます。

そして、15行目でScreenToWorldPointを使って、ワールド座標に変換しています。

ちなみに、カメラオブジェクトには、今回「MainCamera」のタグを付けているので、「Camera.main」と記述しています。

ただし、スクリーン座標は平面なので、Z軸の値がカメラの位置で取得されてしまい、3Dゲームの場合、ScreenToWorldPointでそのまま変換すると、カメラの座標が取得されてしまうことになります。

そこで、14行目で取得したスクリーン座標のZ軸の値をカメラよりも奥にずらしておくことで、ワールド座標を取得できるようにしています。

これでゲームを実行してみると、

マウスカーソルの位置が移動するたびにワールド座標を取得出来ているのが分かります。

マウスを使ってゲームオブジェクトを配置する処理

実際に、Unity上でScreenToWorldPointメソッドを使った処理を作ってみます。

ここでは、マウスをクリックした位置に以下のキューブ型のオブジェクトを複製して配置していくという処理を作ります。

空オブジェクトを配置して、以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class Test : MonoBehaviour
{
    public GameObject cube; // キューブオブジェクトを取得する変数
    float cubePosZ; // 初期のZ座標を取得する変数
    Vector3 mousePos;
    Vector3 worldPos;

    void Start()
    {
        cubePosZ = cube.transform.position.z;   // キューブオブジェクトのZ座標の位置を取得
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            mousePos = Input.mousePosition + new Vector3(0, 0, 10f);    // マウスのスクリーン座標のZ軸をずらした処理
            worldPos = Camera.main.ScreenToWorldPoint(mousePos);        //  ワールド座標に変換する処理
            GameObject clone = Instantiate(cube);   // オブジェクトを複製する処理
            clone.transform.position = new Vector3(worldPos.x, worldPos.y, cubePosZ);   // 複製したオブジェクトの座標を指定する処理
        }
    }
}

5行目でオブジェクトを取得するための変数をpublicを付けて宣言しておき、インスペクターウィンドウから指定しておきます。

また、複製したオブジェクトのZ座標の値を、現在配置されているオブジェクトのZ座標と同じ値にしたいので、Start内の12行目で値を取得しています。

次に、マウスをクリックした場合の中の19行目で、マウスカーソルの位置のスクリーン座標を取得して、先ほどと同様にZ座標の値をずらしておきます。

そして、20行目でScreenToWorldPointメソッドを使って、ワールド座標に変換しています。

オブジェクトの複製は、21行目でInstantiateメソッドを使っていて、複製されたオブジェクトの座標を22行目で指定する処理を行っています。

これでゲームを実行してみると、

マウスをクリックした場所にオブジェクトが複製されるようになりました。

ワールド座標をスクリーン座標に変換する方法

次は、反対にワールド座標をスクリーン座標に変換する方法を紹介していきます。

WorldToScreenPointメソッドで変換処理を行う

ワールド座標をスクリーン座標に変換する場合、同じくCameraコンポーネントで定義されているWorldToScreenPointメソッドを使って変換することができます。

このWorldToScreenPointメソッドは、以下のように記述して使います。

Camera.WorldToScreenPoint(position);

position:ワールド座標(Vector3型)

WorldToScreenPointの引数に、ワールド座標をVector3型で指定することで、スクリーン座標に変換されてVector3型もしくはVector2型で取得することができます。

例えば、以下の配置されているオブジェクトのスクリーン座標を取得してみます。

オブジェクトに以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class Test : MonoBehaviour
{
    Vector2 screenPos;

    void Start()
    {
        screenPos = Camera.main.WorldToScreenPoint(transform.position); // スクリーン座標に変更する処理
        Debug.Log(screenPos);
    }
}

9行目でVector2型の変数に、WorldToScreenPointメソッドでワールド座標をスクリーン座標に変換した値を代入しています。

これでゲームを実行してみると、

スクリーン座標の値が取得できているのが分かります。

オブジェクトの近くにUIを表示させる処理

WorldToScreenPointメソッドを使う機会として、オブジェクトの座標を使ってUIの位置を決めるような場合に使います。

ここでは、UIで表示させているテキストの位置を、オブジェクトの移動に合わせて動かすという処理を作ってみます。

UIのテキストに以下のスクリプトをアタッチしておきます。

using UnityEngine;

public class Test : MonoBehaviour
{
    public GameObject cube; // キューブオブジェクトを取得

    Vector3 cubePos;
    Vector3 screenPos;
    Vector2 uiPos;

    RectTransform textRect;
    RectTransform parentRect;

    void Start()
    {
        textRect = GetComponent<RectTransform>();   // テキストのRectTransformコンポーネントを取得
        parentRect = textRect.parent.GetComponent<RectTransform>(); // 親のCanvasのRectTransformコンポーネントを取得
        MovePosition();
    }

    void Update()
    {
        if (cubePos != cube.transform.position)
        {
            MovePosition();
        }
    }

    void MovePosition()
    {
        cubePos = cube.transform.position;  // キューブオブジェクトのワールド座標を取得
        screenPos = Camera.main.WorldToScreenPoint(cubePos - new Vector3(0, 2f, 0));    // Y座標をずらしてスクリーン座標に変換処理
        RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, screenPos, Camera.main, out uiPos); // UI空間におけるローカル座標に変換
        textRect.localPosition = uiPos; // テキストの座標を変更する処理
    }
}

まず、自身のUI座標の位置を変更できるように、16行目でGetComponentメソッドRectTransformコンポーネントを取得しています。

そして、どのUI空間における座標を取得するかを決めるため、親となるCanvasのオブジェクトRectTransformコンポーネントも17行目で取得しています。

次に、MovePositionというメソッドを定義していて、その中の31行目でオブジェクトの座標位置を取得し、32行目でWorldToScreenPointメソッドを使い、オブジェクトの座標にY軸を減算した座標をスクリーン座標に変換する処理を行っています。

また、今回のようにUI要素の座標を指定する際は、スクリーン座標をさらにUIの中における座標(UIローカル座標)に変換する必要があり、この際に33行目でScreenPointToLocalPointInRectangleメソッドを使っています。

ScreenPointToLocalPointInRectangle(rect, screenPoint, cam, out localPoint);

rect:どのUIの中の座標に変換するかを指定(Recttransform型)
screenPoint:変換したいスクリーン座標(Vector2型)
cam:UIに関連するカメラ(Camera型)
localPoint:変換したUIローカル座標(Vector2型)

それぞれ上記のように引数を指定することで、最後の引数に設定しているuiPosの変数でUI空間における座標を取得できます。

そして、34行目でこの座標をテキストの座標に代入しています。

あとは、オブジェクトの移動処理を別で作りゲームを実行してみると、

上記のようにオブジェクトの移動に合わせて、テキストのUI要素も移動できているのが分かります。

今回はテキストで行いましたが、キャラクター毎のHPバーなども同様に移動に合わせて表示させることができます。

まとめ

このページでは、Unityでスクリーン座標からワールド座標への変換、またワールド座標からスクリーン座標への変換方法をまとめていきましたが、いかがでしたでしょうか?

スクリーン座標からワールド座標への変換は、ScreenToWorldPointメソッドを使うことで、ワールド座標に変換することができます。

反対に、ワールド座標からスクリーン座標への変換は、WorldToScreenPointメソッドを使うと、スクリーン座標に変換することができます。

マウスをクリックした場所や、オブジェクトが移動した先にUIを表示させたいといった場合に、それぞれの変換処理が使われることがあります。

最後までお読みいただきまして、ありがとうございました!

コメント