Unityを触り始めたころは、何でもかんでも Update() に書きがちですよね。
「Fキーが押されたらフルスクリーン」「数字キーで解像度変更」「オプション画面からレンダリングスケール変更」…と、全部を1つのスクリプトの Update() に押し込んでしまうと、すぐにカオスになります。

  • どのキーがどの解像度だったか分からない
  • UIのボタンからも同じ処理を呼びたくなってコピペ地獄
  • ビルドターゲットごとの挙動差分を入れ始めて、巨大な if の塊になる

こういうときに意識したいのが「機能ごとにコンポーネントを分ける」設計です。
今回は「画面解像度・フルスクリーン・レンダリングスケール」の変更だけを担当する、小さなコンポーネント ResolutionScaler を作ってみましょう。

【Unity】ゲームの見え方を一括管理!「ResolutionScaler」コンポーネント

このコンポーネントは、

  • ウィンドウ解像度の変更
  • フルスクリーンの ON / OFF
  • レンダリングスケール(内部解像度)の変更

といった「画面の見え方」に関する処理だけを担当します。
入力の受け取り(ボタン・キーボード・設定画面のスライダーなど)は別コンポーネントに任せ、このコンポーネントには 「こうしてほしい」 という命令だけを投げるイメージですね。

フルコード


using UnityEngine;
using UnityEngine.Events;

namespace Sample.Resolution
{
    /// <summary>
    /// 画面解像度 / フルスクリーン / レンダリングスケールをまとめて管理するコンポーネント。
    /// ・ゲーム内オプションメニュー
    /// ・デバッグ用の解像度切り替え
    /// などから呼び出して使います。
    /// 
    /// 入力(キー入力やUIボタン)は別コンポーネントに任せ、
    /// このクラスは「画面の見え方を変更する役割」だけに責務を絞っています。
    /// </summary>
    public class ResolutionScaler : MonoBehaviour
    {
        [Header("起動時の基本設定")]
        [SerializeField]
        private bool applyOnStart = true;

        [SerializeField]
        private int initialWidth = 1920;

        [SerializeField]
        private int initialHeight = 1080;

        [SerializeField]
        private FullScreenMode initialFullScreenMode = FullScreenMode.FullScreenWindow;

        [SerializeField, Range(0.5f, 2.0f)]
        private float initialRenderScale = 1.0f;

        [Header("解像度プリセット(任意)")]
        [Tooltip("UIなどから簡単に切り替えたい解像度プリセットを定義しておきます。")]
        [SerializeField]
        private ResolutionPreset[] presets = new ResolutionPreset[]
        {
            new ResolutionPreset("HD (1280x720)", 1280, 720),
            new ResolutionPreset("Full HD (1920x1080)", 1920, 1080),
            new ResolutionPreset("WQHD (2560x1440)", 2560, 1440),
        };

        [Header("イベント(任意)")]
        [Tooltip("解像度やフルスクリーンが変更されたときに呼ばれるイベント")]
        [SerializeField]
        private UnityEvent onScreenSettingsChanged;

        // 現在の状態をキャッシュ(Inspector からも確認しやすいようにする)
        [Header("現在の状態(デバッグ用)")]
        [SerializeField, ReadOnlyInInspector]
        private int currentWidth;

        [SerializeField, ReadOnlyInInspector]
        private int currentHeight;

        [SerializeField, ReadOnlyInInspector]
        private FullScreenMode currentFullScreenMode;

        [SerializeField, ReadOnlyInInspector]
        private float currentRenderScale;

        private void Start()
        {
            if (applyOnStart)
            {
                ApplyScreenSettings(
                    initialWidth,
                    initialHeight,
                    initialFullScreenMode,
                    initialRenderScale
                );
            }
            else
            {
                // 現在の状態をキャッシュだけしておく
                currentWidth = Screen.width;
                currentHeight = Screen.height;
                currentFullScreenMode = Screen.fullScreenMode;
                currentRenderScale = GetRenderScale();
            }
        }

        #region Public API - 外部から呼び出すためのメソッド群

        /// <summary>
        /// 解像度とフルスクリーンモード、レンダリングスケールをまとめて変更します。
        /// UIボタンやスライダーなどから直接呼び出してOKです。
        /// </summary>
        public void ApplyScreenSettings(
            int width,
            int height,
            FullScreenMode fullScreenMode,
            float renderScale)
        {
            // 幅・高さの下限チェック(極端に小さい値を防ぐ)
            width = Mathf.Max(320, width);
            height = Mathf.Max(240, height);

            // レンダリングスケールの範囲をクランプ
            renderScale = Mathf.Clamp(renderScale, 0.5f, 2.0f);

            // 解像度とフルスクリーンモードを適用
            Screen.SetResolution(width, height, fullScreenMode);

            // レンダリングスケールを適用
            SetRenderScale(renderScale);

            // キャッシュを更新
            currentWidth = width;
            currentHeight = height;
            currentFullScreenMode = fullScreenMode;
            currentRenderScale = renderScale;

            // イベント発火(UIのラベル更新などに使える)
            onScreenSettingsChanged?.Invoke();
        }

        /// <summary>
        /// 解像度だけを変更します。フルスクリーンモードとレンダリングスケールは維持。
        /// </summary>
        public void SetResolution(int width, int height)
        {
            ApplyScreenSettings(width, height, currentFullScreenMode, currentRenderScale);
        }

        /// <summary>
        /// フルスクリーンモードだけを変更します。
        /// </summary>
        public void SetFullScreenMode(FullScreenMode mode)
        {
            ApplyScreenSettings(currentWidth, currentHeight, mode, currentRenderScale);
        }

        /// <summary>
        /// フルスクリーンの ON/OFF を切り替えます。
        /// Windowed / FullScreenWindow をトグルする簡易版です。
        /// </summary>
        public void ToggleFullScreen()
        {
            var nextMode = currentFullScreenMode == FullScreenMode.Windowed
                ? FullScreenMode.FullScreenWindow
                : FullScreenMode.Windowed;

            SetFullScreenMode(nextMode);
        }

        /// <summary>
        /// レンダリングスケールだけを変更します。
        /// </summary>
        public void SetRenderScale(float scale)
        {
            scale = Mathf.Clamp(scale, 0.5f, 2.0f);

#if UNITY_6000_0_OR_NEWER
            // Unity 6 の場合は DynamicResolutionHandler などを使う想定ですが、
            // 標準APIが変わる可能性もあるため、ここでは URP/HDRP 前提の RenderScale 管理を
            // プロジェクトごとに実装してもらう形にしています。
            // この記事では汎用的な実装として QualitySettings の LODBias を
            // 「なんちゃってレンダースケール」として利用しています。
            //
            // ※実際のプロジェクトでは、URP の renderScale などに差し替えてください。
            QualitySettings.lodBias = scale;
#else
            QualitySettings.lodBias = scale;
#endif
            currentRenderScale = scale;
        }

        /// <summary>
        /// 現在のレンダリングスケールを取得します。
        /// </summary>
        public float GetRenderScale()
        {
            // 上の SetRenderScale と同じロジックで値を返します。
            return QualitySettings.lodBias;
        }

        /// <summary>
        /// プリセット名で解像度を切り替えます。
        /// UIの Dropdown や Button から呼び出すと便利です。
        /// </summary>
        public void ApplyPresetByName(string presetName)
        {
            foreach (var preset in presets)
            {
                if (preset != null && preset.Name == presetName)
                {
                    ApplyScreenSettings(
                        preset.Width,
                        preset.Height,
                        currentFullScreenMode,
                        currentRenderScale
                    );
                    return;
                }
            }

            Debug.LogWarning($"[ResolutionScaler] 指定されたプリセット名が見つかりません: {presetName}");
        }

        /// <summary>
        /// インデックスでプリセットを適用します。
        /// </summary>
        public void ApplyPresetByIndex(int index)
        {
            if (presets == null || presets.Length == 0)
            {
                Debug.LogWarning("[ResolutionScaler] プリセットが設定されていません。");
                return;
            }

            if (index < 0 || index >= presets.Length)
            {
                Debug.LogWarning($"[ResolutionScaler] インデックスが範囲外です: {index}");
                return;
            }

            var preset = presets[index];
            if (preset == null)
            {
                Debug.LogWarning($"[ResolutionScaler] インデックス {index} のプリセットが null です。");
                return;
            }

            ApplyScreenSettings(
                preset.Width,
                preset.Height,
                currentFullScreenMode,
                currentRenderScale
            );
        }

        #endregion

        #region デバッグ用(任意で使う)

        /// <summary>
        /// 現在の設定をログに出力します。
        /// デバッグボタンなどに割り当てておくと便利です。
        /// </summary>
        public void LogCurrentSettings()
        {
            Debug.Log(
                $"[ResolutionScaler] " +
                $"Resolution: {currentWidth}x{currentHeight}, " +
                $"FullScreenMode: {currentFullScreenMode}, " +
                $"RenderScale(LOD Bias): {currentRenderScale:F2}"
            );
        }

        #endregion

        #region 内部クラス・属性

        [System.Serializable]
        private class ResolutionPreset
        {
            [SerializeField]
            private string name;

            [SerializeField]
            private int width;

            [SerializeField]
            private int height;

            public string Name => name;
            public int Width => width;
            public int Height => height;

            public ResolutionPreset(string name, int width, int height)
            {
                this.name = name;
                this.width = width;
                this.height = height;
            }
        }

        /// <summary>
        /// Inspector 上で値は見えるが編集はできないようにするための属性。
        /// デバッグ用の状態表示に便利です。
        /// </summary>
        private class ReadOnlyInInspectorAttribute : PropertyAttribute { }

        #if UNITY_EDITOR
        using UnityEditor;

        /// <summary>
        /// ReadOnlyInInspector 属性の描画用カスタムプロパティドローア。
        /// エディタ拡張ですが、同一ファイル内にまとめておきます。
        /// </summary>
        [CustomPropertyDrawer(typeof(ReadOnlyInInspectorAttribute))]
        private class ReadOnlyInInspectorDrawer : PropertyDrawer
        {
            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                using (new EditorGUI.DisabledScope(true))
                {
                    EditorGUI.PropertyField(position, property, label, true);
                }
            }
        }
        #endif

        #endregion
    }
}

使い方の手順

  1. コンポーネントを用意する
    上のコードを ResolutionScaler.cs という名前で保存し、Unity6 プロジェクトの Assets/ 配下に置きます。
    適当な空オブジェクト(例: SystemRoot)を作成し、ResolutionScaler をアタッチしましょう。
  2. 起動時の解像度を設定する
    ResolutionScaler を選択すると、Inspector に「起動時の基本設定」が表示されます。
    • Apply On Start:起動時に自動で解像度を変更するか
    • Initial Width / Height:起動時の解像度
    • Initial Full Screen Mode:起動時のフルスクリーンモード
    • Initial Render Scale:起動時の「なんちゃってレンダースケール」

    ここを設定しておけば、ゲーム開始時に自動で画面設定が適用されます。

  3. UI から呼び出してみる(オプションメニューの例)
    オプション画面用の Canvas を作り、以下のような UI を配置します。
    • 「1280×720」ボタン
    • 「1920×1080」ボタン
    • 「フルスクリーン切り替え」ボタン
    • レンダリングスケール用スライダー(0.5〜1.5 など)

    それぞれの OnClick / OnValueChanged に、ResolutionScaler をドラッグ&ドロップして、

    • ApplyPresetByIndex(0)ApplyPresetByIndex(1)
    • ToggleFullScreen()
    • SetRenderScale(float)

    を割り当てれば、簡単なオプションメニューが完成します。

  4. プレイヤーや敵などのゲームロジックと分離する
    プレイヤーの移動や敵AIのスクリプトには、解像度変更のコードを一切書かないようにしましょう。
    例えば「F キーでフルスクリーン切り替え」をしたい場合は、別コンポーネントを用意してこう書きます。
    
    using UnityEngine;
    
    public class FullScreenToggleInput : MonoBehaviour
    {
        [SerializeField]
        private Sample.Resolution.ResolutionScaler scaler;
    
        private void Update()
        {
            // シンプルな例として旧Inputを使用
            if (Input.GetKeyDown(KeyCode.F))
            {
                scaler.ToggleFullScreen();
            }
        }
    }
    

    こうしておけば「入力処理」と「画面設定の変更」が綺麗に分離され、後から Input System に置き換えるのも楽になります。

メリットと応用

ResolutionScaler のように、画面設定だけを担当するコンポーネントを用意しておくと、

  • プレハブ(オプション画面 UI)から簡単に画面設定を操作できる
  • シーンごとに解像度の初期値を変えたいときも、コンポーネントの値を変えるだけで済む
  • 将来 URP/HDRP のレンダリングスケールに対応したくなっても、このクラスだけ差し替えればよい
  • 入力系(キーボード / パッド / UI)と画面設定ロジックが分離され、テストしやすい

特にレベルデザインの観点では、

  • 「このシーンは重いから、初期レンダリングスケールを少し下げておこう」
  • 「タイトル画面は 4K、ゲーム本編は 1080p ベースにしよう」

といった調整を、スクリプトを触らずに Inspector からサクッと行えるのが大きなメリットです。

さらに、onScreenSettingsChanged イベントを使えば、解像度変更時に UI 表示を更新することもできます。

改造案:解像度ラベルを自動更新する

例えば、現在の解像度を UI Text に表示したい場合、以下のようなコンポーネントを追加すると便利です。


using UnityEngine;
using UnityEngine.UI;

public class ResolutionLabelUpdater : MonoBehaviour
{
    [SerializeField]
    private Sample.Resolution.ResolutionScaler scaler;

    [SerializeField]
    private Text label; // TextMeshPro を使う場合は TMP_Text に差し替え

    // ResolutionScaler の onScreenSettingsChanged にこのメソッドを登録しておく
    public void UpdateLabel()
    {
        if (label == null || scaler == null)
        {
            return;
        }

        // Screen.width / height を直接参照してもOK
        int w = Screen.width;
        int h = Screen.height;

        label.text = $"{w} x {h}";
    }
}

このように、小さな責務のコンポーネントを組み合わせていくと、
巨大な God クラスに頼らずに、拡張しやすい画面設定システムを作ることができます。コンポーネント指向で、気持ちよく解像度管理していきましょう。