カメラの向きに追従(ビルボード化)2Dスプライト【URP】

Unity技術

カメラの移動・回転に合わせて2Dスプライトの向きを追従(ビルボード化)させます。

システム概要

Unity 6 (URP) 環境の 2.5D ゲームにおいて、
カメラの移動・回転に合わせて2Dスプライトの向きを追従(ビルボード化)させるコンポーネントです。
少数のスプライト(最大50個程度)を対象とし、軽快かつ堅牢に動作します。

コア設計・ロジックの要点

イベント駆動型の回転検知:

  • LateUpdate 内で mainCameraTransform.rotation != lastCameraRotation をチェック。
  • カメラの停止中は計算処理を完全にスキップするため、
    毎フレームの不要なトランスフォーム計算コスト(CPU負荷)がほぼゼロに抑えられます。

2.5D専用の角度制御(潰れ防止):

  • Y軸(水平回転):カメラの eulerAngles.y(Yaw)をそのまま追従。
  • X軸(垂直傾き):インスペクターで設定する customPitchAngle で固定。
    カメラの見下ろしアングル(Pitch)によるスプライトの縦潰れを防ぎます。
  • Z軸:0f 固定(画像の傾きを防止)。

Sys_Billboard25D.cs(新規作成)

using UnityEngine;

[DisallowMultipleComponent]
public class Sys_Billboard25D : MonoBehaviour
{
    [SerializeField, Range(0f, 80f), Tooltip("スプライトの起こし角度(縦潰れ防止)")]
    private float customPitchAngle = 30f;

    private Transform mainCameraTransform;
    private Quaternion lastCameraRotation;

    private void Start()
    {
        if (Camera.main != null)
        {
            mainCameraTransform = Camera.main.transform;
            UpdateRotation();
        }
    }

    private void LateUpdate()
    {
        if (mainCameraTransform == null) return;

        // カメラの回転が変化したときだけ更新(停止時は処理をスキップ)
        if (mainCameraTransform.rotation != lastCameraRotation)
        {
            UpdateRotation();
        }
    }

    // カメラ回転完了時などに外部から直接呼び出すことも可能
    public void UpdateRotation()
    {
        if (mainCameraTransform == null) return;

        float cameraYaw = mainCameraTransform.eulerAngles.y;
        transform.rotation = Quaternion.Euler(customPitchAngle, cameraYaw, 0f);

        lastCameraRotation = mainCameraTransform.rotation;
    }
}

インスペクター設定・運用ルール

  • アタッチ対象:カメラ目線にしたい 2D スプライト(またはその親オブジェクト)
  • customPitchAngle の目安:
    標準値:30f(初期設定値)
    調整指針:使用するシネマカメラのX軸傾き(見下ろし角度)と同一または近い数値に設定すると、
    ドット絵やイラスト本来の頭身・縦横比が最も正確に保たれます。
  • カメラタグ:
    Camera.main を使用してカメラを取得するため、
    シーン内の対象メインカメラには必ず MainCamera タグを設定すること。

注意点・エッジケース対策

① 動的生成(Instantiate)時の挙動
実行中にプレハブとして生成された場合、Start() 内で自動的に一度 UpdateRotation() が実行され、
その時点のカメラ方向をすぐに向きます。

② 外部スクリプトからの直接制御(手動更新)
カメラカット切り替え時やシネマティクス演出時など、完全に確定したタイミングで回転を同期させたい場合は、
外部から component.UpdateRotation() を直接呼び出すことも可能。

➂ ライティング・色味についての前提
常にスプライトの表面がカメラに向くため、スプライトの面法線(Normal)もカメラ方向を向きます。
暗い場所から明るい場所へ向いた際など、Directional Light の向きによってスプライトの明暗が正しく変わります。
(裏面を向いて暗くなる事象は発生しません)

将来的な拡張についての懸念

現在、Start() で Camera.main を参照してキャッシュしています。
もし「ゲーム途中でカメラ(Camera.main)が別のカメラオブジェクトに切り替わる演出」
(例: 探索カメラから会話用シネマカメラへの切り替え、またはマルチカメラ構成)が存在する場合、
mainCameraTransform の参照が古いままになり、新しいカメラに追従しなくなる可能性があります。

Camera.main(Unity側の実体カメラ)が常に1つ固定で、CinemachineのVCam切り替えによってその実体カメラの Transform が動く構成であれば、参照が外れるリスクはありません。
今回の決定稿の設計で完全に動作します。

追加補足仕様(Cinemachine VCam切り替え時の動作)

VCam切り替え時の動作:
CinemachineBrain が実体カメラ(Camera.main)の Transform をブレンド移動・回転させるため、mainCameraTransform.rotation の変化が毎フレーム検知されます。

ブレンド中の挙動:
VCam間のブレンド移動中も滑らかにスプライトが追従し、
カメラが停止すると自動的に計算をスキップする省電力状態へ移行します。

コメント