【Cocos Creator】アタッチするだけ!ScreenshotTaker (スクショ撮影)の実装方法【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】ScreenshotTakerの実装:アタッチするだけで「F12キーで画面スクショを撮って日時ファイル名で保存」できる汎用スクリプト

この記事では、任意のノードにアタッチするだけで「指定キー(デフォルトF12)を押すと、現在のゲーム画面をキャプチャし、日時付きファイル名で保存する」汎用コンポーネント ScreenshotTaker を実装します。
ゲームのデバッグ用スクリーンショット、プレイヤー向けの撮影機能、演出確認などに便利で、他のカスタムスクリプトに一切依存しない「単体完結型」のコンポーネントです。


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

機能要件の整理

  • 指定したキー(デフォルトは F12)を押すと、そのフレームのレンダリング結果を画像として取得する。
  • 取得した画像を「日時付きファイル名」で保存する。
  • エディタ上(プレビュー)とビルド後(ブラウザ / ネイティブ)で、可能な範囲で動作するように設計する。
  • 他のカスタムスクリプトやシングルトンに依存せず、このコンポーネント単体で完結する。
  • インスペクタで挙動を細かく制御できるようにする。

プラットフォーム別の挙動設計

Cocos Creator 3.8 では、フレームバッファから画像データを取得するために screen / director.root.framebuffer などを利用できますが、画像の「ファイル保存」方法はプラットフォームごとに異なります

  • Web / エディタプレビュー: HTMLCanvasElement から toDataURL を用いて PNG データURLを生成し、<a download> クリックでダウンロードさせる。
  • ネイティブ(Android / iOS / Windows / macOS ビルド): Cocos の標準APIだけでは直接ファイルに書き出せないため、本コンポーネントでは「一旦 PNG バイナリをログに出す」か「未実装で警告を出す」方針とし、必要に応じて後からネイティブプラグインで拡張できるように実装する。

本記事では、Web/エディタでの実用性を重視しつつ、ネイティブ環境では「スクリーンショット処理をスキップし、警告ログを出す」防御的な実装にします。

インスペクタで設定可能なプロパティ設計

ScreenshotTaker コンポーネントのプロパティは次のように設計します。

  • enabledInEditorPreview: boolean
    – エディタプレビュー再生中(Playボタンで再生した状態)でもスクリーンショットを撮れるようにするかどうか。
    – デフォルト: true
  • captureKeyCode: number
    – スクリーンショットを撮影するキーの KeyCode(Cocosの KeyCode enum の値)。
    – デフォルト: KeyCode.F12
    – 例: F12以外にしたい場合は、KeyCode.P などの数値を指定。
  • fileNamePrefix: string
    – 出力ファイル名の先頭に付けるプレフィックス。
    – デフォルト: "screenshot_"
    – 例: "battle_" とすると battle_2025-01-01_23-59-59.png のような名前で保存される。
  • useLocalTime: boolean
    – 日時の生成にローカルタイム(new Date())を使うか、UTCを使うか。
    – デフォルト: true(ローカルタイム)
  • includeMilliseconds: boolean
    – ファイル名にミリ秒まで含めるかどうか。連写したい場合の衝突回避用。
    – デフォルト: false
  • imageMimeType: string
    – 生成する画像の MIME タイプ。多くのブラウザでは image/png または image/jpeg を指定可能。
    – デフォルト: "image/png"
  • jpegQuality: number
    – JPEG 形式使用時の品質(0〜1)。PNG では無視される。
    – デフォルト: 0.92
  • logOnSuccess: boolean
    – スクリーンショット保存に成功したときにログを出すかどうか。
    – デフォルト: true
  • logOnFailure: boolean
    – 何らかの理由でスクリーンショットに失敗したときに警告ログを出すかどうか。
    – デフォルト: true

キーコードは TypeScript コード上では KeyCode.F12 などと書きますが、インスペクタ上では数値として表示されます。
デフォルト値はコード側で F12 に設定しておくので、基本的には変更不要です。


TypeScriptコードの実装

以下が完成した ScreenshotTaker.ts の全コードです。


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

/**
 * ScreenshotTaker
 * 指定キーを押したときに現在の画面をキャプチャし、
 * Web環境では日時付きファイル名でダウンロードさせる汎用コンポーネント。
 *
 * 注意:
 * - Web / Editor プレビューで実用的に動作します。
 * - ネイティブビルドでは、標準APIのみでは直接ファイル保存ができないため、
 *   本コンポーネントは警告ログを出して処理をスキップします。
 */
@ccclass('ScreenshotTaker')
export class ScreenshotTaker extends Component {

    @property({
        tooltip: 'エディタのプレビュー再生中でもスクリーンショットを撮れるようにするかどうか。',
    })
    public enabledInEditorPreview: boolean = true;

    @property({
        tooltip: 'スクリーンショット撮影に使用するキーコード。\nデフォルトは F12 キー。',
    })
    public captureKeyCode: number = KeyCode.F12;

    @property({
        tooltip: '出力ファイル名の先頭に付けるプレフィックス。\n例: "screenshot_" → screenshot_2025-01-01_12-00-00.png',
    })
    public fileNamePrefix: string = 'screenshot_';

    @property({
        tooltip: '日時文字列の生成にローカルタイムを使用するかどうか。\nfalse の場合は UTC で生成します。',
    })
    public useLocalTime: boolean = true;

    @property({
        tooltip: 'ファイル名にミリ秒まで含めるかどうか。\n連写する場合の衝突回避に有効です。',
    })
    public includeMilliseconds: boolean = false;

    @property({
        tooltip: '画像の MIME タイプ。\n通常は "image/png" か "image/jpeg" を指定します。',
    })
    public imageMimeType: string = 'image/png';

    @property({
        tooltip: 'JPEG 形式使用時の品質(0〜1)。\nPNG の場合は無視されます。',
        min: 0,
        max: 1,
        step: 0.01,
    })
    public jpegQuality: number = 0.92;

    @property({
        tooltip: 'スクリーンショット保存に成功したときにログを出力するかどうか。',
    })
    public logOnSuccess: boolean = true;

    @property({
        tooltip: 'スクリーンショットに失敗したときに警告ログを出力するかどうか。',
    })
    public logOnFailure: boolean = true;

    /**
     * onLoad:
     * - キーボード入力リスナーを登録します。
     * - Editor 環境での動作可否を判定します。
     */
    onLoad() {
        // Editor上でも、ゲームを再生しているときだけ動かす
        if (!this.enabledInEditorPreview && sys.isEditor) {
            if (this.logOnSuccess || this.logOnFailure) {
                console.log('[ScreenshotTaker] Editor プレビューでは無効化されています。');
            }
            return;
        }

        input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);
    }

    /**
     * onDestroy:
     * - キーボード入力リスナーを解除します。
     */
    onDestroy() {
        input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
    }

    /**
     * キー入力イベントハンドラ。
     * 指定されたキーコードが押されたときにスクリーンショットを撮影します。
     */
    private _onKeyDown(event: EventKeyboard) {
        if (event.keyCode === this.captureKeyCode) {
            this.captureScreen();
        }
    }

    /**
     * スクリーンショットを撮影し、可能であればファイルとして保存(ダウンロード)します。
     */
    public captureScreen() {
        // ネイティブ環境では標準APIで直接ファイル保存できないため、警告を出して終了
        if (sys.isNative) {
            if (this.logOnFailure) {
                console.warn('[ScreenshotTaker] ネイティブ環境では標準APIのみでのスクリーンショット保存は未実装です。' +
                    '必要に応じてネイティブプラグインで拡張してください。');
            }
            return;
        }

        // Web / Editor 環境のみサポート
        if (!sys.isBrowser && !sys.isEditor) {
            if (this.logOnFailure) {
                console.warn('[ScreenshotTaker] このプラットフォームではスクリーンショット保存はサポートされていません。');
            }
            return;
        }

        // director.root?.framebuffer を使って描画内容を取得する方法もありますが、
        // Web 環境では game.canvas をそのまま利用するのが簡便です。
        const anyGame: any = game as any;
        const canvas: HTMLCanvasElement | null = anyGame.canvas ?? null;

        if (!canvas) {
            if (this.logOnFailure) {
                console.warn('[ScreenshotTaker] Canvas が取得できません。スクリーンショットに失敗しました。');
            }
            return;
        }

        try {
            const mime = this.imageMimeType || 'image/png';
            let dataUrl: string;

            if (mime === 'image/jpeg' || mime === 'image/jpg') {
                const quality = this._clamp(this.jpegQuality, 0, 1);
                dataUrl = canvas.toDataURL('image/jpeg', quality);
            } else {
                // デフォルトは PNG
                dataUrl = canvas.toDataURL('image/png');
            }

            const fileName = this._buildFileName();
            this._downloadDataUrl(dataUrl, fileName);

            if (this.logOnSuccess) {
                console.log(`[ScreenshotTaker] スクリーンショットを保存しました: ${fileName}`);
            }
        } catch (e) {
            if (this.logOnFailure) {
                console.warn('[ScreenshotTaker] スクリーンショットの取得中にエラーが発生しました:', e);
            }
        }
    }

    /**
     * 日時付きファイル名を生成します。
     * 例: screenshot_2025-01-01_23-59-59.png
     */
    private _buildFileName(): string {
        const now = new Date();
        let year: number;
        let month: number;
        let day: number;
        let hour: number;
        let minute: number;
        let second: number;
        let ms: number;

        if (this.useLocalTime) {
            year = now.getFullYear();
            month = now.getMonth() + 1;
            day = now.getDate();
            hour = now.getHours();
            minute = now.getMinutes();
            second = now.getSeconds();
            ms = now.getMilliseconds();
        } else {
            year = now.getUTCFullYear();
            month = now.getUTCMonth() + 1;
            day = now.getUTCDate();
            hour = now.getUTCHours();
            minute = now.getUTCMinutes();
            second = now.getUTCSeconds();
            ms = now.getUTCMilliseconds();
        }

        const pad2 = (n: number) => n.toString().padStart(2, '0');
        const pad3 = (n: number) => n.toString().padStart(3, '0');

        let timestamp = `${year}-${pad2(month)}-${pad2(day)}_${pad2(hour)}-${pad2(minute)}-${pad2(second)}`;
        if (this.includeMilliseconds) {
            timestamp += `-${pad3(ms)}`;
        }

        const prefix = this.fileNamePrefix || 'screenshot_';
        const ext = this._getExtensionFromMime();

        return `${prefix}${timestamp}.${ext}`;
    }

    /**
     * MIMEタイプから拡張子を推定します。
     */
    private _getExtensionFromMime(): string {
        const mime = (this.imageMimeType || '').toLowerCase();
        if (mime.includes('jpeg') || mime.includes('jpg')) {
            return 'jpg';
        }
        if (mime.includes('png')) {
            return 'png';
        }
        if (mime.includes('webp')) {
            return 'webp';
        }
        // デフォルト
        return 'png';
    }

    /**
     * DataURL をダウンロードさせる (Web / Editor 専用)。
     */
    private _downloadDataUrl(dataUrl: string, fileName: string) {
        // Editor のプレビュー再生も実際にはブラウザ環境なので、そのまま DOM API を利用できます。
        const a = document.createElement('a');
        a.href = dataUrl;
        a.download = fileName;
        // 一部ブラウザでは DOM に追加しないと動かないケースがあるため、一時的に追加
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
    }

    /**
     * 数値を min〜max にクランプします。
     */
    private _clamp(v: number, min: number, max: number): number {
        if (v < min) return min;
        if (v > max) return max;
        return v;
    }
}

主要な処理のポイント解説

  • onLoad / onDestroy
    onLoad でキーボード入力イベントを登録し、onDestroy で確実に解除しています。
    enabledInEditorPreviewfalse かつ sys.isEditor の場合は、エディタプレビューでは何もしません。
  • _onKeyDown
    – 押されたキーの keyCodecaptureKeyCode(デフォルト F12)と一致したときに captureScreen() を呼びます。
  • captureScreen
    – ネイティブ環境では標準APIだけでファイル保存できないため、警告ログを出して終了する防御的実装。
    – Web / Editor では game.canvas(内部的には HTMLCanvasElement)を取得し、toDataURL() で画像データURLを生成。
    – MIMEタイプが JPEG の場合のみ jpegQuality を使用し、それ以外は PNG として出力します。
    _buildFileName() で日時付きファイル名を作成し、_downloadDataUrl() でブラウザのダウンロードをトリガーします。
  • _buildFileName
    – ローカルタイム / UTC を切り替え可能。
    includeMilliseconds が true の場合は xxx-SSS の形式でミリ秒も付与し、連写時のファイル名衝突を避けます。
  • _downloadDataUrl
    <a> 要素を一時的に生成し、download 属性にファイル名を設定して click() を呼ぶことで、ブラウザにダウンロードさせています。
    – Editor プレビューもブラウザ上で動作するため、この方法で問題なく動きます。

使用手順と動作確認

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

  1. Assets パネルで右クリックします。
  2. Create → TypeScript を選択します。
  3. ファイル名を ScreenshotTaker.ts に変更します。
  4. 作成した ScreenshotTaker.ts をダブルクリックしてエディタ(VSCodeなど)で開き、先ほどのコードを全て貼り付けて保存します。

2. テスト用ノードの作成

  1. Hierarchy パネルで右クリック → Create → UI → Canvas(既にある場合は不要)。
  2. Canvas の子として、Create → UI → Sprite など、画面に変化が分かりやすいノードを1つ作成します。
  3. 背景やボタンなど、何かしら画面に表示されるものを用意しておくとテストしやすいです。

3. ScreenshotTaker コンポーネントのアタッチ

  1. スクリーンショット機能を管理したいノードを選択します。
    おすすめは Canvas ルートノード です(常にシーンに存在するため)。
  2. Inspector パネルの下部にある Add Component ボタンをクリックします。
  3. Custom カテゴリの中から ScreenshotTaker を選択して追加します。

4. インスペクタでプロパティを設定

Inspector に表示される ScreenshotTaker のプロパティを確認し、必要に応じて調整します。

  • Enabled In Editor Preview: チェックを入れたまま(true)にしておくと、エディタの再生中でも F12 で撮影できます。
  • Capture Key Code: デフォルトで F12 の数値が入っています。そのままで問題ありません。
    例えば P キーにしたい場合は、コード側で KeyCode.P をデフォルト値として書き換えるか、インスペクタ上で対応する数値に変更します。
  • File Name Prefix: 例として battle_debug_ などに変更すると、ファイル名が分かりやすくなります。
  • Use Local Time: 日本時間などローカルタイムで良ければチェックしたままでOKです。
  • Include Milliseconds: 連写する予定がある場合はチェックを入れておくと、ファイル名の重複を避けやすくなります。
  • Image Mime Type: デフォルトの image/png のままがおすすめです。
    軽量な JPEG が欲しい場合は image/jpeg に変更します。
  • Jpeg Quality: JPEG 使用時のみ有効です。画質とファイルサイズのバランスを見て 0.7〜0.95 くらいに調整してください。
  • Log On Success / Log On Failure: 動作確認中はどちらもチェック ON にしておくと、Console で状況が分かりやすくなります。

5. 実際に動かして確認する

  1. エディタ上部の Play ボタンを押して、ゲームをプレビュー再生します。
  2. ゲーム画面が表示された状態で、キーボードの F12 キーを押します。
  3. ブラウザ(またはエディタ内ブラウザ)で、ファイルのダウンロードダイアログが表示されるか、自動的にファイルがダウンロードされるはずです。
  4. ダウンロードされたファイル名が、設定した File Name Prefix と日時になっているか確認します。
    例: screenshot_2025-01-01_23-59-59.png
  5. 画像を開き、実際にプレイ中の画面が正しくキャプチャされているかを確認してください。
  6. Console(開発者ツール)に [ScreenshotTaker] スクリーンショットを保存しました: ... というログが出力されていれば成功です。

6. ビルド後の挙動について

  • Web ビルド: エディタプレビューと同様に、ブラウザ環境で動作し、F12(または指定キー)押下で画像ダウンロードが行われます。
    ※ ブラウザのデフォルトの F12(開発者ツール)と競合する場合があります。その場合は Capture Key Code を別のキーに変更してください(例: KeyCode.P)。
  • ネイティブビルド(Android / iOS / Windows / macOS): 本コンポーネントは標準APIのみを使用しているため、ファイル保存は行わず、警告ログを出すだけの挙動になります。
    実際に端末のストレージへ保存したい場合は、C++/Java/Objective-C などでネイティブプラグインを実装し、このコンポーネントから呼び出す形で拡張してください。

まとめ

本記事では、Cocos Creator 3.8.7 / TypeScript で、他のノードやカスタムスクリプトに依存しない「単体完結型」のスクリーンショットコンポーネント ScreenshotTaker を実装しました。

  • 任意のノード(推奨: Canvas)にアタッチするだけで、指定キー(デフォルト F12)で画面キャプチャが可能。
  • ファイル名は プレフィックス + 日時 で自動生成され、ローカルタイム / UTC やミリ秒の有無も切り替え可能。
  • Web / エディタ環境では、その場で PNG / JPEG ファイルとしてダウンロードされるため、デバッグ用・プレイヤー向け機能としてすぐに活用できる。
  • ネイティブ環境では防御的に未実装扱いとし、必要に応じてネイティブプラグインで拡張できる設計。

このように、インスペクタだけで設定が完結する汎用コンポーネントを用意しておくと、シーンごとに同じ機能を何度も実装する手間が省け、ゲーム開発の効率が大きく向上します。
今回の ScreenshotTaker をベースに、例えば「特定のUIを一時的に非表示にしてから撮影する」「撮影時にフラッシュ演出を入れる」など、演出付きのスクリーンショット機能へと発展させることも容易です。

まずはこの記事のコードをそのままコピーし、F12を押して実際にスクリーンショットが保存される体験をしてみてください。そのうえで、プロジェクトに合わせてプレフィックスやキー設定をカスタマイズしていくと、より使い勝手の良い撮影環境が構築できます。

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をコピーしました!