Unityを扱っている中で初心者が一番腹立たしく感じるのは狙った場所に狙ったオブジェクトが表示できない時や何も表示されない時ではないでしょうか?カメラ、3Dオブジェクト、UIの関係と座標について理解して、狙ったところに狙ったものを表示させたいものです。
UIと3Dオブジェクトの共存については以下の記事でまとめています。
今回は3DオブジェクトとUIの座標系について説明したいと思います。
座標の種類
まず大きく分類して座標には以下の2つが存在します。
- ワールド座標
- ローカル座標
世界(ワールド)を中心とするか、親の世界(ローカル)を中心とするかの2種類です。親子とはヒエラルキーWindowで上位にいるのが親で下位にいるのが子です。ローカルという考え方は座標だけでなく、スケール、ローテーションというのもすべて親基準で考えられることになります。ピンとこないかと思いますので、以下に例を示してみます。
ワールド座標
最初、ChildCubeとParentCubeは親子関係になく、それぞれのオブジェクトはヒエラルキーでは最上位に配置されていることとします。よって、ワールド座標=ローカル座標で、右に表示されているのもワールド座標となっています。
親子関係なし |
Position | Scale | ||||
X | Y | Z | X | Y | Z | |
ParentCube | 0 | 200 | 0 | 200 | 200 | 200 |
ChildCube | 0 | 0 | 0 | 100 | 100 | 100 |
ローカル座標
ここで、ChildCubeをParentCubeの子にしてみます。どうでしょう、個人的にはかなり想定外の値になりました。直感的にわかりにくいのはここかと思います。
親子関係あり |
Position | Scale | ||||
X | Y | Z | X | Y | Z | |
ParentCube | 0 | 200 | 0 | 200 | 200 | 200 |
ChildCube | 0 | -1 | 0 | 0.5 | 0.5 | 0.5 |
まず、ChildCubeのY座標が-1になっています。なぜ-1?と思われるかと思います。これはParentCube(親)のScale 単位に座標の単位が変換された結果となります。
親のYのScaleは200なので、ワールド座標200がローカル座標1と等しくなります。また親が基準なので、ChildCubeはY方向マイナスの位置にいるので座標は負になります。結果-200/200=-1でY座標は-1になるのです。
同様に、ChildCubeのScaleもParentCubeのScale単位になっています。親のScaleの半分なので0.5になっているのです。
このように子に配置することで、数値的には非常にわかりづらく複雑になったような気分になります。ではどのように活用するものなのでしょうか?それは親の移動、拡大縮小、回転をした時、その配置を維持したまま子も変化してくれるというのが一番最初に浮かぶ利点かと思います。
先ほどの例を2倍に拡大して、45度回転してみました。
親子の位置を維持したまま、拡大、回転できているのがわかるかと思います。回転も親を中心に回っています。これは親子でなければそれぞれの位置を計算するのは非常に大変ですが、親子にしておくことで、ワールド座標を計算する必要がなく、親のパラメータを変更するだけで容易に変換することができます。
UIの座標世界
UIもワールド座標やローカル座標を持っています。ただ、それらの座標を意識してUIを配置することはありません。UIはRect Transformという別の座標系を利用して配置していきます。なぜでしょうか?その理由含めて以下で解説していきます。
UIを描くCanvasの配置
まず仮に、UIをワールド座標で表現しようとすると、Canvasは常に最適なサイズでカメラに収まっている必要があります。表示する画面の解像度やアスペクト比でCanvasをズームしたり、引いたりして調整する必要も出てくるでしょう。これを全てのデバイスを意識して実装するのは非常に大変です。
これを防ぐために、UIのCanvasの幅と高さはデバイスの解像度やアスペクト比に依存するようにして、それにカメラが合わせるという仕組みがあります。カメラは常にデバイスに依存せず、ぴったりのサイズでUIを全画面で表示できるのです。
UIは常に全画面なので、カメラの映し出す範囲を変更してもサイズが追従してくれます。カメラのSizeパラメータを変更すると、UIは常に全画面ですが、その間にあるオブジェクトの大きさが変化します。
Rect Transformの概念
Rect Transformはワールド座標やローカル座標を意識することなく、Canvasの座標でUIを配置することのできる仕組みです。カメラの動きや位置に依存しないので、ゲームとは切り離してUI設計することができます。UIと3Dオブジェクトを共存させる方法についてはこちらで詳しく解説しています。
もちろん、Rect Transformで配置されるUIもワールド座標を持っていないわけではありません。試しにキャンバスの上に配置したボタンオブジェクトの位置を以下のスクリプトで表示させてみます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonScr : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
// Rect Transform Objectの取得
RectTransform rectTransform = GetComponent<RectTransform>();
// ワールド座標の出力
Debug.Log(rectTransform.position);
}
// Update()は省略
}
以下のように座標 (0,350,131.9)が取得されました(ログ参照)。ボタンはセンターなのでX=0、カメラのSizeが350、ボタンのpivotが0.5、1なので、Yは350、ZはCanvasのZ座標から計算して正しい座標となっている(詳細はここでは省略)ことを確認しました。
まとめ
座標といっても様々な座標があることがわかりました。簡単にまとめると以下のようになるかと思います。
3Dオブジェクト | 2Dオブジェクト | |
World座標 | ヒエラルキー最上位に配置されたオブジェクトの座標系 |
意識しなくてよい |
Local座標 | 親オブジェクトを基準とした座標系子オブジェクトに適用 | 意識しなくてよい 参照、変更も可能 Rect Transformで配置、調整 |
この辺を理解してから、オブジェクトが表示されないとか、変な位置に配置されるとかに悩まされることが格段に減りました。理解するまではUIに3Dオブジェクトが隠れたり、UIが出なかったりと悪戦苦闘していました。座標系はまだまだ勉強することが多そうなので、またある程度まとまったら記事にしようと思います。
コメント