【Unity】画面の位置(スクリーン座標)をワールド座標に変換する

この記事では Unity の C# スクリプトを使い、スクリーン座標からワールド座標を求める方法について説明します。

使用した Unity のバージョンは 2020.1.1f1(64-bit)
プラットフォームは Windows 10(64ビット) です。


以前、別のブログに掲載していた記事を手直ししたものです。
当時は Unity 4 でしたが、この部分は全く変わっていません。
たぶん、バージョンに関係なく使えます。

変換方法

ScreenToWorldPoint() を使います。

使用例
// マウスのポインタがあるスクリーン座標を取得
Vector3 screen_point = Input.mousePosition;

// z に 1 を入れないと正しく変換できない
screen_point.z = 1.0f;

// スクリーン座標をワールド座標に変換
Vector3 world_position = Camera.main.ScreenToWorldPoint(screen_point);
注意が必要なポイント

スクリーン座標は2次元なので、3次元の Vector3 に突っ込んだ時、z は初期値の 0 になります。
0 で困るときは、0 以外の値を入れる必要があります。

スクリーン座標をワールド座標に変換する際、スクリーン座標の z に 1 を入れないと、正しく変換できません。
うっかり、z を初期値(0)のまま ScreenToWorldPoint() に渡してしまうと、おかしなワールド座標に変換されてしまいます。

補足

Input.mousePosition は、古い Input システムでのマウスカーソル位置を取得する方法ですが、(2020/10/31現在)まだ使えます。
新しい Input システムでは、Mouse.current.position.ReadValue() を使います。
新しい Input システムを使うには、パッケージマネージャーを使ってインストールした上で、あれこれセットアップが必要です。
インストールとセットアップ方法については、下記ブログ記事参照。

何故この記事を書いたのか?

Camera.main.ScreenToWorldPoint() を初めて使ったとき、うまく変換できず、その理由に気づくまで何時間も格闘しました。
私と同じ失敗で貴重な時間を浪費する方が少しでも減りますよう、祈りを込めて、ここに記録しておきます。

応用例

応用すると、こんなことができます。

クリックした位置にキューブを移動させる…ということをやっていますが、クリックした位置(スクリーン座標)をキューブを移動させる位置(ワールド座標)に変換する必要があり、そこで使っています。

ソースコード

using UnityEngine;

public class CubeScript : MonoBehaviour
{
	public float length_per_second = 2.0f;        //移動スピード

	Vector3 destination;    //目的地の座標
	Vector3 start_point;    //移動開始時の座標
	float start_time;        //移動開始時の時間
	float journey_length;	//移動距離

	bool is_moving = false;

	void Update()
	{
		if (Input.GetMouseButtonDown(0))
		{
			this.start_point = this.transform.position;

			Vector3 screen_point = Input.mousePosition;
			screen_point.z = 1.0f;
			this.destination = Camera.main.ScreenToWorldPoint(screen_point);

			this.start_time = Time.time;
			this.journey_length = Vector3.Distance(this.start_point, this.destination);

			this.is_moving = true;
		}

		if (this.is_moving)
		{
			float length_by_elapsed_time = (Time.time - this.start_time) * this.length_per_second;
			float fraction_length_by_elapsed_time = length_by_elapsed_time / this.journey_length;

			Vector3 lerp = Vector3.Lerp(this.start_point, this.destination, fraction_length_by_elapsed_time);
			this.transform.position = lerp;

			if (Vector3.Distance(this.transform.position, this.destination) < 0.1f)
			{
				//	目的地までの距離が 0.1 未満になったら移動完了
				this.is_moving = false;
			}
		}
	}
}

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です