【Cocos Creator】アタッチするだけ!SceneChanger (シーン遷移)の実装方法【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】SceneChangerの実装:ボタンにアタッチするだけで任意シーンへ遷移できる汎用スクリプト

このガイドでは、ボタンが押されたら指定したシーンへ即座に切り替えるための汎用コンポーネント SceneChanger を実装します。
Button コンポーネントを持つノードにこのスクリプトをアタッチし、インスペクタでシーン名(またはパス)を指定するだけで、他のスクリプトに一切依存せずシーン遷移を実現できます。


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

要件整理

  • このコンポーネントを Button コンポーネントを持つノード にアタッチすると、ボタンが押されたときに指定したシーンへ遷移する。
  • 外部の GameManager やシングルトンには一切依存しない。
  • 必要な情報はすべて インスペクタの @property から設定できるようにする。
  • Button コンポーネントが存在しない/シーン名が未設定など、よくあるミスに対しては エラーログや警告ログを出して防御的に実装 する。
  • シーン遷移中の多重入力を防ぐため、1度押されたら 2回目以降は無視 できるようにする。

シーン指定の考え方(Cocos Creator 3.8)

Cocos Creator 3.x では、ビルド時に登録されたシーンdirector.loadScene(sceneName) で読み込みます。

  • インスペクタで指定するのは ビルド設定に登録されたシーン名(通常はシーンアセットのファイル名)。
  • 例: assets/scenes/MainMenu.scene なら、シーン名は MainMenu
  • フォルダパスは不要で、シーン名だけを指定すれば OK です。

本コンポーネントでは、プロパティ名を分かりやすくするために targetSceneName とします。

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

今回の SceneChanger コンポーネントで用意するプロパティとその役割は以下の通りです。

  • targetSceneName: string
    • 遷移先のシーン名。
    • 必須。空文字のままだと遷移せず、コンソールにエラーログを出します。
    • 例: MainMenu, Game, Result など。
  • autoBindButton: boolean
    • アタッチ先ノードの Button コンポーネントに自動でクリックイベントを登録するかどうか。
    • true の場合、onLoad 時に Button の clickEvents に自分自身を登録します。
    • 既に手動でイベントを設定している場合は、false にしておくと二重登録を防げます。
    • デフォルト: true
  • allowMultipleClick: boolean
    • ボタンを連打したときに、複数回シーン遷移処理を走らせるかどうか
    • false の場合、1回クリックして遷移を開始したら、2回目以降のクリックは無視します。
    • デフォルト: false(多重遷移防止のため推奨)
  • logOnClick: boolean
    • クリック時にコンソールへログを出すかどうか。
    • デバッグ時に有効にすると、どのシーンへ遷移しようとしているか確認できます。
    • デフォルト: true
  • logOnError: boolean
    • シーン名未設定、Button 未取得、ロード失敗などのエラーをコンソールへ出すかどうか。
    • デフォルト: true

内部状態として、以下のようなフラグを持たせます(インスペクタには出しません)。

  • _isLoading: boolean
    • 既にシーンロードを開始しているかどうかを記録。
    • allowMultipleClick === false のときに多重クリックを防ぐために使用。

TypeScriptコードの実装

以下が、Cocos Creator 3.8.7 用の SceneChanger.ts の完全な実装例です。


import { _decorator, Component, Node, Button, EventHandler, director } from 'cc';
const { ccclass, property } = _decorator;

/**
 * SceneChanger
 * 
 * ボタンがクリックされたときに、指定したシーンへ遷移する汎用コンポーネント。
 * 
 * 使用方法:
 * - Button コンポーネントを持つノードにアタッチし、
 *   targetSceneName に遷移先シーン名を設定するだけで動作します。
 */
@ccclass('SceneChanger')
export class SceneChanger extends Component {

    @property({
        tooltip: '遷移先のシーン名(ビルド設定に登録されているシーン名を入力)\n例: MainMenu, Game, Result'
    })
    public targetSceneName: string = '';

    @property({
        tooltip: 'true の場合、このノードの Button コンポーネントに自動でクリックイベントを登録します。\n既に手動でイベントを設定している場合は false にしてください。'
    })
    public autoBindButton: boolean = true;

    @property({
        tooltip: 'true の場合、ボタン連打で複数回シーンロードを試みます。\nfalse の場合、1回目のクリック後はロード完了まで後続のクリックを無視します。'
    })
    public allowMultipleClick: boolean = false;

    @property({
        tooltip: 'true の場合、クリック時にどのシーンへ遷移するかログを出力します。'
    })
    public logOnClick: boolean = true;

    @property({
        tooltip: 'true の場合、Button 未設定やシーン名未設定などのエラーをログ出力します。'
    })
    public logOnError: boolean = true;

    /** 既にシーンロードを開始しているかどうか */
    private _isLoading: boolean = false;

    /** キャッシュしておく Button コンポーネント参照 */
    private _button: Button | null = null;

    onLoad() {
        // Button コンポーネントを取得
        this._button = this.getComponent(Button);

        if (!this._button) {
            if (this.logOnError) {
                console.error(
                    `[SceneChanger] Button コンポーネントが見つかりません。` +
                    `Node 名: "${this.node.name}".\n` +
                    `このノードに Button コンポーネントを追加してから SceneChanger を使用してください。`
                );
            }
            return;
        }

        // autoBindButton が有効な場合、クリックイベントを自動登録
        if (this.autoBindButton) {
            this._bindClickEventIfNeeded();
        }
    }

    /**
     * Button の clickEvents に自分自身の onClick メソッドを登録する。
     * すでに同じ EventHandler が登録されている場合は重複登録しない。
     */
    private _bindClickEventIfNeeded() {
        if (!this._button) {
            return;
        }

        const clickEvents = this._button.clickEvents;

        // すでに同じターゲット・コンポーネント・メソッド名のイベントがあるかチェック
        const hasSameHandler = clickEvents.some((event) => {
            return event.target === this.node
                && event.component === 'SceneChanger'
                && event.handler === 'onClick';
        });

        if (hasSameHandler) {
            // すでに登録済みなら何もしない
            return;
        }

        const handler = new EventHandler();
        handler.target = this.node;
        // コンポーネント名(@ccclass の文字列)を指定
        handler.component = 'SceneChanger';
        handler.handler = 'onClick';

        clickEvents.push(handler);
    }

    /**
     * Button から呼ばれるクリックイベントハンドラ。
     * - autoBindButton = true の場合、自動的に Button に登録されます。
     * - autoBindButton = false の場合は、Button のインスペクタから手動で設定してください。
     */
    public onClick() {
        // シーン名が設定されているかチェック
        if (!this.targetSceneName || this.targetSceneName.trim().length === 0) {
            if (this.logOnError) {
                console.error(
                    `[SceneChanger] targetSceneName が設定されていません。` +
                    `Node 名: "${this.node.name}".\n` +
                    `インスペクタで遷移先のシーン名を指定してください。`
                );
            }
            return;
        }

        // 多重ロード防止
        if (!this.allowMultipleClick && this._isLoading) {
            if (this.logOnClick) {
                console.log(
                    `[SceneChanger] 既にシーン "${this.targetSceneName}" のロードを開始しています。` +
                    `後続のクリックは無視されます。`
                );
            }
            return;
        }

        this._isLoading = true;

        if (this.logOnClick) {
            console.log(
                `[SceneChanger] ボタンがクリックされました。` +
                `シーン "${this.targetSceneName}" へ遷移します。`
            );
        }

        // シーン遷移開始
        director.loadScene(this.targetSceneName, (error?: Error | null) => {
            // コールバックはシーンロード完了時に呼ばれます。
            if (error) {
                if (this.logOnError) {
                    console.error(
                        `[SceneChanger] シーン "${this.targetSceneName}" のロードに失敗しました。`,
                        error
                    );
                }
                // 失敗した場合は再度クリックを許可する
                this._isLoading = false;
            } else {
                if (this.logOnClick) {
                    console.log(
                        `[SceneChanger] シーン "${this.targetSceneName}" のロードが完了しました。`
                    );
                }
                // 正常にシーンが切り替わると、このコンポーネントは破棄される想定
            }
        });
    }
}

コードのポイント解説

  • onLoad()
    • アタッチ先ノードから Button コンポーネントを取得します。
    • 見つからなかった場合は console.error でエラーログを出し、以降の処理は行いません。
    • autoBindButtontrue なら、_bindClickEventIfNeeded() を呼んでクリックイベントを自動登録します。
  • _bindClickEventIfNeeded()
    • Button.clickEventsEventHandler を追加し、onClick() を呼ぶように設定します。
    • 同じノード・コンポーネント名・メソッド名のハンドラが既にある場合は、重複登録しないようにチェックしています。
  • onClick()
    • Button から呼ばれるクリックイベントハンドラです。
    • targetSceneName が未設定(空文字)の場合はエラーログを出して処理を中断します。
    • allowMultipleClick === false かつ _isLoading === true の場合、多重クリックを無視します。
    • director.loadScene でシーンロードを開始し、完了時にログを出します。
    • ロード失敗時にはエラーログを出し、_isLoadingfalse に戻して再クリックを許可します。

使用手順と動作確認

ここからは、実際に Cocos Creator 3.8.7 のエディタで SceneChanger を使う手順を説明します。

1. スクリプトファイルを作成する

  1. エディタの Assets パネルで、スクリプトを置きたいフォルダ(例: assets/scripts)を右クリックします。
  2. Create > TypeScript を選択し、ファイル名を SceneChanger.ts にします。
  3. 作成された SceneChanger.ts をダブルクリックして開き、中身をすべて削除してから、先ほどのコードを丸ごと貼り付けて保存します。

2. テスト用のシーンとボタンを用意する

  1. シーンを2つ以上用意 します。
    • 例: MainMenu.sceneGame.scene
    • まだない場合は File > New Scene で作成し、Assets に保存します。
  2. ビルド設定にシーンを登録します。
    1. メニューから Project > Project Settings… を開きます。
    2. 左メニューから Scenes を選択します。
    3. Add ボタンで MainMenu.sceneGame.scene を追加します。
    4. ここで表示される Scene Name が、targetSceneName に設定する文字列になります(通常はファイル名)。
  3. MainMenu.scene を開き、ボタン用のノードを作成します。
    1. Hierarchy パネルで右クリック > Create > UI > Button を選択します。
    2. 作成された Button ノードの名前を ToGameButton など分かりやすいものに変更します。

3. SceneChanger コンポーネントをアタッチする

  1. Hierarchy で先ほど作成した Button ノード(例: ToGameButton)を選択します。
  2. Inspector パネルの下部で Add Component をクリックします。
  3. Custom カテゴリを開き、一覧から SceneChanger を選択します。
    • もし Custom に表示されない場合は、スクリプトの保存忘れやコンパイルエラーがないか確認してください。

4. プロパティを設定する

Inspector で SceneChanger のプロパティを次のように設定します。

  • Target Scene NametargetSceneName
    • 例: Game
    • ビルド設定の Scenes に登録したシーン名と一致させてください。
  • Auto Bind ButtonautoBindButton
    • true のままで OK です。
    • これにより、Button の Click Events に自動で SceneChanger.onClick が登録されます。
  • Allow Multiple ClickallowMultipleClick
    • 基本的には false のままを推奨します(連打で多重ロードしないように)。
  • Log On ClicklogOnClick
    • デバッグ中は true にしておくと、クリック時のログが確認できて便利です。
  • Log On ErrorlogOnError
    • true のままを推奨します。設定ミスをすぐに発見できます。

設定後、同じノードにアタッチされている Button コンポーネントを確認すると、Click Events のリストに SceneChanger.onClick が自動登録されているはずです。

5. 実行して動作確認する

  1. エディタ右上の Play ボタン(プレビュー)をクリックして、MainMenu.scene を実行します。
    • もし別のシーンが起動する場合は、そのシーンを閉じて MainMenu.scene を開き直してから Play してください。
  2. ゲーム画面が表示されたら、先ほど作成したボタン(ToGameButton)をクリックします。
  3. クリックすると、Game シーンに切り替われば成功です。
  4. コンソールログ(Console パネル)を開き、以下のようなログが出ていることも確認してみてください。
    • [SceneChanger] ボタンがクリックされました。シーン "Game" へ遷移します。
    • [SceneChanger] シーン "Game" のロードが完了しました。

6. よくあるエラーと対処

  • クリックしても何も起きない
    • Console に以下のようなエラーが出ていないか確認します。
      • [SceneChanger] Button コンポーネントが見つかりません。
        • → ボタンノードに Button コンポーネント が付いているか確認してください。
      • [SceneChanger] targetSceneName が設定されていません。
        • targetSceneName にシーン名を入力してください。
      • [SceneChanger] シーン "XXXX" のロードに失敗しました。
        • → ビルド設定の Scenes にそのシーンが追加されているか、シーン名のスペルミスがないか確認してください。
  • クリックイベントが二重に発火している気がする
    • Button の Click Events に、SceneChanger.onClick が複数登録されていないか確認します。
    • 手動でイベントを設定している場合は、autoBindButtonfalse にし、自動登録を無効にしてください。

まとめ

SceneChanger コンポーネントを使うことで、

  • ボタンにスクリプトを 1つアタッチしてシーン名を入力するだけで、シーン遷移を実装できる。
  • 外部の GameManager やシングルトンに依存せず、完全に独立した再利用可能コンポーネント として扱える。
  • Button 未設定、シーン名未設定、多重クリックなどの典型的なミスを、ログと防御的実装で安全にハンドリングできる。

このパターンを応用すれば、例えば

  • 「確認ダイアログを挟んでからシーン遷移する」コンポーネント
  • 「フェードアウト演出を再生してからシーンをロードする」コンポーネント
  • 「ロード中はボタンを自動で無効化する」コンポーネント

といったバリエーションも、同じく 1コンポーネント完結 の設計で量産できます。
まずは今回の SceneChanger をプロジェクト内の各種ボタンに適用し、シーン遷移処理の共通化・簡素化を体感してみてください。

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