テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】【uGUI】Screenの座標とWorld(3D)座標の変換について

f:id:tsubaki_t1:20160301003627g:plain

HUDのように、UIをキャラクターの上に表示する方法について、少し悩んだのでメモします。

World SpaceとScreen Spaceについて

UIを考える場合、二つの座標系について考える必要があります。つまりWorld SpaceとScreen Spaceです。

World spaceは3D空間上の座標で、XYZで指定します。この座標は世界の中心点(x:0, y:0, z:0)を基準にした座標です。World座標の位置はカメラに依存せず、むしろカメラが3D空間内を映す感じです。

f:id:tsubaki_t1:20160301004051j:plain

Screen Spaceは、いわゆるUIの座標です。初期設定(アンカーが中央にある)だと、0,0は画面の中心です。World座標と異なり、位置はカメラや画面サイズに依存します。

f:id:tsubaki_t1:20160301004211j:plain

アンカー等についてはこちら

tsubakit1.hateblo.jp

World座標をUIの座標へ変換する

UIをキャラクター等の上に表示して追随するためには、キャラクターを配置しているWorld座標からUIの座標へ変換する必要があります。

この際、面倒な事にRender ModeがScreen Space OverlayかScreen Space CameraかScreen Space Worldかで計算式が異なります

World Space を Screen Space Overlayへ

f:id:tsubaki_t1:20160301005249j:plain

まずはWorld SpaceをScreen Space Overlayへ変化します。Screen Space Overlayはあまり変換などを考える必要が無く、RectTransformUtility.WorldToScreenPoint 一発で変換できます。

gist.github.com

なお、この際の「中心点」はPivotと一致します。つまり、Pivotを左下(0,0)に設定すると、左下を中心に対象を追跡します。これは他のRenderModeでも一緒です。

f:id:tsubaki_t1:20160301020839j:plain

f:id:tsubaki_t1:20160301020311j:plain

World Space を Screen Space Cameraへ

f:id:tsubaki_t1:20160301010409j:plain

Screen Space CameraはUIをカメラで映す状態です。このUIの位置やサイズはカメラに依存するので、少し処理が複雑になります。
まずScreenSpaceOverlayと同じようにWorld SpaceからScreen Spaceに変換し、さらにRectTransformUtility.ScreenPointToLocalPointInRectangleを使用してScreen Spaceを特定のUI用カメラから見た座標へ変換します。

f:id:tsubaki_t1:20160301010934j:plain

gist.github.com

World SpaceをWorld Spaceへ

f:id:tsubaki_t1:20160301011427j:plain

World Space(3D空間)をWorld Space(Canvas)へ…についてですが、変換自体はScreen Space Cameraと同じ仕組みで変換が可能です。
またWorld Space(Canvas)は3D空間上に自由に配置することができるので、いっそのこと対象のモデルの子オブジェクトとして置いてしまうのも面白いかもしれません。

f:id:tsubaki_t1:20160301011411j:plain

ScreenSpace Overlayを他の座標系へ変換する

Screen Space Overlayの座標系は、UIのイベントコールバック等で使用されます。このため、もしScreenSpaceCameraやScreen Space worldを使用していた場合変換が必要です。

変換についてはこの記事が詳しいです。

appleorbit.hatenablog.com

UIの座標をWorld Spaceに変換する

UIの座標を今度はWorld座標に変換してみます。ただしUIは2次元・3Dは3次元なので少しだけ考える必要があります。

まずアイディアとしてあるのが、RectTransformUtility.ScreenPointToRay を利用する方法です。UIのイベント発生地からカメラの方向へ向かってRayを設定してくれるので、あとはRaycastを設定すればドラッグした位置に玉を配置できちゃいます。

gist.github.com

f:id:tsubaki_t1:20160301014746g:plain

もう一つはRectTransformUtility.ScreenPointToWorldPointInRectangleを使用するアプローチです。こっちはScreenSpaceCameraやScreenSpace Worldで動作します。
この手法を用いた場合、球はScreenSpaceCameraのPlane Distanceの位置に配置されます。

f:id:tsubaki_t1:20160301015547g:plain

要するに、UIからWorld座標を取得する際、距離が固定長ならばScreenPointToWorldPointInRectangle、障害物に依存するならばScreenPointToRay を使うのが良さそうです。

関連

appleorbit.hatenablog.com

westhillapps.blog.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp