【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でエラーログを出し、以降の処理は行いません。 autoBindButtonがtrueなら、_bindClickEventIfNeeded()を呼んでクリックイベントを自動登録します。
- アタッチ先ノードから
- _bindClickEventIfNeeded()
Button.clickEventsにEventHandlerを追加し、onClick()を呼ぶように設定します。- 同じノード・コンポーネント名・メソッド名のハンドラが既にある場合は、重複登録しないようにチェックしています。
- onClick()
- Button から呼ばれるクリックイベントハンドラです。
targetSceneNameが未設定(空文字)の場合はエラーログを出して処理を中断します。allowMultipleClick === falseかつ_isLoading === trueの場合、多重クリックを無視します。director.loadSceneでシーンロードを開始し、完了時にログを出します。- ロード失敗時にはエラーログを出し、
_isLoadingをfalseに戻して再クリックを許可します。
使用手順と動作確認
ここからは、実際に Cocos Creator 3.8.7 のエディタで SceneChanger を使う手順を説明します。
1. スクリプトファイルを作成する
- エディタの Assets パネルで、スクリプトを置きたいフォルダ(例:
assets/scripts)を右クリックします。 - Create > TypeScript を選択し、ファイル名を
SceneChanger.tsにします。 - 作成された
SceneChanger.tsをダブルクリックして開き、中身をすべて削除してから、先ほどのコードを丸ごと貼り付けて保存します。
2. テスト用のシーンとボタンを用意する
- シーンを2つ以上用意 します。
- 例:
MainMenu.sceneとGame.scene - まだない場合は File > New Scene で作成し、Assets に保存します。
- 例:
- ビルド設定にシーンを登録します。
- メニューから Project > Project Settings… を開きます。
- 左メニューから Scenes を選択します。
- Add ボタンで
MainMenu.sceneとGame.sceneを追加します。 - ここで表示される Scene Name が、
targetSceneNameに設定する文字列になります(通常はファイル名)。
MainMenu.sceneを開き、ボタン用のノードを作成します。- Hierarchy パネルで右クリック > Create > UI > Button を選択します。
- 作成された Button ノードの名前を
ToGameButtonなど分かりやすいものに変更します。
3. SceneChanger コンポーネントをアタッチする
- Hierarchy で先ほど作成した Button ノード(例:
ToGameButton)を選択します。 - Inspector パネルの下部で Add Component をクリックします。
- Custom カテゴリを開き、一覧から SceneChanger を選択します。
- もし Custom に表示されない場合は、スクリプトの保存忘れやコンパイルエラーがないか確認してください。
4. プロパティを設定する
Inspector で SceneChanger のプロパティを次のように設定します。
- Target Scene Name(
targetSceneName)- 例:
Game - ビルド設定の Scenes に登録したシーン名と一致させてください。
- 例:
- Auto Bind Button(
autoBindButton)trueのままで OK です。- これにより、Button の Click Events に自動で
SceneChanger.onClickが登録されます。
- Allow Multiple Click(
allowMultipleClick)- 基本的には
falseのままを推奨します(連打で多重ロードしないように)。
- 基本的には
- Log On Click(
logOnClick)- デバッグ中は
trueにしておくと、クリック時のログが確認できて便利です。
- デバッグ中は
- Log On Error(
logOnError)trueのままを推奨します。設定ミスをすぐに発見できます。
設定後、同じノードにアタッチされている Button コンポーネントを確認すると、Click Events のリストに SceneChanger.onClick が自動登録されているはずです。
5. 実行して動作確認する
- エディタ右上の Play ボタン(プレビュー)をクリックして、
MainMenu.sceneを実行します。- もし別のシーンが起動する場合は、そのシーンを閉じて
MainMenu.sceneを開き直してから Play してください。
- もし別のシーンが起動する場合は、そのシーンを閉じて
- ゲーム画面が表示されたら、先ほど作成したボタン(
ToGameButton)をクリックします。 - クリックすると、
Gameシーンに切り替われば成功です。 - コンソールログ(Console パネル)を開き、以下のようなログが出ていることも確認してみてください。
[SceneChanger] ボタンがクリックされました。シーン "Game" へ遷移します。[SceneChanger] シーン "Game" のロードが完了しました。
6. よくあるエラーと対処
- クリックしても何も起きない
- Console に以下のようなエラーが出ていないか確認します。
[SceneChanger] Button コンポーネントが見つかりません。- → ボタンノードに Button コンポーネント が付いているか確認してください。
[SceneChanger] targetSceneName が設定されていません。- →
targetSceneNameにシーン名を入力してください。
- →
[SceneChanger] シーン "XXXX" のロードに失敗しました。- → ビルド設定の Scenes にそのシーンが追加されているか、シーン名のスペルミスがないか確認してください。
- Console に以下のようなエラーが出ていないか確認します。
- クリックイベントが二重に発火している気がする
- Button の Click Events に、
SceneChanger.onClickが複数登録されていないか確認します。 - 手動でイベントを設定している場合は、
autoBindButtonをfalseにし、自動登録を無効にしてください。
- Button の Click Events に、
まとめ
SceneChanger コンポーネントを使うことで、
- ボタンにスクリプトを 1つアタッチしてシーン名を入力するだけで、シーン遷移を実装できる。
- 外部の
GameManagerやシングルトンに依存せず、完全に独立した再利用可能コンポーネント として扱える。 - Button 未設定、シーン名未設定、多重クリックなどの典型的なミスを、ログと防御的実装で安全にハンドリングできる。
このパターンを応用すれば、例えば
- 「確認ダイアログを挟んでからシーン遷移する」コンポーネント
- 「フェードアウト演出を再生してからシーンをロードする」コンポーネント
- 「ロード中はボタンを自動で無効化する」コンポーネント
といったバリエーションも、同じく 1コンポーネント完結 の設計で量産できます。
まずは今回の SceneChanger をプロジェクト内の各種ボタンに適用し、シーン遷移処理の共通化・簡素化を体感してみてください。




