【Cocos Creator】アタッチするだけ!ResolutionScaler (解像度変更)の実装方法【TypeScript】

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

【Cocos Creator 3.8】ResolutionScaler の実装:アタッチするだけでウィンドウ解像度・フルスクリーン・レンダリングスケールを制御する汎用スクリプト

本記事では、ゲームの解像度・ウィンドウサイズ・フルスクリーン・レンダリングスケールを、
1つのコンポーネントをアタッチするだけでまとめて制御できる「ResolutionScaler」 を実装します。

このコンポーネントを使うと、例えば以下のような用途に便利です:

  • PC向けビルドで、起動時のウィンドウサイズとフルスクリーン状態をまとめて設定したい
  • 低スペック環境向けに、内部レンダリング解像度だけ下げてパフォーマンスを上げたい
  • デバッグ時に、ゲーム実行中にホットキーで解像度プリセットを切り替えたい

外部の GameManager やシングルトンには一切依存せず、このスクリプト単体で完結します。
設定はすべて Inspector のプロパティから行えるように設計します。


コンポーネントの設計方針

1. 機能要件の整理

ResolutionScaler が担う機能は次の通りです。

  1. 起動時にウィンドウ解像度を設定
    ・指定した幅・高さにウィンドウをリサイズ(対応プラットフォームのみ)
  2. 起動時にフルスクリーン ON/OFF を設定
  3. レンダリングスケール(内部解像度スケール)を設定
    ・描画解像度を下げてパフォーマンスを上げたり、上げて画質を良くする
  4. 実行中にホットキーで解像度プリセットを切り替え可能(任意機能)
  5. ブラウザ / ネイティブ / エディタ再生 など環境差を考慮し、防御的に動作

Cocos Creator 3.8 では、view モジュールや game.canvas などを通して
ウィンドウサイズやフルスクリーン、解像度スケールを制御できます。
ただし、ブラウザとネイティブで挙動が異なるため、環境を判定しつつ安全に呼び出します。

2. 外部依存をなくす設計

  • Scene 全体の解像度制御ですが、特定ノードにアタッチする 1 コンポーネントとして完結させます。
  • 他のスクリプトやシングルトンには依存せず、設定はすべて @property から行います。
  • キーボード入力も、このコンポーネントが自前で登録・解除します。

3. Inspector で設定可能なプロパティ設計

ResolutionScaler で用意するプロパティと役割は以下の通りです。

  • applyOnStart: boolean
    ・起動時(start)に、以下の解像度・フルスクリーン・スケール設定を自動適用するかどうか。
    true:自動で適用 / false:スクリプトから明示的にメソッドを呼ぶときだけ適用。
  • targetWidth: number
    ・設定したい論理解像度の幅(ピクセル)。
    ・0 以下の場合は「幅は変更しない」とみなす。
  • targetHeight: number
    ・設定したい論理解像度の高さ(ピクセル)。
    ・0 以下の場合は「高さは変更しない」とみなす。
  • useFullscreen: boolean
    ・起動時にフルスクリーンを有効にするかどうか。
    ・true:フルスクリーン要求 / false:ウィンドウモード要求(可能な環境のみ)。
  • toggleFullscreenWithKey: boolean
    ・実行中にホットキーでフルスクリーンを ON/OFF できるようにするか。
  • fullscreenToggleKey: KeyCode
    ・フルスクリーン切り替えに使うキー(例:F11)。
  • renderScale: number
    ・内部レンダリング解像度のスケール。
    ・1.0 = 100%(デフォルト)、0.5 = 50%(低解像度で高速)、2.0 = 200%(高解像度で高画質)。
    ・0.1 未満の値は安全のため自動的に 0.1 にクランプ。
  • applyRenderScaleOnStart: boolean
    ・起動時に renderScale を適用するかどうか。
  • enableResolutionPresets: boolean
    ・実行中にホットキーで解像度プリセットを切り替えられるようにするか。
  • presetSwitchKey: KeyCode
    ・解像度プリセットを順送りで切り替えるためのキー(例:KeyCode.DIGIT_1)。
  • presetResolutions: Vec2[]
    ・プリセットの解像度リスト(x: 幅, y: 高さ)。
    ・例:1280×720, 1920×1080 などを複数登録しておき、ホットキーで順番に切り替える。
  • logDebugInfo: boolean
    ・解像度変更やフルスクリーン変更時に、console.log で詳細情報を出力するか。

これらのプロパティにより、ゲーム起動時の固定設定から
実行中のテスト用ホットスワップまで、幅広い用途に対応できます。


TypeScriptコードの実装

以下が完成した ResolutionScaler コンポーネントの全コードです。


import { _decorator, Component, sys, view, game, input, Input, KeyCode, EventKeyboard, Vec2, director } from 'cc';
const { ccclass, property } = _decorator;

/**
 * ResolutionScaler
 * - ウィンドウ解像度 / フルスクリーン / レンダリングスケールをまとめて制御する汎用コンポーネント
 * - 外部スクリプトへの依存なし。アタッチするだけで使用可能。
 */
@ccclass('ResolutionScaler')
export class ResolutionScaler extends Component {

    @property({
        tooltip: 'start() 時に設定を自動適用するかどうか。\nON: 起動時に targetWidth/Height, useFullscreen, renderScale を反映します。'
    })
    public applyOnStart: boolean = true;

    @property({
        tooltip: 'ターゲットの論理解像度(幅, px)。\n0 以下の場合は幅は変更しません。'
    })
    public targetWidth: number = 1920;

    @property({
        tooltip: 'ターゲットの論理解像度(高さ, px)。\n0 以下の場合は高さは変更しません。'
    })
    public targetHeight: number = 1080;

    @property({
        tooltip: '起動時にフルスクリーンを有効にするかどうか。\n実行環境によっては無視される場合があります。'
    })
    public useFullscreen: boolean = false;

    @property({
        tooltip: '実行中にホットキーでフルスクリーンを ON/OFF できるようにするか。'
    })
    public toggleFullscreenWithKey: boolean = true;

    @property({
        tooltip: 'フルスクリーン切り替えに使用するキー。\n例: F11 など。',
        type: KeyCode
    })
    public fullscreenToggleKey: KeyCode = KeyCode.F11;

    @property({
        tooltip: '内部レンダリング解像度のスケール。\n1.0 = 100% (デフォルト)\n0.5 = 50% (低解像度で高速)\n2.0 = 200% (高解像度で高画質)\n0.1 未満にはなりません。'
    })
    public renderScale: number = 1.0;

    @property({
        tooltip: 'start() 時に renderScale を適用するかどうか。'
    })
    public applyRenderScaleOnStart: boolean = true;

    @property({
        tooltip: '実行中にホットキーで解像度プリセットを切り替えられるようにするか。'
    })
    public enableResolutionPresets: boolean = false;

    @property({
        tooltip: '解像度プリセットを順送りで切り替えるためのキー。\n例: 1 キーなど。',
        type: KeyCode
    })
    public presetSwitchKey: KeyCode = KeyCode.DIGIT_1;

    @property({
        tooltip: '解像度プリセットのリスト。\n(x: 幅, y: 高さ) の形式で登録します。',
        type: [Vec2]
    })
    public presetResolutions: Vec2[] = [
        new Vec2(1280, 720),
        new Vec2(1920, 1080),
        new Vec2(2560, 1440),
    ];

    @property({
        tooltip: '解像度変更やフルスクリーン変更時に、ログを出力するかどうか。'
    })
    public logDebugInfo: boolean = true;

    // 内部状態
    private _currentPresetIndex: number = 0;
    private _isFullscreen: boolean = false;

    onLoad() {
        // 現在のフルスクリーン状態を初期化
        this._isFullscreen = this._getFullscreenStateSafe();

        // キーボードイベント登録
        input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);

        if (this.logDebugInfo) {
            console.log('[ResolutionScaler] onLoad - platform:', sys.platform, 'isNative:', sys.isNative);
        }
    }

    start() {
        if (this.applyOnStart) {
            this.applyResolutionSettings();
        }

        if (this.applyRenderScaleOnStart) {
            this.applyRenderScale();
        }
    }

    onDestroy() {
        // キーボードイベント解除
        input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
    }

    /**
     * 現在の設定に基づいて解像度とフルスクリーン設定を適用します。
     * 外部からも呼び出し可能です。
     */
    public applyResolutionSettings() {
        this._applyWindowSize();
        this._applyFullscreen(this.useFullscreen);
    }

    /**
     * 現在の renderScale を適用します。
     * 外部からも呼び出し可能です。
     */
    public applyRenderScale() {
        let scale = this.renderScale;
        if (scale < 0.1) {
            console.warn('[ResolutionScaler] renderScale が 0.1 未満のため 0.1 にクランプします:', scale);
            scale = 0.1;
        }

        // Cocos Creator 3.8 では view.setResolutionPolicy / setDesignResolutionSize などが基本ですが、
        // 内部レンダリング解像度を直接スケールする用途として view.setDesignResolutionSize を利用します。
        // ここでは現在のフレームサイズに対してスケールをかける形で実装します。
        const frameSize = view.getFrameSize();
        const designWidth = frameSize.width * scale;
        const designHeight = frameSize.height * scale;

        view.setDesignResolutionSize(designWidth, designHeight, view.getResolutionPolicy());

        if (this.logDebugInfo) {
            console.log('[ResolutionScaler] applyRenderScale - scale:', scale,
                'designSize:', designWidth, 'x', designHeight);
        }
    }

    /**
     * 解像度プリセットを次のものに切り替えます。
     */
    public switchToNextPreset() {
        if (!this.enableResolutionPresets || this.presetResolutions.length === 0) {
            return;
        }
        this._currentPresetIndex = (this._currentPresetIndex + 1) % this.presetResolutions.length;
        const preset = this.presetResolutions[this._currentPresetIndex];
        this._applySpecificResolution(preset.x, preset.y);

        if (this.logDebugInfo) {
            console.log('[ResolutionScaler] switchToNextPreset - index:', this._currentPresetIndex,
                'resolution:', preset.x, 'x', preset.y);
        }
    }

    // =========================
    // 内部実装
    // =========================

    private _onKeyDown(event: EventKeyboard) {
        const keyCode = event.keyCode;

        if (this.toggleFullscreenWithKey && keyCode === this.fullscreenToggleKey) {
            this._toggleFullscreen();
        }

        if (this.enableResolutionPresets && keyCode === this.presetSwitchKey) {
            this.switchToNextPreset();
        }
    }

    private _applyWindowSize() {
        const width = this.targetWidth;
        const height = this.targetHeight;

        if (width <= 0 && height <= 0) {
            // 何も指定されていない場合はスキップ
            if (this.logDebugInfo) {
                console.log('[ResolutionScaler] _applyWindowSize - targetWidth/Height が未指定のためスキップ');
            }
            return;
        }

        const frameSize = view.getFrameSize();
        let newWidth = width > 0 ? width : frameSize.width;
        let newHeight = height > 0 ? height : frameSize.height;

        // ブラウザとネイティブでの扱いを分ける
        if (sys.isNative) {
            // ネイティブでは view.setFrameSize が利用可能
            view.setFrameSize(newWidth, newHeight);
        } else {
            // Web プラットフォームでは canvas サイズを直接変更
            const canvas = game.canvas as HTMLCanvasElement | null;
            if (canvas) {
                canvas.width = newWidth;
                canvas.height = newHeight;
            } else {
                console.warn('[ResolutionScaler] _applyWindowSize - game.canvas が取得できませんでした');
            }
        }

        // デザイン解像度も合わせて変更(解像度ポリシーは現状維持)
        view.setDesignResolutionSize(newWidth, newHeight, view.getResolutionPolicy());

        if (this.logDebugInfo) {
            console.log('[ResolutionScaler] _applyWindowSize - new size:', newWidth, 'x', newHeight,
                'platform:', sys.platform, 'isNative:', sys.isNative);
        }
    }

    private _applySpecificResolution(width: number, height: number) {
        if (width <= 0 || height <= 0) {
            console.warn('[ResolutionScaler] _applySpecificResolution - 無効な解像度:', width, 'x', height);
            return;
        }

        if (sys.isNative) {
            view.setFrameSize(width, height);
        } else {
            const canvas = game.canvas as HTMLCanvasElement | null;
            if (canvas) {
                canvas.width = width;
                canvas.height = height;
            } else {
                console.warn('[ResolutionScaler] _applySpecificResolution - game.canvas が取得できませんでした');
            }
        }

        view.setDesignResolutionSize(width, height, view.getResolutionPolicy());
    }

    private _applyFullscreen(enable: boolean) {
        // Web / ネイティブでフルスクリーンの扱いが異なるので、view の API を介して制御します。
        if (enable) {
            this._requestFullscreenSafe();
        } else {
            this._exitFullscreenSafe();
        }
    }

    private _toggleFullscreen() {
        const current = this._getFullscreenStateSafe();
        if (current) {
            this._exitFullscreenSafe();
        } else {
            this._requestFullscreenSafe();
        }
    }

    private _requestFullscreenSafe() {
        try {
            if (sys.isNative) {
                // 一部ネイティブプラットフォームではフルスクリーン切り替えが OS 側に依存するため、
                // Cocos 側での直接制御は限定的です。ここでは view の API を試みます。
                if ((view as any).setFullscreen) {
                    (view as any).setFullscreen(true);
                } else {
                    console.warn('[ResolutionScaler] _requestFullscreenSafe - ネイティブで setFullscreen が利用できません');
                }
            } else {
                // Web: view のフルスクリーン API を使用
                if ((view as any).setFullscreen) {
                    (view as any).setFullscreen(true);
                } else if (game.canvas && (game.canvas as any).requestFullscreen) {
                    (game.canvas as any).requestFullscreen();
                } else {
                    console.warn('[ResolutionScaler] _requestFullscreenSafe - フルスクリーン API が利用できません');
                }
            }
            this._isFullscreen = true;

            if (this.logDebugInfo) {
                console.log('[ResolutionScaler] _requestFullscreenSafe - フルスクリーン要求');
            }
        } catch (e) {
            console.error('[ResolutionScaler] _requestFullscreenSafe - エラー:', e);
        }
    }

    private _exitFullscreenSafe() {
        try {
            if (sys.isNative) {
                if ((view as any).setFullscreen) {
                    (view as any).setFullscreen(false);
                } else {
                    console.warn('[ResolutionScaler] _exitFullscreenSafe - ネイティブで setFullscreen が利用できません');
                }
            } else {
                if ((view as any).setFullscreen) {
                    (view as any).setFullscreen(false);
                } else if (document.fullscreenElement && (document as any).exitFullscreen) {
                    (document as any).exitFullscreen();
                } else {
                    console.warn('[ResolutionScaler] _exitFullscreenSafe - フルスクリーン解除 API が利用できません');
                }
            }
            this._isFullscreen = false;

            if (this.logDebugInfo) {
                console.log('[ResolutionScaler] _exitFullscreenSafe - フルスクリーン解除要求');
            }
        } catch (e) {
            console.error('[ResolutionScaler] _exitFullscreenSafe - エラー:', e);
        }
    }

    private _getFullscreenStateSafe(): boolean {
        try {
            if (sys.isNative) {
                if ((view as any).isFullscreen) {
                    return (view as any).isFullscreen();
                }
                // ネイティブでは状態取得 API がない場合もあるため、内部状態を信頼
                return this._isFullscreen;
            } else {
                if ((view as any).isFullscreen) {
                    return (view as any).isFullscreen();
                }
                return !!document.fullscreenElement;
            }
        } catch {
            return this._isFullscreen;
        }
    }
}

コードの要点解説

  • onLoad
    ・現在のフルスクリーン状態を取得して内部フラグを初期化。
    input.on(KEY_DOWN) でキーボード入力を監視し、ホットキー処理を登録。
  • start
    applyOnStart が true の場合に applyResolutionSettings() を実行し、
    ウィンドウサイズとフルスクリーンを起動時に適用。
    applyRenderScaleOnStart が true の場合に applyRenderScale() を実行。
  • onDestroy
    ・登録したキーボードイベントを必ず解除し、リークや重複登録を防止。
  • applyResolutionSettings()
    ・外部からも呼べる公開メソッド。
    _applyWindowSize()_applyFullscreen() をまとめて呼び出す。
  • applyRenderScale()
    renderScale を 0.1 以上にクランプし、
    現在のフレームサイズにスケールを掛けたデザイン解像度を view.setDesignResolutionSize で設定。
  • switchToNextPreset()
    ・プリセットの解像度リストから次の解像度を選び、_applySpecificResolution() で適用。
  • _onKeyDown()
    fullscreenToggleKey が押されたらフルスクリーンをトグル。
    presetSwitchKey が押されたら解像度プリセットを切り替え。
  • _applyWindowSize()
    targetWidth/targetHeight を元に新しいウィンドウサイズを決定。
    ・ネイティブ:view.setFrameSize を使用。
    ・Web:game.canvas.width/height を直接変更。
    ・合わせて view.setDesignResolutionSize でデザイン解像度も更新。
  • _applySpecificResolution()
    ・プリセット切り替え用の内部メソッド。
    _applyWindowSize() と同様にプラットフォーム別にサイズを変更。
  • _requestFullscreenSafe() / _exitFullscreenSafe()
    ・Web / ネイティブで利用可能なフルスクリーン API を試しつつ、例外をキャッチして安全に処理。
  • _getFullscreenStateSafe()
    ・可能であれば view.isFullscreen()document.fullscreenElement から状態を取得し、
    利用できない環境では内部フラグを返す。

使用手順と動作確認

1. スクリプトファイルの作成

  1. エディタ左下の Assets パネルで任意のフォルダ(例:assets/scripts)を選択します。
  2. 右クリック → CreateTypeScript を選択します。
  3. ファイル名を ResolutionScaler.ts に変更します。
  4. 作成された ResolutionScaler.ts をダブルクリックして開き、
    既存のテンプレートコードをすべて削除して、前章のコードを丸ごと貼り付けて保存します。

2. テスト用シーン/ノードの用意

  1. Hierarchy パネルで、解像度をテストしたいシーンを開きます(例:Main.scene)。
  2. Hierarchy の何もないところを右クリック → CreateEmpty Node で空ノードを作成します。
  3. ノード名を分かりやすく ResolutionController などに変更します。

このノードは単にコンポーネントを載せるための「入れ物」です。
どのノードに付けても動作は同じですが、シーンのルート直下などに置いておくと分かりやすいです。

3. ResolutionScaler コンポーネントをアタッチ

  1. Hierarchy で先ほど作成した ResolutionController ノードを選択します。
  2. 右側の Inspector パネルで Add Component ボタンをクリックします。
  3. CustomResolutionScaler を選択して追加します。

4. Inspector プロパティの設定例

まずは基本的な PC 用フル HD 設定の例です。

  • applyOnStart: ON
  • targetWidth: 1920
  • targetHeight: 1080
  • useFullscreen: ON(PC フルスクリーン起動したい場合)
  • toggleFullscreenWithKey: ON
  • fullscreenToggleKey: F11
  • renderScale: 1.0
  • applyRenderScaleOnStart: ON
  • enableResolutionPresets: ON
  • presetSwitchKey: 1(KeyCode.DIGIT_1
  • presetResolutions:
    • Element 0: (1280, 720)
    • Element 1: (1920, 1080)
    • Element 2: (2560, 1440)
  • logDebugInfo: ON(開発中は ON 推奨)

5. エディタ上での動作確認

  1. エディタ右上の ▶ (Play) ボタンを押して、エディタ内でゲームを再生します。
  2. ゲームが起動すると、コンソールに [ResolutionScaler] 系のログが表示されます。
    • 解像度変更ログ:_applyWindowSize - new size: 1920 x 1080 ...
    • フルスクリーン要求ログ:_requestFullscreenSafe - フルスクリーン要求
    • レンダースケール適用ログ:applyRenderScale - scale: 1 ...
  3. 再生中に F11 を押すと、フルスクリーンの ON/OFF が切り替わるか確認します。
  4. 再生中に 1 キー(DIGIT_1)を押すと、1280×720 → 1920×1080 → 2560×1440 → … と解像度が順に切り替わるか確認します。

※ エディタ再生では、フルスクリーンやウィンドウサイズ変更が OS / エディタ側に制限される場合があります。
その場合は PC ビルド(Windows / macOS)Web ビルド を作成して実機で確認してください。

6. パフォーマンス用のレンダリングスケール調整例

低スペック PC 向けに、内部解像度だけ下げて FPS を稼ぎたい場合の例です。

  • targetWidth / targetHeight: 0(変更しない)
  • useFullscreen: ON
  • renderScale: 0.75(75% 解像度で描画)
  • applyRenderScaleOnStart: ON
  • enableResolutionPresets: OFF(不要なら)

この設定では、ウィンドウサイズは OS 側のデフォルトに任せつつ、
内部レンダリング解像度だけを 75% に落とすため、見た目は少しぼやけるが FPS が上がる効果が期待できます。


まとめ

ResolutionScaler コンポーネントは、

  • 起動時のウィンドウ解像度・フルスクリーン・レンダリングスケールをまとめて制御
  • 実行中にホットキーでフルスクリーンや解像度プリセットを切り替え
  • Web / ネイティブ環境差を考慮した防御的な実装

を、1 つのスクリプトをアタッチするだけで実現します。

外部の GameManager などに依存せず、すべての設定を Inspector から行えるため、
シーンごとに異なる解像度ポリシーを簡単に試せるほか、
ビルドターゲットごとのプリセットを複数用意して切り替えるといった運用も容易になります。

このコンポーネントをベースに、例えば

  • 画質設定メニュー(Low / Medium / High)の UI から renderScale やプリセットを変更
  • プラットフォーム別(モバイル / PC / Web)に異なるデフォルト解像度を自動選択

といった機能も、ResolutionScaler の公開メソッドを呼ぶだけで拡張できます。

まずは本記事のコードをそのままコピーして試し、
自分のプロジェクトに合った解像度戦略へとカスタマイズしてみてください。

Godot 4ゲーム制作 実践ドリル 100本ノック

新品価格
¥1,250から
(2025/12/13 21:27時点)

Godot4& GDScriptではじめる 2Dゲーム開発レシピ

新品価格
¥590から
(2025/12/13 21:46時点)

Unity 6 C#スクリプト 100本ノック

新品価格
¥1,230から
(2025/12/29 10:28時点)

Cocos Creator100本ノックTypeScriptで書く!

新品価格
¥1,250から
(2025/12/29 10:32時点)

URLをコピーしました!