Unityを触り始めた頃によくやってしまうのが、「Update に全部書いてしまう」スタイルですよね。
カメラ操作、プレイヤー入力、UI更新、エフェクト制御……全部ひとつのスクリプトに詰め込んでしまうと、少し仕様を変えたいだけでもコードのどこを触ればいいのか分からなくなってしまいます。

カメラの「ちょっとした演出」も同じで、「プレイヤー制御スクリプトの中にカメラの揺れやズレの処理を全部書く」ような実装だと、すぐにGodクラス化してしまいます。

そこで今回は、

  • 「特定キーを押している間だけ」
  • 「マウス位置の方向に」
  • 「カメラをふわっとずらす」

という機能だけに責務を絞ったコンポーネント
「MousePeek(マウス視点)」 を作って、カメラにアタッチするだけで使えるようにしてみましょう。

【Unity】マウス方向にチラ見カメラ!「MousePeek」コンポーネント

以下が、Unity6(新Input System前提)で動作する「MousePeek」コンポーネントのフルコードです。
カメラにアタッチすることで、「指定キーを押している間だけ」マウス方向にカメラをオフセットさせます。


using UnityEngine;
using UnityEngine.InputSystem; // 新Input System 用

/// <summary>
/// 特定キーを押している間、マウス位置の方向へカメラをオフセットするコンポーネント。
/// カメラにアタッチして使います。
/// </summary>
[RequireComponent(typeof(Camera))]
public class MousePeek : MonoBehaviour
{
    // --- 設定項目(インスペクターから調整) ---

    [Header("基本設定")]
    [SerializeField]
    private float maxOffsetDistance = 2.0f;
    // カメラが最大でどれくらいずれるか(ワールド座標の距離)

    [SerializeField]
    private float moveSpeed = 10.0f;
    // 現在位置から目標オフセット位置への追従速度(Lerp用)

    [SerializeField]
    private float returnSpeed = 10.0f;
    // キーを離したときに元の位置へ戻る速度(Lerp用)

    [SerializeField]
    private float depthFromCamera = 10.0f;
    // マウスのワールド座標を取得する際に使う深度(カメラからの距離)

    [Header("入力設定")]
    [SerializeField]
    private Key peekKey = Key.Mouse2;
    // マウス中ボタンなど。Keyboardのキーも指定可能(Key.LeftShift など)

    [SerializeField]
    private bool useScreenEdgeNormalization = true;
    // true: 画面中心からの相対位置でオフセット
    // false: ワールド座標ベースでオフセット

    [Header("デバッグ")]
    [SerializeField]
    private bool drawGizmos = true;
    // シーンビューにオフセット位置を表示するか

    // --- 内部状態 ---

    private Camera _camera;
    private Vector3 _basePosition;
    // カメラの「基準位置」(オフセット前の位置)

    private bool _isPeeking;
    // 現在「覗き込み中」かどうか

    private void Awake()
    {
        _camera = GetComponent<Camera>();
        _basePosition = transform.position;
    }

    private void OnEnable()
    {
        // 有効化時に基準位置を更新しておく
        _basePosition = transform.position;
    }

    private void Update()
    {
        if (Mouse.current == null)
        {
            // マウスが存在しない環境(ゲームパッドのみ等)では何もしない
            return;
        }

        // キー入力の状態を取得
        bool isKeyPressed = Keyboard.current != null && Keyboard.current[peekKey].isPressed
                            || Mouse.current != null && Mouse.current[peekKey].isPressed;

        _isPeeking = isKeyPressed;

        // 現在のカメラ位置
        Vector3 currentPosition = transform.position;

        // 目標位置を計算
        Vector3 targetPosition = _basePosition;

        if (_isPeeking)
        {
            // マウス方向にオフセットを計算
            Vector3 offset = CalculateOffsetFromMouse();
            targetPosition = _basePosition + offset;

            // 覗き込み中は moveSpeed で追従
            transform.position = Vector3.Lerp(
                currentPosition,
                targetPosition,
                1f - Mathf.Exp(-moveSpeed * Time.deltaTime)
            );
        }
        else
        {
            // 覗き込みをしていないときは basePosition に戻る
            transform.position = Vector3.Lerp(
                currentPosition,
                _basePosition,
                1f - Mathf.Exp(-returnSpeed * Time.deltaTime)
            );
        }
    }

    /// <summary>
    /// マウス位置からカメラのオフセットベクトルを計算する。
    /// </summary>
    private Vector3 CalculateOffsetFromMouse()
    {
        // 画面上のマウス位置(ピクセル座標)
        Vector2 mousePos = Mouse.current.position.ReadValue();
        Vector3 offset = Vector3.zero;

        if (useScreenEdgeNormalization)
        {
            // --- 画面中心からの相対位置でオフセットするモード ---
            // 画面サイズ
            float width = Screen.width;
            float height = Screen.height;

            // 画面中心を(0,0)、四隅を(-1,-1)〜(1,1)に正規化
            Vector2 normalized = new Vector2(
                (mousePos.x - width * 0.5f) / (width * 0.5f),
                (mousePos.y - height * 0.5f) / (height * 0.5f)
            );

            // 正規化された値をクランプ(安全のため)
            normalized = Vector2.ClampMagnitude(normalized, 1f);

            // カメラの右方向と上方向を使ってオフセットを作る
            Vector3 right = transform.right;
            Vector3 up = transform.up;

            offset = (right * normalized.x + up * normalized.y) * maxOffsetDistance;
        }
        else
        {
            // --- ワールド座標ベースでオフセットするモード ---
            // マウス位置をワールド座標に変換
            Vector3 screenPoint = new Vector3(mousePos.x, mousePos.y, depthFromCamera);
            Vector3 worldPoint = _camera.ScreenToWorldPoint(screenPoint);

            // カメラ基準位置から見たマウス方向
            Vector3 dir = (worldPoint - _basePosition);
            dir.z = 0f; // 2DゲームなどでZ方向に動かしたくない場合は0に固定

            if (dir.sqrMagnitude > 0.0001f)
            {
                dir.Normalize();
                offset = dir * maxOffsetDistance;
            }
        }

        return offset;
    }

    /// <summary>
    /// シーンビュー上でオフセット位置を可視化する(デバッグ用)。
    /// </summary>
    private void OnDrawGizmosSelected()
    {
        if (!drawGizmos)
        {
            return;
        }

        // 再生中かどうかで基準位置を決定
        Vector3 basePos = Application.isPlaying ? _basePosition : transform.position;

        Gizmos.color = Color.cyan;
        Gizmos.DrawWireSphere(basePos, 0.1f);

        // 想定される最大オフセット範囲を円で表示
        Gizmos.color = new Color(0f, 1f, 1f, 0.25f);
        Gizmos.DrawWireSphere(basePos, maxOffsetDistance);
    }

    /// <summary>
    /// ゲーム中に「今の位置を基準位置として登録し直す」ためのメソッド。
    /// 例えばカメラの追従ターゲットが変わったときに外部から呼ぶ想定。
    /// </summary>
    public void ResetBasePositionToCurrent()
    {
        _basePosition = transform.position;
    }
}

使い方の手順

ここでは、典型的な「プレイヤー追従カメラ」に MousePeek を足して、
「中ボタンを押している間だけ、マウス方向にカメラがチラ見する」例を想定して説明します。

  1. コンポーネントを作成する
    • Unity の Project ビューで MousePeek.cs を作成し、上記コードをコピペします。
    • 保存したら、コンパイルエラーが出ていないことを確認します。
  2. カメラにアタッチする
    • シーン内のカメラ(例: Main Camera)を選択します。
    • Inspector の「Add Component」から MousePeek を追加します。
    • [RequireComponent(typeof(Camera))] を付けているので、カメラ以外にアタッチしようとすると警告が出ます。
  3. インスペクターでパラメータを調整する
    例として、次のように設定してみましょう。
    • Max Offset Distance: 1.5〜3.0(カメラがどれくらい動くか。3Dなら小さめ、2Dならやや大きめがおすすめ)
    • Move Speed: 10〜20(覗き込み開始の追従速度)
    • Return Speed: 10〜20(元位置に戻る速度)
    • Depth From Camera: 10(カメラから10ユニット先の平面でマウス位置を解釈)
    • Peek Key: Mouse2(マウス中ボタン)や LeftShift など好みで変更
    • Use Screen Edge Normalization: ON なら「画面端に行くほど強くオフセット」、OFF なら「ワールド座標方向ベース」
  4. 実際のゲームで試す
    具体的な使用例として、次のようなシチュエーションで試すと分かりやすいです。
    • プレイヤーキャラの追従カメラ
      • プレイヤーを追いかけるカメラに MousePeek を付けると、
      • 「中ボタンを押している間だけ、マウス方向に画面を少しずらして先を確認できる」
      • という「チラ見」機能になります。見通しの悪いダンジョンや横スクロールの先読みなどに便利です。
    • タワーディフェンスやRTSの俯瞰カメラ
      • 通常は WASD やマウスドラッグでスクロールしつつ、
      • 一時的に MousePeek でマウス方向に画面を寄せることで、素早く戦況を確認できます。
    • 動く床やギミックのプレビュー
      • 2Dアクションで、足場の先にあるトラップや動く床を、
      • 「覗き込みキー」を押している間だけチラ見できるようにすると、理不尽さを減らしつつ緊張感を保てます。

メリットと応用

カメラの「覗き込み」機能を MousePeek という単一コンポーネントに切り出すことで、次のようなメリットがあります。

  • プレイヤー制御スクリプトを肥大化させない
    カメラの演出ロジックをプレイヤーの移動スクリプトから完全に分離できるので、
    「移動は移動」「カメラ演出はカメラ演出」と責務を分けて考えられます。
  • プレハブ化してどのシーンでも再利用できる
    MousePeek を付けたカメラをプレハブ化しておけば、
    新しいシーンにそのままドラッグ&ドロップするだけで「覗き込みカメラ」が完成します。
    レベルデザイナーがパラメータを触るだけで演出の強さを調整できるのもポイントです。
  • 他のカメラ演出コンポーネントと組み合わせやすい
    例えば、カメラシェイク用コンポーネント、ズーム制御コンポーネントなどと併用しても、
    それぞれが小さな責務に分かれているので、バグの原因を追いやすくなります。

応用として、「覗き込み中は少しだけカメラをズームインする」などの演出も簡単に追加できます。
例えば、次のようなメソッドを MousePeek に追加し、Update から呼び出すと、
覗き込み中に FOV を変化させることができます。


    /// <summary>
    /// 覗き込み中にカメラのFOVを少し変化させる応用例。
    /// Camera.main ではなく、自分の Camera コンポーネントを直接操作します。
    /// </summary>
    private void UpdatePeekFov(float normalFov = 60f, float peekFov = 55f, float fovLerpSpeed = 5f)
    {
        if (_camera == null) return;

        float targetFov = _isPeeking ? peekFov : normalFov;

        _camera.fieldOfView = Mathf.Lerp(
            _camera.fieldOfView,
            targetFov,
            1f - Mathf.Exp(-fovLerpSpeed * Time.deltaTime)
        );
    }

このように、小さなコンポーネントとしてカメラの「覗き込み」だけを切り出しておくと、
あとから「ちょっとズームを足す」「ちょっとシェイクを足す」といった改造がしやすくなります。
Update に全部書き込むスタイルから卒業して、責務ごとにコンポーネントを分けていきましょう。