【Unity】オブジェクトの頭上に情報を表示する(基本)
様々な方法がありますが、今回は、UI(TMPro)オブジェクトにスクリプトをアタッチすることで、3Dオブジェクトの頭上に情報を表示する基本的な方法について解説します。
考え方
3DオブジェクトのプレイヤーとUIオブジェクトは異なる座標系を使用しているため、位置を適切に変換してUIオブジェクトをプレイヤーの頭上に表示する必要があります。これにより、UIオブジェクトが常にプレイヤーの頭上に表示されるように見せることができます。

Unityで混乱しやすい3つの座標——ワールド座標、スクリーン座標、UI(RectTransform)のローカル座標——を、定義→原点/単位→相互変換→落とし穴 の順で整理します。
1) 座標の定義と原点・単位
ワールド座標 (World space)
- 3Dシーン全体を表す座標空間。Transform.position がこの座標。
- 原点:シーンの(0,0,0)。単位:メートル相当(ゲーム的な任意単位)。
- カメラに依存しない。
- 記事でも、3D空間の座標として説明されています。
スクリーン座標 (Screen space / pixel)
- 画面上のピクセル座標。(x, y) は左下が(0,0)、右上が(画面幅, 画面高)。
- 原点:画面左下。単位:ピクセル。
- Camera.WorldToScreenPoint(worldPos) で取得。記事でもこの変換が示されています。
UIのローカル座標 (RectTransform local space)
- ある RectTransform(多くはCanvas直下のパネルなど)を基準にした2D座標。
- 原点:そのRectTransformのピボット(pivot)位置。※アンカーではありません(重要)。記事でも「原点=Pivot、Anchorとは別」と明記。
- 単位:ピクセル(Canvas Scalerの設定でスケール影響)。
- スクリーン座標からは RectTransformUtility.ScreenPointToLocalPointInRectangle で変換。記事のサンプルと解説どおりです。
2) 代表的な変換レシピ
A. ワールド → スクリーン → UIローカル(Canvas基準)
オブジェクト頭上にUIを出す王道手順(記事の手順と同じ)。
// 1) World → Screen
Vector3 screen = Camera.main.WorldToScreenPoint(worldPos);
// 2) Screen → UI Local (parentRectTransform基準)
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentRectTransform,
screen,
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
out Vector2 local
);
// 3) 目的のUI要素のlocalPosition/anchoredPositionに反映
uiRect.localPosition = local + new Vector2(0f, 50f); // 例: 50px 上にオフセット
- ScreenPointToLocalPointInRectangle を使う点・カメラ引数の扱いは記事と同一です。
- カメラ引数:
- Screen Space – Overlay のCanvas → null
- Screen Space – Camera / World Space のCanvas → canvas.worldCamera を渡す
B. ワールド → ビューポート(0–1) → スクリーン/UI
ビューポートは「画面内の正規化座標」。端末解像度に依存しない配置計算に便利。
Vector3 vp = Camera.main.WorldToViewportPoint(worldPos); // (0..1, 0..1, depth)
Vector2 screen = new Vector2(
vp.x * Screen.width,
vp.y * Screen.height
);
// あとは A と同じ
C. スクリーン(マウス/タッチ)→ UIローカル
クリック地点などをUI基準に取りたいケース。
Vector2 local;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentRectTransform,
Input.mousePosition,
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
out local
);
D. 逆変換(UIローカル → スクリーン → ワールド)
UIの位置に最も近いワールド点を得たい場合など。
// 1) UI Local → Screen
Vector2 screen = RectTransformUtility.WorldToScreenPoint(
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
uiRect.position // ※RectTransformの「ワールド座標」を使う
);
// 2) Screen → World(デプスはカメラからの距離で調整)
Vector3 world = Camera.main.ScreenToWorldPoint(new Vector3(screen.x, screen.y, depthFromCamera));
3) UIローカル座標で混乱しやすい点(重要)
- 原点はPivot:RectTransform の pivot(0〜1)でローカル原点が決まる。Anchorは原点に影響しない。記事でも強調あり。
- localPosition と anchoredPosition
- localPosition:親のTransformに対する位置(3D Transform寄りの概念)
- anchoredPosition:アンカー矩形からの2Dオフセット(UIレイアウト向け)
- 「Canvas直下の1枚板の中でラベルをちょい動かす」等は anchoredPosition が直感的。
- CanvasのRender Modeによりカメラ引数が変わる(前述)。
- Canvas Scaler(Scale With Screen Size 等)で「見た目ピクセル↔計算ピクセル」がズレることがある。UI単位は参照解像度基準にスケーリングされる点を意識。
- 複数カメラ:WorldToScreenPoint に使うカメラと、Canvasに設定した worldCamera の整合を取る。
- UIが画面外に出る:WorldToViewportPoint の z < 0(背面)や (x,y) が0〜1外なら、非表示・クランプ等の分岐を入れる。
4) 実用スニペット:安全に「頭上ラベル」を追従
using UnityEngine;
using UnityEngine.UI;
public class WorldLabelFollower : MonoBehaviour
{
[SerializeField] Transform target; // 追従対象(ワールド)
[SerializeField] RectTransform parentRect; // UIの基準(Canvas配下のPanelなど)
[SerializeField] RectTransform labelRect; // 追従させるUI
[SerializeField] Canvas canvas; // 対象Canvas
[SerializeField] Vector2 pixelOffset = new(0, 50);// 頭上オフセット
[SerializeField] float hideBehindCameraZ = 0.1f; // 背面時の非表示閾値
void LateUpdate()
{
if (!target) return;
Vector3 sp = Camera.main.WorldToScreenPoint(target.position);
// 背面(z<0)や極端に近い/遠い時のガード
if (sp.z < hideBehindCameraZ)
{
if (labelRect.gameObject.activeSelf) labelRect.gameObject.SetActive(false);
return;
}
if (!labelRect.gameObject.activeSelf) labelRect.gameObject.SetActive(true);
// Screen → UI Local
RectTransformUtility.ScreenPointToLocalPointInRectangle(
parentRect,
new Vector2(sp.x, sp.y) + pixelOffset,
canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera,
out Vector2 local
);
// UI座標を反映(pivot基準)
labelRect.anchoredPosition = local;
}
}
- 変換の骨子は記事のサンプルと同じで、非表示ガードや anchoredPosition を使う実務寄り版です。
5) この記事との対応ポイント(抜粋)
- World→Screen→UI Local の流れ(WorldToScreenPoint と RectTransformUtility.ScreenPointToLocalPointInRectangle)を丁寧に解説。
- ローカル原点はPivot、Anchorは原点に影響しない点の注意書き。
- 実コード例:parentRectTransform と playerTransform をSerializeFieldで受けてUpdateで追従。
ワールド座標・スクリーン座標・UIローカル座標に加えて、Unityには ビューポート座標 (Viewport space) も存在します。これは カメラで映している範囲を正規化した相対座標系 です。
- 範囲:画面左下が (0,0)、右上が (1,1)
- 単位:0~1 の割合(解像度やピクセル数に依存しない)
- z値:カメラからの距離(ワールド単位)
変換例
// ワールド座標 → ビューポート座標
Vector3 vp = Camera.main.WorldToViewportPoint(worldPos);
// ビューポート座標 → ワールド座標
Vector3 world = Camera.main.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 10f));
利用シーン
- 解像度に依存しない配置例:画面中央にオブジェクトを置く → (0.5, 0.5)
- 画面内外の判定
bool isVisible = vp.z > 0 && vp.x >= 0 && vp.x <= 1 && vp.y >= 0 && vp.y <= 1;
- クロスプラットフォーム対応画面比率や解像度が異なっても相対的な位置で処理できる
こうして「補足」としてまとめることで、記事の主題である「頭上にUIを出す実装」に直接必要な知識を説明しつつ、読者がさらに発展的に使えるViewport座標の存在も押さえられる構成になります。
サンプルシーン構成
以下の構成でプロジェクトを作成します。
- プロジェクト名:LifeGaugeSample
- プレイヤーオブジェクト:プレイヤーのモデルやキャラクター
- ライフゲージオブジェクト:プレイヤーの頭上に常に表示されるUIオブジェクト(例:テキストや画像)
プレイヤーオブジェクト
プレイヤーオブジェクトは、シーン内で動くキャラクターやモデルです。このオブジェクトの頭上にUIが常に表示されるように設定します。

ライフゲージオブジェクト
ライフゲージオブジェクトには、テキストUI(TMPro)を使用します。このオブジェクトをUIのローカル座標系で移動させ、常にプレイヤーの頭上に留まるようにします。テキスト以外のUI要素(画像やボタンなど)でも同様の手法が適用できます。

スクリプト
ライフゲージオブジェクトにアタッチするスクリプトを作成します。このスクリプトでは、以下の2つのアウトレットをインスペクターから接続します。
- UIテキストの親オブジェクト(Canvas)
- 追従するプレイヤーオブジェクト(Cubeなど)
注意点:
- それぞれの型は
GameObject
型ではなく、具体的なコンポーネント型(RectTransform
やTransform
)である必要があります。これにより、ゲームオブジェクトにアタッチされているコンポーネントのインスタンスを取得できます。
以下は、スクリプトのサンプルです。
using UnityEngine;
// プレイヤーのライフゲージを表示するクラス
public class LifeGauge : MonoBehaviour
{
// 親のRectTransformを指定します。
[SerializeField]
private RectTransform parentRectTransform;
// プレイヤーのTransformを指定します。
[SerializeField]
private Transform playerTransform;
// フレームごとに呼び出されるUpdateメソッド
void Update()
{
// プレイヤーの位置をスクリーン座標に変換します。
Vector3 screenPoint = Camera.main.WorldToScreenPoint(playerTransform.position);
// スクリーン座標をローカル座標に変換します。
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRectTransform, screenPoint, null, out Vector2 localPoint);
// ゲージの位置をプレイヤーの頭上に設定します(Y座標を50ピクセル上に設定)。
transform.localPosition = localPoint + new Vector2(0, 50);
}
}
コード解説
RectTransform の役割
RectTransform
は、UnityのUIシステムで使用されるコンポーネントで、UI要素(テキスト、画像、ボタンなど)の位置、サイズ、回転、スケールを制御します。ライフゲージの親オブジェクト(Canvas)のRectTransform
を参照することで、UI内での正確な位置調整が可能です。
[SerializeField]
private RectTransform parentRectTransform;

parentRectTransform
はプライベートな変数で、[SerializeField]
属性によりインスペクターから設定可能です。この変数はライフゲージを配置する親のRectTransform
を参照します。
Transform の役割
Transform
コンポーネントは、ゲームオブジェクトの位置、回転、スケールなどの変換情報を管理します。プレイヤーの位置情報を取得するために使用します。
[SerializeField]
private Transform playerTransform;
playerTransform
もプライベートな変数で、[SerializeField]
属性によりインスペクターから設定可能です。この変数は追従するプレイヤーオブジェクトのTransform
を参照します。
ワールド座標からスクリーン座標への変換
プレイヤーの位置を3D空間のワールド座標から2Dスクリーン座標に変換します。これにより、UI上で正確な位置に表示することが可能です。
Vector3 screenPoint = Camera.main.WorldToScreenPoint(playerTransform.position);
Camera.main
を使用して、プレイヤーの位置をスクリーン座標に変換し、その結果をscreenPoint
に格納します。
ワールド座標
3D空間を指す座標

スクリーン座標
UI(Canvas)での2次元座標
左下が原点とした位置を示します

プレイヤーとスクリーン座標

Camera.mainを使用して、playerTransformの位置をワールド座標からスクリーン座標に変換し、その座標をscreenPointに格納します。これにより、キャラクターの位置情報を画面上の座標に変換できます。
スクリーン座標から親(Canvas)の子オブジェクト(Text(TMP))としてのローカル座標系に変換
ライフゲージのText(TMP)が子オブジェクトなので、続いてローカル座標系に変換します
screenPointを parentRectTransform のローカル座標系に変換します。結果は localPoint に格納されます。これにより、親RectTransform内での位置を取得できます。
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRectTransform, screenPoint, null, out Vector2 localPoint);
RectTransformUtility.ScreenPointToLocalPointInRectangle メソッドは、UnityのUIシステムで使用される、スクリーン座標から指定された親要素のローカル座標系への変換を行うための便利なメソッドです。このメソッドはUI要素の操作やイベント処理の際に非常に役立ちます。
以下に、このメソッドの各引数と役割を説明します:
- parentRectTransform: これは、ローカル座標系に変換するための親要素の RectTransform コンポーネントを指定するパラメータです。スクリーン座標からローカル座標に変換する際、この親要素の座標系を基準として変換が行われます。
- screenPoint: screenPoint は、スクリーン上での座標を指定するパラメータです。通常、マウスの位置やタッチイベントの位置など、デバイスの画面上での座標を指定します。
canvas
(null): canvas パラメータは、特定の Canvas グループに関連する場合に指定されます。通常、これをnullに設定し、screenPoint と parentRectTransform の間の変換を行います。ただし、特定の Canvas グループに関連付けられている場合、それを指定できます。- localPoint: localPoint パラメータは、メソッドの出力として使用されます。メソッドが成功した場合、
localPoint
に変換されたローカル座標が格納されます。この座標は、指定された parentRectTransform 内での位置を示します。
このメソッドは、スクリーン上の座標(screenPoint)を、指定した親要素のローカル座標系に変換するのに使用されます。ローカル座標系の原点はCanvasの中心地点です。例えば、マウスカーソルの位置を取得し、それをUI要素のローカル座標系内で使用したい場合に便利です。このメソッドを使用することで、UI要素の位置に関する計算を簡略化し、正確な位置情報を得ることができます。

アンカーを中心とした時の座標系のイメージがローカル座標系のイメージになります
実際は、アンカーの位置を変えてもローカル座標系の原点には反映されません
UnityでUI要素(例えば、ライフゲージ)を3Dオブジェクト(プレイヤー)の頭上に表示する際、RectTransformUtility.ScreenPointToLocalPointInRectangle
メソッドを使用してスクリーン座標をUIのローカル座標系に変換します。このときのローカル座標系の中心位置(原点)がどこに設定されているかは、UIの配置や設定において重要なポイントです。
ローカル座標系の原点とは?
ローカル座標系の原点は、特定のRectTransform
コンポーネントにおける基準点のことを指します。この原点は、主に以下の2つのプロパティによって決定されます。
- アンカー(Anchor)
- ピボット(Pivot)
ピボット(Pivot)の役割
RectTransform
のローカル座標系における原点は、ピボットポイントによって決定されます。ピボットは、UI要素の回転やスケーリングの基準点として機能するだけでなく、ローカル座標系の原点としても使用されます。
- デフォルト設定: 通常、
RectTransform
のピボットは(0.5, 0.5)
に設定されており、これはUI要素の中心を意味します。 - カスタマイズ: ピボットを変更することで、ローカル座標系の原点をUI要素の異なる位置(例えば、左下や右上)に移動させることができます。
この場合の原点位置
今回のスクリプトおよび設定において、以下のように考えることができます。
- 親
RectTransform
(Canvas)のピボット:- 通常、Canvasの
RectTransform
のピボットは(0.5, 0.5)
であり、Canvasの中心がローカル座標系の原点となります。 - これにより、
RectTransformUtility.ScreenPointToLocalPointInRectangle
メソッドで変換されたlocalPoint
は、Canvasの中心を基準とした相対位置となります。
- 通常、Canvasの
- 子
RectTransform
(ライフゲージ)のローカル座標:- ライフゲージの
RectTransform
のピボットも通常は(0.5, 0.5)
です。 - スクリプト内で設定される
transform.localPosition
は、親Canvasのローカル座標系における位置であり、ピボットを基準とした相対位置になります。
- ライフゲージの
図解による説明
以下に、ローカル座標系の原点位置を視覚的に説明します。
Canvas(親 RectTransform)
+---------------------------+
| |
| (0,0) | ← ピボット(中心)がローカル座標系の原点
| + |
| |
| ライフゲージ |
| |
+---------------------------+
- Canvasの中心が
(0,0)
の原点となります。 RectTransformUtility.ScreenPointToLocalPointInRectangle
によって取得されたlocalPoint
は、この原点を基準に計算されます。- スクリプトでは、
localPoint
にオフセット(例えば、new Vector2(0, 50)
)を加えることで、ライフゲージがプレイヤーの頭上に適切に配置されます。
ピボットの変更がローカル座標系に与える影響
ピボットを変更すると、ローカル座標系の原点も移動します。例えば、ピボットを左下に設定すると、ローカル座標系の原点はCanvasの左下に移動します。この設定により、localPoint
の基準が変わるため、ライフゲージの表示位置も相対的に変動します。
例: ピボットを左下に設定
Canvas(親 RectTransform)
+---------------------------+
| |
| (0, Canvas.height) | ← ピボットが左下に移動
| + |
| |
| ライフゲージ |
| |
+---------------------------+
この場合、localPoint
は左下を基準として計算されるため、スクリプト内でのオフセット調整もそれに合わせて行う必要があります。
まとめ
- ローカル座標系の原点は、
RectTransform
のピボットポイントに基づいて決定されます。 - 通常、Canvasのピボットは中央に設定されており、これがローカル座標系の原点となります。
- ピボットを変更することで、ローカル座標系の原点も変更され、UI要素の配置やスクリプト内での位置調整に影響を与えます。
- スクリプトを使用してUI要素をプレイヤーの頭上に表示する際は、親Canvasのピボット設定を確認し、それに基づいて位置調整を行うことが重要です。
これにより、UI要素が期待通りに表示され、プレイヤーの動きに合わせて正確に追従するようになります。
ライフゲージの位置設定
取得したローカル座標にオフセットを加算して、ライフゲージをプレイヤーの頭上に表示します。
transform.localPosition = localPoint + new Vector2(0, 50);
ここでは、Y軸方向に50ピクセルのオフセットを加えています。この値を調整することで、ライフゲージの表示位置を微調整できます。
このスクリプトは、ゲームオブジェクトにアタッチされた際に、指定された親RectTransform内でキャラクターの位置に合わせてライフゲージを表示するのに役立ちます。
ここでのコードでは以下の行が使用されています:
transform.localPosition = localPoint + new Vector2(0, 50);
これに対して、「なぜ RectTransform.localPosition
ではなく transform.localPosition
を使用しているのか」ですが、具体的には、このスクリプトがアタッチされているオブジェクトがCanvas下のTextMeshPro子オブジェクトであることを踏まえています。
1. Transform
と RectTransform
の関係
まず、Transform
と RectTransform
の関係を明確にしましょう。
Transform
:- Unityのすべてのゲームオブジェクトは
Transform
コンポーネントを持っています。 - オブジェクトの位置、回転、スケールを管理します。
- Unityのすべてのゲームオブジェクトは
RectTransform
:Transform
のサブクラスであり、主にUI要素(Canvasの子オブジェクトなど)に使用されます。- UIのレイアウトやアンカー、ピボットなどの追加機能を提供します。
重要な点は、RectTransform
は Transform
を継承しているということです。つまり、RectTransform
は Transform
のすべてのプロパティとメソッドを持ちながら、さらにUI特有の機能を追加しています。
2. なぜ transform.localPosition
で問題ないのか
RectTransform
が Transform
を継承しているため、transform.localPosition
を使用しても機能的には問題ありません。具体的には以下のようになります。
// RectTransformがTransformを継承しているため、localPositionは使用可能
transform.localPosition = localPoint + new Vector2(0, 50);
この場合、transform
は実際には RectTransform
コンポーネントへの参照となります。したがって、localPosition
を設定することで、UI要素の位置を変更することができます。
3. RectTransform
を明示的に使用する利点
技術的には transform.localPosition
でも動作しますが、コードの可読性と意図の明確化の観点から、RectTransform
を明示的に使用することが推奨されます。以下にその理由を示します。
- 可読性の向上:
- 他の開発者がコードを読んだ際に、このオブジェクトがUI要素であることが明確になります。
- UI特有のプロパティへのアクセス:
RectTransform
にはanchoredPosition
やsizeDelta
など、UIレイアウトに特化したプロパティが存在します。これらを使用する場合、RectTransform
を明示的に扱う方が自然です。
- 一貫性:
- プロジェクト全体で
RectTransform
を明示的に使用することで、一貫したコードスタイルを維持できます。
- プロジェクト全体で
具体例:
以下のように RectTransform
をキャッシュして使用する方法が考えられます。
using UnityEngine;
public class LifeGauge : MonoBehaviour
{
[SerializeField]
RectTransform parentRectTransform;
[SerializeField]
Transform playerTransform;
// RectTransformをキャッシュする
RectTransform rectTransform;
void Awake()
{
rectTransform = GetComponent<RectTransform>();
}
void Update()
{
Vector3 screenPoint = Camera.main.WorldToScreenPoint(playerTransform.position);
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRectTransform, screenPoint, null, out Vector2 localPoint);
Debug.Log("screenPoint: " + screenPoint);
Debug.Log("localPoint: " + localPoint);
// RectTransformを使用
rectTransform.localPosition = localPoint + new Vector2(0, 50);
}
}
4. anchoredPosition
の使用を検討する
UI要素の位置を設定する際には、localPosition
よりも anchoredPosition
を使用する方が適切な場合があります。anchoredPosition
はアンカーに基づいた位置を設定するため、レイアウトの柔軟性が向上します。
rectTransform.anchoredPosition = localPoint + new Vector2(0, 50);
まとめ
transform.localPosition
は技術的に問題なく使用可能:RectTransform
はTransform
を継承しているため、transform.localPosition
を使用しても機能します。
RectTransform
を明示的に使用することを推奨:- コードの可読性と意図の明確化のために、
RectTransform
を使用する方が望ましいです。 - UI特有のプロパティ(例:
anchoredPosition
)を使用する際にも便利です。
- コードの可読性と意図の明確化のために、
- 実装例の改善:
RectTransform
をキャッシュし、anchoredPosition
を使用することで、より柔軟で明確なコードになります。
元のコードで transform.localPosition
を使用している理由は、おそらく単純に Transform
がベースクラスであり、機能的には問題がないためです。しかし、将来的な拡張性や可読性を考慮すると、RectTransform
を明示的に使用する方がベストプラクティスといえます。
発展(インスペクターで頭上位置の調整ができるようにする)
次のようにリファクタリングすることで、プレイヤーと表示されるUIとの位置関係の調整をインスペクターから調整できるようになります
using UnityEngine;
public class LifeGauge : MonoBehaviour
{
[SerializeField] private RectTransform parentRectTransform;
[SerializeField] private Transform playerTransform;
[SerializeField] private Vector2 offset = new Vector2(0, 50);
private void Update()
{
UpdateLifeGaugePosition();
}
private void UpdateLifeGaugePosition()
{
Vector3 screenPoint = Camera.main.WorldToScreenPoint(playerTransform.position);
RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRectTransform, screenPoint, null, out Vector2 localPoint);
transform.localPosition = localPoint + offset;
}
}
変更点の説明
- オフセットフィールドの導入
[SerializeField] private Vector2 offset = new Vector2(0, 50);
offset
フィールドを追加し、UIの表示位置をインスペクターから調整できるようにしました。これにより、表示位置の微調整が容易になります。
- 位置更新処理の分離
private void UpdateLifeGaugePosition()
{
// 位置更新の処理
}
位置更新のコードをUpdateLifeGaugePosition
メソッドに切り出し、Update
メソッドをシンプルに保ちました。これにより、コードの可読性と保守性が向上します
利点
柔軟な調整:インスペクターからオフセット値を変更できるため、ライフゲージの表示位置を簡単に調整できます。
コードの整理:位置更新処理をメソッドに分離することで、コードが整理され、理解しやすくなります。
保守性の向上:将来的な変更や拡張が容易になります。
以上で、Unityにおいて3Dオブジェクトの頭上に情報を表示する基本的な方法について解説しました。今回の方法を基に、さらに複雑なUI表示や情報追従の実装に挑戦してみてください。
ディスカッション
コメント一覧
まだ、コメントがありません