Unity技術資料: 単一キャンバスで複数の3DオブジェクトにステータスUIを表示する方法
本資料では、Unityを使用して単一のキャンバス(Canvas)上に複数の3Dオブジェクトの頭上にステータスUI(例:名前ラベル、ヘルスバー)を表示する方法について詳細に解説します。効率的なUI管理とパフォーマンス最適化を考慮し、スクリーン座標への変換を活用した実装手法を紹介します。
はじめに
Unityにおいて、複数の3Dオブジェクトに対して個別のステータスUIを表示する場合、各オブジェクトごとにキャンバスを配置するとパフォーマンスに影響を与える可能性があります。そこで、単一のキャンバスを使用して効率的にUIを管理する方法を紹介します。
前提条件
- Unity 2019.4以降
- 基本的なUnityの操作知識
- C#プログラミングの基礎知識
実装手順
ステップ1: キャンバスの設定
- Canvasの作成
- Hierarchyビューで右クリック → UI → Canvasを選択して新しいCanvasを作成します。
- 作成したCanvasの名前を「StatusCanvas」など分かりやすい名前に変更します。
- CanvasのRender Mode設定
StatusCanvas
を選択し、InspectorビューでCanvas
コンポーネントのRender Mode
を以下のいずれかに設定します。- Screen Space – Overlay: UIが常にスクリーン上にオーバーレイ表示されます。
- Screen Space – Camera: 特定のカメラに対してUIをレンダリングします。この場合、
Render Camera
を指定します。
- Canvas Scalerの設定
Canvas
のCanvas Scaler
コンポーネントを確認し、UI Scale Mode
をScale With Screen Sizeに設定します。Reference Resolution
を適切な値(例:1920×1080)に設定し、Screen Match Mode
をMatch Width Or Heightに設定します。
ステップ2: UIプレハブの作成
- UI要素の作成
StatusCanvas
の子として、表示したいステータスのUI要素を作成します。例として、名前表示用のText
とヘルスバー用のSlider
を作成します。- UI要素の配置やデザインを調整し、見栄えを整えます。
- プレハブ化
- 作成したUI要素(例:名前表示用の
Text
とヘルスバー用のSlider
を含む親オブジェクト)を選択し、Projectビューにドラッグ&ドロップしてプレハブ化します。例として「StatusUI.prefab」と命名します。
- 作成したUI要素(例:名前表示用の
ステップ3: スクリプトの作成
スクリプトの新規作成
Projectビューで右クリック → Create → C# Scriptを選択し、「StatusDisplay.cs」と命名します。
スクリプトの編集
- 以下のコードを
StatusDisplay.cs
に記述します。
using UnityEngine;
using UnityEngine.UI;
public class StatusDisplay : MonoBehaviour
{
[Header("UI設定")]
public Camera mainCamera; // メインカメラ
public Canvas canvas; // ステータスを表示するキャンバス
public GameObject statusPrefab; // ステータスUIのプレハブ
public Vector3 offset = new Vector3(0, 2, 0); // UIのオフセット位置
private GameObject statusUIInstance;
private RectTransform canvasRectTransform;
void Start()
{
// カメラの設定
if (mainCamera == null)
mainCamera = Camera.main;
// キャンバスの設定
if (canvas == null)
canvas = FindObjectOfType<Canvas>();
// ステータスUIのインスタンスを生成
statusUIInstance = Instantiate(statusPrefab, canvas.transform);
canvasRectTransform = canvas.GetComponent<RectTransform>();
// 初期設定(例:名前の設定)
Text nameText = statusUIInstance.transform.Find("NameText").GetComponent<Text>();
nameText.text = gameObject.name;
}
void Update()
{
if (statusUIInstance != null)
{
// オブジェクトの位置にオフセットを加算
Vector3 worldPosition = transform.position + offset;
// ワールド座標をスクリーン座標に変換
Vector3 screenPosition = mainCamera.WorldToScreenPoint(worldPosition);
// スクリーン座標をキャンバスのローカル座標に変換
Vector2 anchoredPosition;
RectTransformUtility.ScreenPointToLocalPointInRectangle(
canvasRectTransform,
screenPosition,
canvas.renderMode == RenderMode.ScreenSpaceCamera ? mainCamera : null,
out anchoredPosition
);
// UIの位置を更新
RectTransform statusRect = statusUIInstance.GetComponent<RectTransform>();
statusRect.anchoredPosition = anchoredPosition;
// オブジェクトがカメラの前にあるか確認し、UIの表示/非表示を切り替え
bool isVisible = screenPosition.z > 0;
statusUIInstance.SetActive(isVisible);
}
}
void OnDestroy()
{
// オブジェクトが破壊された際にUIも破壊
if (statusUIInstance != null)
{
Destroy(statusUIInstance);
}
}
}
- コードの解説
- 変数説明
mainCamera
: スクリーン座標への変換に使用するカメラ。指定がない場合はメインカメラを自動取得。canvas
: UIを配置するキャンバス。指定がない場合はシーン内のCanvasを自動取得。statusPrefab
: ステータス表示用のUIプレハブ。offset
: UIをオブジェクトの頭上に表示するための位置オフセット。
Start
メソッド- 必要なコンポーネント(カメラ、キャンバス)を取得。
- ステータスUIのインスタンスを生成し、キャンバスの子として配置。
- UI内の
Text
コンポーネントにオブジェクト名を設定。
Update
メソッド- オブジェクトの位置にオフセットを加算し、ワールド座標を取得。
WorldToScreenPoint
を使用してワールド座標をスクリーン座標に変換。ScreenPointToLocalPointInRectangle
を使用してスクリーン座標をキャンバスのローカル座標に変換。- UIの
RectTransform
のanchoredPosition
を更新して位置を設定。 - オブジェクトがカメラの前にあるか確認し、UIの表示/非表示を切り替え。
OnDestroy
メソッド- オブジェクトが破壊された際に対応するUIも破壊することでメモリリークを防止。
- 変数説明
ステップ4: スクリプトの適用
- プレハブの準備
- 作成した
StatusUI.prefab
には、以下のUI要素を含めます。- NameText: オブジェクト名を表示する
Text
コンポーネント。 - HealthBar: ヘルスを表示する
Slider
コンポーネントなど。
- NameText: オブジェクト名を表示する
- 作成した
- 3Dオブジェクトへのスクリプト適用
- Hierarchyビューでステータスを表示したい3Dオブジェクトを選択します。
- Inspectorビューで「Add Component」をクリックし、
StatusDisplay
スクリプトを追加します。 - スクリプトのインスペクターで以下を設定します。
Status Prefab
:StatusUI.prefab
をドラッグ&ドロップ。Offset
: 必要に応じて調整(例:(0, 2, 0)
など)。
- 複数オブジェクトへの適用
- 複数の3Dオブジェクトに対して同様に
StatusDisplay
スクリプトを追加することで、各オブジェクトの頭上にステータスUIが表示されます。
- 複数の3Dオブジェクトに対して同様に
ステップ5: パフォーマンスの最適化
大量のオブジェクトに対してステータスUIを表示する場合、パフォーマンスの低下が懸念されます。以下の最適化手法を検討します。
- プールシステムの導入
- UI要素の動的生成と破棄を避け、事前に一定数のUIインスタンスをプールして再利用します。これにより、ガベージコレクションの発生を抑制し、パフォーマンスを向上させます。
- UIの更新頻度の調整
- 全てのUIを毎フレーム更新するのではなく、一定間隔(例:0.1秒ごと)で更新するようにします。
Update
メソッドではなく、Coroutine
を使用する方法も有効です。
- 全てのUIを毎フレーム更新するのではなく、一定間隔(例:0.1秒ごと)で更新するようにします。
- カリングの活用
- カメラの視野外にあるオブジェクトのUIは非表示にすることで、不要な描画を削減します。
Renderer.isVisible
プロパティを活用して、オブジェクトの可視状態を判定します。
- カメラの視野外にあるオブジェクトのUIは非表示にすることで、不要な描画を削減します。
- UI要素の軽量化
- 必要最低限のUI要素のみを表示し、過剰なエフェクトやアニメーションを避けることで、描画負荷を軽減します。
補足: ワールドスペースキャンバスの代替案
上記の方法はスクリーンスペースキャンバスを使用していますが、特定のケースではワールドスペースキャンバスを使用する方が適している場合があります。ワールドスペースキャンバスは3D空間内にUIを配置するため、カメラとの連動が自然になります。
ワールドスペースキャンバスの設定手順
- キャンバスの設定変更
StatusCanvas
を選択し、Canvas
コンポーネントのRender Mode
をWorld Spaceに変更します。RectTransform
のサイズを適切に調整し、UIが適切なスケールで表示されるようにします。
- UIプレハブの作成
- ワールドスペースキャンバス用のUIプレハブを作成します。通常、テキストや画像は3Dオブジェクトの子として配置します。
StatusUI.prefab
をワールドスペースキャンバスに適したサイズと配置に調整します。
- スクリプトの調整
- ワールドスペースキャンバスを使用する場合、UIの位置は3D空間内で直接管理します。スクリーン座標への変換は不要です。
StatusDisplay.cs
のUpdate
メソッドを以下のように変更します。
- ワールドスペースキャンバスを使用する場合、UIの位置は3D空間内で直接管理します。スクリーン座標への変換は不要です。
void Update()
{
if (statusUIInstance != null)
{
// UIの位置をオブジェクトの頭上に設定
Vector3 worldPosition = transform.position + offset;
statusUIInstance.transform.position = worldPosition;
// オブジェクトがカメラの前にあるか確認
Vector3 toCamera = mainCamera.transform.position - transform.position;
bool isVisible = Vector3.Dot(transform.forward, toCamera) > 0;
statusUIInstance.SetActive(isVisible);
}
}
ワールドスペースキャンバスでは、UIが3D空間内に存在するため、直接位置を設定するだけで表示位置が反映されます。
まとめ
本資料では、Unityにおいて単一のキャンバスを使用して複数の3Dオブジェクトの頭上にステータスUIを表示する方法について解説しました。スクリーンスペースキャンバスとワールドスペースキャンバスの両方の手法を紹介し、各々の利点と実装手順を説明しました。また、大量のオブジェクトに対応する際のパフォーマンス最適化手法についても触れました。
効率的なUI管理とパフォーマンスの維持を両立させるために、プロジェクトの要件に応じて最適な手法を選択してください。
ディスカッション
コメント一覧
まだ、コメントがありません