【Unity】【uGUI】Screenの座標とWorld(3D)座標の変換について
HUDのように、UIをキャラクターの上に表示する方法について、少し悩んだのでメモします。
- World SpaceとScreen Spaceについて
- World座標をUIの座標へ変換する
- ScreenSpace Overlayを他の座標系へ変換する
- UIの座標をWorld Spaceに変換する
- 関連
World SpaceとScreen Spaceについて
UIを考える場合、二つの座標系について考える必要があります。つまりWorld SpaceとScreen Spaceです。
World spaceは3D空間上の座標で、XYZで指定します。この座標は世界の中心点(x:0, y:0, z:0)を基準にした座標です。World座標の位置はカメラに依存せず、むしろカメラが3D空間内を映す感じです。
Screen Spaceは、いわゆるUIの座標です。初期設定(アンカーが中央にある)だと、0,0は画面の中心です。World座標と異なり、位置はカメラや画面サイズに依存します。
アンカー等についてはこちら
World座標をUIの座標へ変換する
UIをキャラクター等の上に表示して追随するためには、キャラクターを配置しているWorld座標からUIの座標へ変換する必要があります。
この際、面倒な事にRender ModeがScreen Space OverlayかScreen Space CameraかScreen Space Worldかで計算式が異なります。
World Space を Screen Space Overlayへ
まずはWorld SpaceをScreen Space Overlayへ変化します。Screen Space Overlayはあまり変換などを考える必要が無く、RectTransformUtility.WorldToScreenPoint 一発で変換できます。
なお、この際の「中心点」はPivotと一致します。つまり、Pivotを左下(0,0)に設定すると、左下を中心に対象を追跡します。これは他のRenderModeでも一緒です。
World Space を Screen Space Cameraへ
Screen Space CameraはUIをカメラで映す状態です。このUIの位置やサイズはカメラに依存するので、少し処理が複雑になります。
まずScreenSpaceOverlayと同じようにWorld SpaceからScreen Spaceに変換し、さらにRectTransformUtility.ScreenPointToLocalPointInRectangleを使用してScreen Spaceを特定のUI用カメラから見た座標へ変換します。
World SpaceをWorld Spaceへ
World Space(3D空間)をWorld Space(Canvas)へ…についてですが、変換自体はScreen Space Cameraと同じ仕組みで変換が可能です。
またWorld Space(Canvas)は3D空間上に自由に配置することができるので、いっそのこと対象のモデルの子オブジェクトとして置いてしまうのも面白いかもしれません。
ScreenSpace Overlayを他の座標系へ変換する
Screen Space Overlayの座標系は、UIのイベントコールバック等で使用されます。このため、もしScreenSpaceCameraやScreen Space worldを使用していた場合変換が必要です。
変換についてはこの記事が詳しいです。
UIの座標をWorld Spaceに変換する
UIの座標を今度はWorld座標に変換してみます。ただしUIは2次元・3Dは3次元なので少しだけ考える必要があります。
まずアイディアとしてあるのが、RectTransformUtility.ScreenPointToRay を利用する方法です。UIのイベント発生地からカメラの方向へ向かってRayを設定してくれるので、あとはRaycastを設定すればドラッグした位置に玉を配置できちゃいます。
もう一つはRectTransformUtility.ScreenPointToWorldPointInRectangleを使用するアプローチです。こっちはScreenSpaceCameraやScreenSpace Worldで動作します。
この手法を用いた場合、球はScreenSpaceCameraのPlane Distanceの位置に配置されます。
要するに、UIからWorld座標を取得する際、距離が固定長ならばScreenPointToWorldPointInRectangle、障害物に依存するならばScreenPointToRay を使うのが良さそうです。