ターゲット(たとえばキャラクターやオブジェクト)の位置をスクリーン上でUI(インジケーター)として表示するためのものです。
例えば画面内に配置してあるOjectを画面外に配置して場所を表示する際に使用する


このスクリプトは、ターゲットがカメラの前にあるか後ろにあるか、または画面外に出ているかを判断し、その位置に合わせてインジケーター(UI)が適切に表示されるようにします。 特に、ターゲットがカメラの後ろにある場合、インジケーターを反転させて表示し、画面外に出ないようにスケーリングして調整する処理を組み込んでいます。
[SerializeField] private Transform target = default;
[SerializeField] private RectTransform textObj = default;
private Camera mainCamera;
private RectTransform rectTransform;
target:
インジケーターが追いかける対象(ターゲット)を設定します。
このターゲットの位置を使ってインジケーターの位置を決定します。
textObj:
UIのインジケーター(テキストや画像など)のRectTransform。
ターゲットの位置に応じてこのUIの位置が動きます。
mainCamera:
カメラを参照するための変数です。ターゲットのワールド座標をスクリーン座標に変換する際に必要です。
rectTransform:
TargetIndicatorがアタッチされているRectTransformを取得します。UIの位置を制御するために使います。
StartメソッドでmainCameraにメインカメラを設定し、rectTransformに自分のRectTransformを設定しています。
LateUpdate メソッドLateUpdate内でUIの位置を毎フレーム更新しています。このメソッドが選ばれている理由は、LateUpdateはUpdateが終了した後に呼ばれるため、他のオブジェクトが動いてからインジケーターを更新することができるからです。
float canvasScale = transform.root.localScale.z;
var center = 0.5f * new Vector3(Screen.width, Screen.height,0f);
var pos = mainCamera.WorldToScreenPoint(target.position) - center;

canvasScale:キャンバスのスケールを取得します。
transform.root.localScale.zにより親オブジェクトのスケールを取得し、スケーリングを適用します。
center:
画面の中心座標です。
Screen.widthとScreen.heightを使用してスクリーンの幅と高さを取得し、それらを半分にして中央の位置を計算します。
のちに、画面中央から見た時の端の座標として使ってる
pos:
ターゲットのワールド座標をスクリーン座標に変換し、画面の中心からの相対位置に変更します。
これで、ターゲットの位置が画面中央を基準に調整されます。
画面中心を基準とした相対座標に変換されます。
if (pos.z < 0f) {
pos.x = -pos.x;
pos.y = -pos.y;
if (Mathf.Approximately(pos.y, 0f)) {
pos.y = -center.y;
}
}
pos.z < 0f:
ターゲットがカメラの後ろにある場合、pos.zは負の値になります。
この場合、ターゲットの位置を反転させる処理が行われます。
pos.xとpos.yを反転させることで、ターゲットがカメラの後ろにいる場合にインジケーターが画面の反対側に表示されます。
Mathf.Approximately(pos.y, 0f):
ターゲットのy座標が0に非常に近い場合(画面中央)に、pos.yを強制的に反転して中央に配置します。
var halfSize = 0.5f * canvasScale * rectTransform.sizeDelta;
float d = Mathf.Max(
Mathf.Abs(pos.x / (center.x - halfSize.x)),
Mathf.Abs(pos.y / (center.y - halfSize.y))
);
halfSize:
インジケーターのサイズを取得し、その半分の大きさを計算します。
このサイズを使って、インジケーターが画面外に出ないように調整します。
d:
ターゲットのスクリーン位置(pos)が画面の中心からどれくらい離れているかを計算します。
これによって、ターゲットが画面外にある場合に、インジケーターの位置が端に寄るようにスケールされます。
bool isOffscreen = (pos.z < 0f || d > 1f);
if (isOffscreen) {
pos.x /= d;
pos.y /= d;
}
isOffscreen:ターゲットがカメラの後ろにあるか、スクリーン外に出ているかを判定します。pos.xとpos.yをdで割ることで、インジケーターが画面端にぴったりくるように調整します。rectTransform.anchoredPosition = pos / canvasScale;
textObj.gameObject.SetActive(true);
rectTransform.anchoredPosition:インジケーターの位置を最終的に設定します。pos / canvasScaleで、インジケーターの位置をキャンバスのスケールに合わせて調整します。textObj.gameObject.SetActive(true):ターゲットが表示されている限り、インジケーターを表示するようにします。using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
public class TargetIndicator : MonoBehaviour
{
[SerializeField] private Transform target = default;
[SerializeField] private RectTransform textObj = default;
private Camera mainCamera;
private RectTransform rectTransform;
private void Start() {
mainCamera = Camera.main;
rectTransform = GetComponent<RectTransform>();
}
private void LateUpdate() {
float canvasScale = transform.root.localScale.z;
var center = 0.5f * new Vector3(Screen.width, Screen.height);
var pos = mainCamera.WorldToScreenPoint(target.position) - center;
if (pos.z < 0f) {
pos.x = -pos.x;
pos.y = -pos.y;
if (Mathf.Approximately(pos.y, 0f)) {
pos.y = -center.y;
}
}
var halfSize = 0.5f * canvasScale * rectTransform.sizeDelta;
float d = Mathf.Max(
Mathf.Abs(pos.x / (center.x - halfSize.x)),
Mathf.Abs(pos.y / (center.y - halfSize.y))
);
bool isOffscreen = (pos.z < 0f || d > 1f);
if (isOffscreen) {
pos.x /= d;
pos.y /= d;
}
rectTransform.anchoredPosition = pos / canvasScale;
textObj.gameObject.SetActive(true);
}
}
#例2 矢印の表示など(回転あり)
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(RectTransform))]
public class TargetIndicator : MonoBehaviour
{
[SerializeField] private Transform target = default;
[SerializeField] private Image arrow = default;
private Camera mainCamera;
private RectTransform rectTransform;
private void Start() {
mainCamera = Camera.main;
rectTransform = GetComponent<RectTransform>();
}
private void LateUpdate() {
float canvasScale = transform.root.localScale.z;
var center = 0.5f * new Vector3(Screen.width, Screen.height);
var pos = mainCamera.WorldToScreenPoint(target.position) - center;
if (pos.z < 0f) {
pos.x = -pos.x;
pos.y = -pos.y;
if (Mathf.Approximately(pos.y, 0f)) {
pos.y = -center.y;
}
}
var halfSize = 0.5f * canvasScale * rectTransform.sizeDelta;
float d = Mathf.Max(
Mathf.Abs(pos.x / (center.x - halfSize.x)),
Mathf.Abs(pos.y / (center.y - halfSize.y))
);
bool isOffscreen = (pos.z < 0f || d > 1f);
if (isOffscreen) {
pos.x /= d;
pos.y /= d;
}
rectTransform.anchoredPosition = pos / canvasScale;
arrow.enabled = isOffscreen;
if (isOffscreen) {
arrow.rectTransform.eulerAngles = new Vector3(
0f, 0f,
Mathf.Atan2(pos.y, pos.x) * Mathf.Rad2Deg
);
}
}
}