Tips

【Unity】uGUIをキャラクターの上に表示する手っ取り早い方法【RenderMode別】

【Unity】uGUIをキャラクターの上に表示する手っ取り早い方法【RenderMode別】

RenderModeによって異なる、HUDの表示方法


uGUIをキャラクターの頭上に表示(HUD)して、オブジェクトの移動に追従させる方法は、CanvasのRenderModeによって異なります。
今回はその実装の手っ取り早い方法について、RenderMode別に解説します。
RenderModeについての詳しい解説は、こちら をご覧ください。

※使用しているUnityのバージョンは2017.1.0f3です。
[Unity_317×90]

Screen Space – Overlayの場合



© Unity Technologies Japan/UCL

CanvasのRenderModeがScree Space – Overlayの場合、UIの位置をキャラクターに合わせて変化させるためには、キャラクターのワールド座標をスクリーン座標に変換してからUIに教える必要があります。

ワールド座標からスクリーン座標への変換は、RectTransformUtilityクラスに用意されているWorldToScreenPointメソッドを使用します。

同期させたいUIオブジェクトに適用するスクリプトです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
 
public class UIController_Overlay : MonoBehaviour {
 
    [SerializeField]
    private Transform targetTfm;
 
    private RectTransform myRectTfm;
    private Vector3 offset = new Vector3(0, 1.5f, 0);
 
    void Start() {
        myRectTfm = GetComponent<RectTransform>();
    }
 
    void Update() {
        myRectTfm.position
            = RectTransformUtility.WorldToScreenPoint(Camera.main, targetTfm.position + offset);
    }
}

WorldToScreenPointメソッドは、第1引数に描画するカメラ(コンポーネント)の指定、第2引数に変換対象のTransformのPosition(Vector3)を指定することで、Canvas平面上に対応するVector2の値を返します。

例えば下記のスクリプトは、位置を変化させたいUIオブジェクトにアタッチします。

UIを追従させる対象のPlayerオブジェクトからワールド座標を取得し、メインカメラにおけるスクリーンポジションを算出してRectTransformのPositionに値を代入しています。

ちなみに、変換される座標はPivotが基準になるため、キャラクターの頭上に表示するためにオフセットで調整しています。

Screen Space – Cameraの場合



© Unity Technologies Japan/UCL

RenderModeがScree Space – Cameraの場合、UIはカメラが描画ことになります。
そのため、Scree Space – Overlayでの変換にもうひと手間加え、ワールド座標から算出したスクリーン座標を、カメラの描画角を基準としたローカル座標に変換します。

ローカル座標への変換は、RectTransformUtilityクラスのScreenPointToLocalPointInRectangleメソッドを使用します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using UnityEngine;
 
public class UIController_Camera : MonoBehaviour {
 
    [SerializeField]
    private RectTransform canvasRectTfm;
    [SerializeField]
    private Transform targetTfm;
 
    private RectTransform myRectTfm;
    private Vector3 offset = new Vector3(0, 1.5f, 0);
 
    void Start() {
        myRectTfm = GetComponent<RectTransform>();
 
    }
 
    void Update() {
        Vector2 pos;
 
        Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(Camera.main, targetTfm.position + offset);
 
        RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTfm, screenPos, Camera.main, out pos);
 
        myRectTfm.position = pos;
 
    }
}

ScreenPointToLocalPointInRectangleメソッドは、第1引数にCanvasのRectTransform、第2引数に変換するスクリーン座標、第3引数にUIを描画するカメラ(コンポーネント)、第4引数にoutパラメータを付与したVector2の変数を渡します。

outに渡す引数は事前に宣言しておきます。算出された値はoutの変数に入ります。

World Spaceの場合



© Unity Technologies Japan/UCL

RenderModeがWorld Spaceの場合、UIは通常のゲームオブジェクトと同じようにワールド座標系に配置できます。
したがって、手っ取り早くプレイヤーの位置と同期させるなら、プレイヤーの子オブジェクトにしてしまいましょう。
ただし、Rotationも同期されてしまうため、UIがカメラの方向を向くようにスクリプトで処理します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using UnityEngine;
 
public class UIController_WorldSpace : MonoBehaviour {
 
    private RectTransform myRectTfm;
 
    void Start() {
        myRectTfm = GetComponent<RectTransform>();
    }
 
    void Update() {
        // 自身の向きをカメラに向ける
        myRectTfm.LookAt(Camera.main.transform);
    }
}

一番手っ取り早い感ありますね。

おまけ(列挙型を使ってRenderModeに動的に対応する)


別々のスクリプトを用意するのが面倒だったので、列挙型のRenderModeを使って一つのスクリプトにまとめてみました。
RenderModeが切り替わっても自動で対応できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
 
public class UIController : MonoBehaviour {
 
    [SerializeField]
    private Canvas canvas;
    [SerializeField]
    private Transform targetTfm;
 
    private RectTransform canvasRectTfm;
    private RectTransform myRectTfm;
    private Vector3 offset = new Vector3(0, 1.5f, 0);
 
    void Start() {
        canvasRectTfm = canvas.GetComponent<RectTransform>();
        myRectTfm = GetComponent<RectTransform>();
    }
 
    void Update() {
        Vector2 pos;
         
        switch (canvas.renderMode) {
 
            case RenderMode.ScreenSpaceOverlay:
                myRectTfm.position = RectTransformUtility.WorldToScreenPoint(Camera.main, targetTfm.position + offset);
 
            break;
 
            case RenderMode.ScreenSpaceCamera:
                Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(Camera.main, targetTfm.position + offset);
                RectTransformUtility.ScreenPointToLocalPointInRectangle(canvasRectTfm, screenPos, Camera.main, out pos);
                myRectTfm.localPosition = pos;
                break;
 
            case RenderMode.WorldSpace:
                myRectTfm.LookAt(Camera.main.transform);
 
            break;
        }
    }
}

私はこれ↑を使ってます。

以上です。

ゲーム制作関連のオススメ連載リンク

とっても手軽なゲーム制作体験!
Unityゲーム開発基礎

実際のリリースゲームを題材にしたハンズオンゲーム制作連載
実践unityゲーム開発

Recent News

Recent Tips

Tag Search