【Unity】オブジェクトの頭上に情報を表示する(基本)

2024年4月17日

様々な方法がありますが、今回は、UI(TMPro)オブジェクトにスクリプトをアタッチすることで実現する方法を見ていきましょう

考え方

3DオブジェクトのプレイヤーとUIオブジェクトは座標が違うため変換して、UIオブジェクトをプレイヤーの近くに表示されているように見せます

サンプルシーン構成

LifeGuageSampleとしてプロジェクトを作成しています

プレイヤーオブジェクト

頭上にUIがいつもついてくるオブジェクト

ライフゲージオブジェクト

テキストUIを使っています
このオブジェクトをUIローカル座標系で移動させ、常にプレイヤーの頭上にとどまることとします
UIであれば、テキスト以外でも同じようにすることができます

スクリプト

頭上のオブジェクト(ライフゲージオブジェクト)にアタッチするスクリプトになります
このスクリプトでは、インスペクターで2つのアウトレット接続をします

  • UIテキストの親オブジェクト(Canvas)
  • 追いかけるプレイヤーオブジェクト(Cube)

ただし、それぞれの型はGameObject型でないことに注意しましょう
コンポーネントの型を指定することでゲームオブジェクトにアタッチされているコンポーネントのインスタンス(実体)を取得することができます

GameObject.FindとGetComponentを同時に実行した振る舞いになります

using UnityEngine;

// プレイヤーのライフゲージを表示するクラス
public class LifeGauge : MonoBehaviour
{
    // 親のRectTransformを指定します。
    [SerializeField]
    RectTransform parentRectTransform;

    // プレイヤーのTransformを指定します。
    [SerializeField]
    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);
    }
}

コード解説

ライフゲージの親オブジェクト(Canvas)の四角形:矩形情報を取得

RectTransform は、UnityゲームエンジンのUIシステムで使用されるコンポーネントです。UI要素(テキスト、画像、ボタンなど)の位置、サイズ、回転、およびスケールを制御するための重要なコンポーネントです

[SerializeField] RectTransform parentRectTransform;

parentRectTransformという名前のプライベートな変数を宣言しています。この変数はInspectorパネルから設定できるように、[SerializeField]属性が付与されています。この変数はRectTransform型で、ライフゲージを配置する親のRectTransformを参照します。

プレイヤーの位置情報などを取得

Transformは、ゲームオブジェクトの位置、回転、スケールなどの変換情報を表すコンポーネントです。Transformコンポーネントは、ゲームオブジェクトが3D空間内でどのように配置され、表示されるかを制御します

[SerializeField] Transform playerTransform;

playerTransformという名前のプライベートな変数を宣言しています。同様に、Inspectorパネルから設定できるように、[SerializeField]属性が付与されています。この変数はTransform型で、キャラクターの位置情報を持つTransformを参照します。

プレイヤーの座標をワールド座標からスクリーン座標へ変換

ワールド座標

3D空間を指す座標

スクリーン座標

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

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

Vector3 screenPoint = Camera.main.WorldToScreenPoint(playerTransform.position);

Camera.mainを使用して、playerTransformの位置をワールド座標からスクリーン座標に変換し、その座標をscreenPointに格納します。これにより、キャラクターの位置情報を画面上の座標に変換できます。

スクリーン座標から親(Canvas)の子オブジェクト(Text(TMP))としてのローカル座標系に変換

プレイヤーをUI上の座標として考えるために、左上が原点のスクリーン座標から、Pivot位置が原点のローカル座標系に変換します

この値を元にライフゲージの座標を追随させます

アンカーを中心とした時の座標系のイメージがローカル座標系のイメージになります
実際は、アンカーの位置を変えてもローカル座標系の原点には反映されません

RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRectTransform, screenPoint, null, out Vector2 localPoint);

screenPointを parentRectTransform のローカル座標系に変換します。結果は localPoint に格納されます。これにより、親RectTransform内での位置を取得できます。

RectTransformUtility.ScreenPointToLocalPointInRectangle メソッドは、UnityのUIシステムで使用される、スクリーン座標から指定された親要素のローカル座標系への変換を行うための便利なメソッドです。このメソッドはUI要素の操作やイベント処理の際に非常に役立ちます。

以下に、このメソッドの各引数と役割を説明します:

  1. parentRectTransform: これは、ローカル座標系に変換するための親要素の RectTransform コンポーネントを指定するパラメータです。スクリーン座標からローカル座標に変換する際、この親要素の座標系を基準として変換が行われます。
  2. screenPoint: screenPoint は、スクリーン上での座標を指定するパラメータです。通常、マウスの位置やタッチイベントの位置など、デバイスの画面上での座標を指定します。
  3. canvas (null): canvas パラメータは、特定の Canvas グループに関連する場合に指定されます。通常、これをnullに設定し、screenPoint と parentRectTransform の間の変換を行います。ただし、特定の Canvas グループに関連付けられている場合、それを指定できます。
  4. localPoint: localPoint パラメータは、メソッドの出力として使用されます。メソッドが成功した場合、localPoint に変換されたローカル座標が格納されます。この座標は、指定された parentRectTransform 内での位置を示します。

このメソッドは、スクリーン上の座標(screenPoint)を、指定した親要素のローカル座標系に変換するのに使用されます。ローカル座標系の原点はCanvasの中心地点です。例えば、マウスカーソルの位置を取得し、それをUI要素のローカル座標系内で使用したい場合に便利です。このメソッドを使用することで、UI要素の位置に関する計算を簡略化し、正確な位置情報を得ることができます。

transform.localPosition = localPoint + new Vector2(0, 50);

ライフゲージのTransformコンポーネントのローカル位置を、localPointに Vector2(0, 50)を加算することで設定します。このコードは、ライフゲージを少し上方にずらして表示するためのもので、キャラクターの位置に対して相対的な位置を表しています。

このスクリプトは、ゲームオブジェクトにアタッチされた際に、指定された親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;
    }
}

このコードの変更点

  1. offset フィールドを導入して、オフセット値を簡単に調整できるようにしました。これにより、オフセットを変更したい場合、コードの複数の箇所を変更する必要がなくなります。
  2. UpdateLifeGaugePosition メソッドを作成し、UIの位置を更新するコードを切り出しました。これにより、Update メソッドが読みやすくなり、関連するコードをまとめました。

これらの変更により、コードがより読みやすくなり、保守性が向上しました。また、オフセットの調整が簡単になり、コードの変更が効率的に行えるようになります。

Unity

Posted by hidepon