【Unity】クリック(タップ)した座標にGameObjectを表示する方法

どうも、ゆみねこです。

クリック・タップした座標になにか表示したいこと、ありますよね。
タップした時のパーティクル(エフェクト)だとか、クリックした場所に建物を建設したりだとか……。

どうですか? 正しく表示できてますか?
あられもない場所に表示されたから、仕方なくScriptに直に数字を書き込んで修正したりして、「後でズレたらどうしよう……」とか思ったりしてませんか?

その悩み、ぼくが解決しよう。

この記事を読めば、もうクリックやタップの表示位置で悩むことはなくなります。
Scriptだけではなく、仕組みまで詳しく説明していくので、特にUnity初心者から抜け出したい人は必見!

クリック・タップした座標を取得する

実は、クリック・タップした座標の取得自体は、すごく簡単にできます。

Input.mousePosition

クリック・タップした座標は、Input.mousePositionで取得できます。

正確に言うと、クリックしたかどうかに関わらず、現在のマウスの座標を取得します。
クリックした時だけ呼び出せば、結果的にクリックした位置が分かるわけです。

//  クリック、あるいはタップした瞬間
if (Input.GetMouseButtonDown (0) || Input.GetTouch (0).phase == TouchPhase.Began) {
        //  クリック・タップした瞬間の座標
        var pos = Input.mousePosition;
}

スクリーン座標

ただし、これで取得できるのはスクリーン座標です。
このままScriptで代入しても、まず間違いなく位置がズレます。

スクリーン座標は、画面の座標を示すための特殊な座標です。
GameObjectを表示させるには、それぞれ別の座標に変換する必要があります。

ワールド座標の場合

クリックした場所に3Dモデルを配置したり、パーティクルを表示させたい時はこちらの方法を使います。

Script

以下のScriptを適当なGameObjectにアタッチすると、クリック・タップした座標にパーティクルを飛ばします。

以下、Scriptの解説です。

Camera.ScreenToWorldPoint

今回の目玉機能です。

スクリーン座標をワールド座標へ変換します。
Cameraクラスから呼び出せます。

Input.mousePositionで取得できるのはスクリーン座標なので、これを使ってワールド座標に変換すれば、ワールド空間に配置できます。

ただし、Input.mousePositionを直接渡すとうまくいかないので、ひと手間くわえてやる必要があります(下記参照)。

transform.forward

Transformの正面のベクトルです。
正確に言うと、transformのZ軸の単位ベクトルです。
UnityのSceneビューの青軸の方向を取得できます。

なぜこれが必要なのかというと、Input.mousePositionで取得できるスクリーン座標は、必ずZ座標が0になってしまうためです。
CameraのZ座標が毎回0とは限らないので、Z軸を補正してやる必要があります。

難しいことは考えずに、とりあえずCameraの正面に表示したいんだから必要なんだ、程度でも十分です。
Cameraの方向を基準にしたい場合に何かとご入用なベクトルなので、覚えておいて損はないですよ。

もちろん、Cameraに限らず、transformを持つGameObject(=UI以外)ならなんでも使えます。

補足 – パーティクルを用意する

ちなみに、即席でパーティクルを作るなら以下のように設定しましょう。

クリックした場所に、モヤが出現します。画像は不要です。

スポンサードサーチ

RectTransform(UI)の場合

クリックした場所に吹き出しを表示したい時などはこちら。

Script

まずは、Scriptから。

Canvas以下の適当なGameObjectにアタッチすると、 クリックした位置にUIのPrefabを生成します。
生成されたPrefabは、ScriptをアタッチしたGameObjectの子として登録されます。

以下、Scriptの解説です。
ちょっと難しいので、一度で理解できなくても落ち込まないでください。

RectTransformUtility.ScreenPointToLocalPointInRectangle

なにこれ舌噛みそう。
スクリーン座標を、あるRectTransformから見たローカル座標に変換します。
ややこしいですね。もう一度言います。

スクリーン座標(マウスの座標)を、あるRectTransformから見たローカル座標のRectTransformに変換します。

結局はスクリーン座標 → RectTransform なので、これ1つで変換自体は終わるのですが、指定する項目が多いのが混乱のもと。

ひとつずつ解説していきましょう。

第一引数:CanvasのRectTransformを指定する

第一引数に、CanvasのRectTransformを指定していますよね。

var canvasRect = canvas.GetComponent<RectTransform> ();
RectTransformUtility.ScreenPointToLocalPointInRectangle (canvasRect, ...);

これは、スクリーン座標を「どのRectTransformを基準に変換するのか」の指定です。

画面を向いたCanvasかもしれないし、DeadSpaceのように傾いたCanvasで指定したいかもしれないし、Canvas内の特定領域かもしれない。

ワールド空間に配置したCanvasを指定すれば、こんなことも

そのため、変換対象のRectTransformを指定する必要があります。
画面のクリックであれば、対象のCanvasを指定しておけば間違いないです。

第二引数:スクリーン座標

第二引数に、変換元となるスクリーン座標を指定します。
Input.mousePositionをそのまま渡せばOKです。

ここは特に説明はいらないですね。

第三引数:Camera

CanvasのRender Cameraを指定すればいいです。
Canvas.worldCameraで取得できます。

Canvas canvas;
canvas.worldCamera; // 上記画像の場合はMain Cameraが取得できる

……なのですが、CanvasのRender Modeによって指定方法が異なります。

Screen Space – Overlaynull
Screen Space – CameraCanvas.worldCamera
World SpaceCanvas.worldCamera

Overlayの場合はRender Cameraがないため、nullを指定しないとバグってしまいます。

//  ScreenSpace Overlayの場合はnullを代入しないとバグる
var camera = (canvas.renderMode == RenderMode.ScreenSpaceOverlay) ? null : canvas.worldCamera;

この記述は「Overlayならnullを、それ以外ならcanvas.worldCameraを代入」という処理です。

第四引数:計算結果(RectTransformのローカル座標)

最後の引数に「out」という見慣れない演算子がついています。

RectTransformUtility.ScreenPointToLocalPointInRectangle (canvasRect, Input.mousePosition, camera, out localpos);

これはUnityではなくC#の機能で、メソッド内から外へ値を渡したい時に使われます。

out pos と書けば、 ScreenPointToLocalPointInRectangle の計算結果がposに格納されます。

これは、 ScreenPointToLocalPointInRectangle を作った人がこういう仕様にしただけなので、深く考えず覚えちゃいましょう。

RectTransform.localPosition

RectTransformのローカル座標を取得します。

先ほども書いたように、 ScreenPointToLocalPointInRectangle は第一引数のRectTransformのローカル座標を取得するので、表示対象のRectTransformのローカル座標に代入する必要があります。

補足:角度とスケーリングの調整

Prefabを生成してから親を登録すると、ローカル座標がイカれてしまい、妙にデカくなったり回転してしまうことがあります。

そのため、念のため生成前のPrefabの角度とスケール値を代入し、表示崩れを防いでいます。

//  生成したPrefabの座標・角度・スケールを調整
var itemRect = item.GetComponent<RectTransform> ();
var prefabRect = uiPrefab.GetComponent<RectTransform> ();
itemRect.localPosition = localpos;
itemRect.localEulerAngles = prefabRect.localEulerAngles;
itemRect.localScale = prefabRect.localScale;

まとめ

RectTransformがやや複雑ですが、慣れれば呼吸をするかのごとく、クリックやタップした位置にGameObjectを表示できるようになります。
分からなくなったら何度でも読み返して、Unityマスターになっちゃおうぜ!

なお、Unityに限らず、個人で活動するゲーム開発者向けの情報を、Twitterで日々発信しています(質問にも答えています)。

記事の更新を見逃したくないという人は、ぜひぼくのTwitterをフォローしてもらえると嬉しいです!

記事をシェアする