4 / 4 頭上表示と座標変換
3D空間での大きさを保つ方法と、画面上の大きさを保つ方法を比較します。
World Spaceの配置
このページは選択課題です。Lesson09をLesson09World.unityとして別名保存します。対象の移動だけを確認するため、空のGameObjectをTargetとして原点へ置きます。Main CameraをPosition (0, 0, -10)、Rotation (0, 0, 0)にします。
CanvasをTargetの子へ移し、Render ModeをWorld Space、Rect TransformのWidth 400、Height 100、Pivot (0.5, 0.5)、Local Position (0, 1, 0)、Local Rotation (0, 0, 0)、Scale (0.01, 0.01, 0.01)にします。GaugeBackgroundはCanvas中央のままです。
World Spaceでは、UIのローカルの幅にTransformのScaleを掛けた値が3D空間での幅になります。この例の幅300のゲージは3ユニットです。親TargetのScaleは(1, 1, 1)を維持します。
ScriptsフォルダーへFaceCamera.csを作り、Canvasへ追加してTarget CameraにMain Cameraを割り当てます。
using UnityEngine;
public class FaceCamera : MonoBehaviour
{
[SerializeField] private Camera targetCamera;
private void LateUpdate()
{
if (targetCamera != null)
transform.rotation = targetCamera.transform.rotation;
}
}Playし、TargetのPosition Zを0 → 10 → 0へ変えます。遠ざかると小さくなり、元へ戻すと同じ大きさに戻ります。カメラと回転をそろえる処理をビルボードと呼びます。確認後はPlayを終了して保存します。
固定サイズの頭上表示
こちらを選ぶ場合は元のLesson09をLesson09Overlay.unityとして別名保存し、CanvasをOverlayのまま使います。Canvasの直下にあるGaugeBackgroundのAnchor Min・MaxとPivotは中央(0.5, 0.5)、Scaleは(1, 1, 1)です。前のWorld Space用の設定は引き継ぎません。
Targetを原点へ作り、Main Cameraは前節と同じ位置・回転にします。空のGameObjectをUIFollowerとして作成し、次のWorldToCanvasFollower.csを追加します。非表示にするゲージとは別の、常に有効なGameObjectへ置くことで、画面へ戻ってきたときも更新できます。
using UnityEngine;
public class WorldToCanvasFollower : MonoBehaviour
{
[SerializeField] private Transform target;
[SerializeField] private Vector3 worldOffset = Vector3.up;
[SerializeField] private Camera worldCamera;
[SerializeField] private RectTransform canvasRect;
[SerializeField] private RectTransform uiRect;
private void LateUpdate()
{
if (uiRect == null)
return;
if (target == null || worldCamera == null || canvasRect == null)
{
uiRect.gameObject.SetActive(false);
return;
}
Vector3 position = target.position + worldOffset;
Vector3 viewport = worldCamera.WorldToViewportPoint(position);
bool visible = viewport.z >= worldCamera.nearClipPlane
&& viewport.z <= worldCamera.farClipPlane
&& viewport.x >= 0f && viewport.x <= 1f
&& viewport.y >= 0f && viewport.y <= 1f;
uiRect.gameObject.SetActive(visible);
if (!visible)
return;
Vector3 screenPoint = worldCamera.WorldToScreenPoint(position);
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvasRect, screenPoint, null, out Vector2 localPoint);
uiRect.anchoredPosition = localPoint;
}
}Target、World Camera、Canvas Rect、UI Rectへ、それぞれTarget、Main Camera、Canvas、GaugeBackgroundを割り当てます。ここではOverlayなので座標変換のカメラ引数はnullです。別のCanvasモードへそのまま流用しません。
PlayしてTargetのZを0 → 10 → -20 → 0へ変えます。距離が変わってもゲージの大きさは一定で、カメラの後ろでは消え、戻すと再表示されます。Xを100へ変えた画面外も確認し、最後は原点へ戻します。
画面上の位置へ変換しただけでは、手前の壁による遮蔽を判定できません。常に状態を見せる用途か、壁の後ろでは隠す用途かを別に決めます。この例の対象は位置・大きさ・画面外からの復帰です。
- 選んだ方式で、距離による大きさの変化を説明できる。
- 固定サイズ方式を選んだ場合、画面外・カメラ後方からの再表示を確認した。
- 基準の位置へ戻し、採用したSceneとPrefabを保存した。