【Cocos Creator 3.8】TimeScaler の実装:アタッチするだけでゲーム全体のスローモーション&倍速を切り替えられる汎用スクリプト
このコンポーネントは、キーボード入力で ゲーム全体の時間スケール を変更し、スローモーションや倍速再生を簡単に切り替えるためのデバッグ用ツールです。任意のノードにアタッチするだけで、プロジェクト全体の timeScale を安全に管理できるようになります。
ゲームバランス調整やアニメーション・エフェクトの確認、物理挙動の検証など、時間をゆっくり/早く進めたい場面で非常に便利です。インスペクタからキー設定や倍率を変えられるので、プロジェクトごとに柔軟に使い回せます。
コンポーネントの設計方針
機能要件の整理
- キーボード入力で
game.timeScaleを変更する。 - 代表的な時間倍率:
- 通常速度(1.0倍)
- スローモーション(例:0.1倍)
- 倍速(例:2.0倍)
- キー入力はインスペクタから変更可能にする。
- 現在の timeScale をログに出力するオプションを用意し、デバッグをしやすくする。
- ゲーム開始時に 必ず 1.0倍にリセット する(他のシーンからの影響を受けないようにする)。
- このスクリプト単体で完結し、他のカスタムスクリプトには一切依存しない。
外部依存をなくすためのアプローチ
game.timeScaleはグローバルに効くので、どのノードにアタッチしても効果は同じ。- キーボード入力は
input.on(Input.EventType.KEY_DOWN, ...)を使い、onEnable/onDisableで確実に登録/解除する。 - 必要な情報(キー・倍率・ログ出力設定など)はすべて
@propertyで公開し、インスペクタから設定できるようにする。 - エディタ上でわかりやすいよう、ツールチップとデフォルト値を丁寧に設定する。
インスペクタで設定可能なプロパティ設計
- enableOnStart: boolean
- 説明: シーン開始時から TimeScaler を有効にするかどうか。
- 用途: 一時的に機能を無効化したい場合にチェックを外す。
- normalScale: number
- 説明: 通常速度時の timeScale(通常は 1.0)。
- 用途: 「このゲームは標準が 0.5倍」など特殊なケースでも柔軟に対応。
- slowScale: number
- 説明: スローモーション時の timeScale(例: 0.1)。
- 用途: 細かい挙動を確認したいときに小さく設定。
- fastScale: number
- 説明: 倍速時の timeScale(例: 2.0)。
- 用途: 長い演出やテストプレイを早送りしたいときに利用。
- keyNormal: KeyCode
- 説明: 通常速度に戻すキー。
- デフォルト:
KeyCode.DIGIT_1(キーボードの「1」)。
- keySlow: KeyCode
- 説明: スローモーションに切り替えるキー。
- デフォルト:
KeyCode.DIGIT_2(キーボードの「2」)。
- keyFast: KeyCode
- 説明: 倍速に切り替えるキー。
- デフォルト:
KeyCode.DIGIT_3(キーボードの「3」)。
- keyTogglePause: KeyCode
- 説明: 一時停止(timeScale 0)と直前の速度をトグルするキー。
- デフォルト:
KeyCode.SPACE。
- minScale: number
- 説明: 設定可能な最小 timeScale(0.0以上)。
- 用途: 誤って負の値や極端に小さい値を入れてしまった場合の保護。
- maxScale: number
- 説明: 設定可能な最大 timeScale。
- 用途: 物理挙動が破綻しないように上限を決める。
- logOnChange: boolean
- 説明: timeScale が変更されたときにコンソールへログを出すかどうか。
- 用途: デバッグ時に現在の速度をすぐ確認できる。
- showInitLog: boolean
- 説明: シーン開始時に現在の設定と初期 timeScale をログ出力するかどうか。
TypeScriptコードの実装
import { _decorator, Component, input, Input, KeyCode, EventKeyboard, game, clamp, sys } from 'cc';
const { ccclass, property } = _decorator;
/**
* TimeScaler
* デバッグ用の時間スケール変更コンポーネント。
* 任意のノードにアタッチするだけで、キーボード入力で game.timeScale を切り替えられます。
*/
@ccclass('TimeScaler')
export class TimeScaler extends Component {
@property({
tooltip: 'シーン開始時から TimeScaler を有効にするかどうか。\n無効にするとキー入力を受け付けません。',
})
public enableOnStart: boolean = true;
@property({
tooltip: '通常速度時の timeScale。\n一般的には 1.0 を指定します。',
min: 0.0,
})
public normalScale: number = 1.0;
@property({
tooltip: 'スローモーション時の timeScale。\n小さいほどゆっくりになります(例: 0.1)。',
min: 0.0,
})
public slowScale: number = 0.1;
@property({
tooltip: '倍速時の timeScale。\n大きいほど速くなります(例: 2.0)。',
min: 0.0,
})
public fastScale: number = 2.0;
@property({
type: KeyCode,
tooltip: '通常速度に戻すキー。',
})
public keyNormal: KeyCode = KeyCode.DIGIT_1;
@property({
type: KeyCode,
tooltip: 'スローモーションに切り替えるキー。',
})
public keySlow: KeyCode = KeyCode.DIGIT_2;
@property({
type: KeyCode,
tooltip: '倍速に切り替えるキー。',
})
public keyFast: KeyCode = KeyCode.DIGIT_3;
@property({
type: KeyCode,
tooltip: '一時停止(timeScale = 0)と直前の速度をトグルするキー。',
})
public keyTogglePause: KeyCode = KeyCode.SPACE;
@property({
tooltip: '設定可能な最小 timeScale。\n0.0 以上の値を推奨します。',
min: 0.0,
})
public minScale: number = 0.0;
@property({
tooltip: '設定可能な最大 timeScale。\nあまり大きくしすぎると物理挙動が不安定になる可能性があります。',
min: 0.0,
})
public maxScale: number = 5.0;
@property({
tooltip: 'timeScale が変更されたときにログを出力するかどうか。',
})
public logOnChange: boolean = true;
@property({
tooltip: 'シーン開始時に現在の設定と初期 timeScale をログ出力するかどうか。',
})
public showInitLog: boolean = true;
// 現在の timeScale を保持(game.timeScale と同期)
private _currentScale: number = 1.0;
// 一時停止トグル用に、直前の timeScale を保持
private _prevScaleBeforePause: number = 1.0;
onLoad() {
// エディタ実行時(プレビュー)・ビルド後どちらでも動く想定
// 設定値の簡易バリデーション
this._validateScales();
// シーン開始時に必ず timeScale を normalScale にリセット
game.timeScale = this._clampScale(this.normalScale);
this._currentScale = game.timeScale;
this._prevScaleBeforePause = this._currentScale;
if (this.showInitLog) {
console.log(
`[TimeScaler] Initialized. ` +
`normal=${this.normalScale}, slow=${this.slowScale}, fast=${this.fastScale}, ` +
`current=${this._currentScale}`
);
}
}
onEnable() {
if (!this.enableOnStart) {
// 無効状態で開始したい場合は、ここでリスナーを登録しない
return;
}
this._registerInput();
}
onDisable() {
this._unregisterInput();
}
onDestroy() {
this._unregisterInput();
}
/**
* キーボード入力の登録
*/
private _registerInput() {
input.on(Input.EventType.KEY_DOWN, this._onKeyDown, this);
}
/**
* キーボード入力の解除
*/
private _unregisterInput() {
input.off(Input.EventType.KEY_DOWN, this._onKeyDown, this);
}
/**
* キー入力ハンドラ
*/
private _onKeyDown(event: EventKeyboard) {
const key = event.keyCode;
if (key === this.keyNormal) {
this._setTimeScale(this.normalScale, 'normal');
} else if (key === this.keySlow) {
this._setTimeScale(this.slowScale, 'slow');
} else if (key === this.keyFast) {
this._setTimeScale(this.fastScale, 'fast');
} else if (key === this.keyTogglePause) {
this._togglePause();
}
}
/**
* timeScale を設定し、ログ出力や内部状態の更新を行う
*/
private _setTimeScale(scale: number, label?: string) {
const clamped = this._clampScale(scale);
// 0 以外から 0 に遷移する場合は、直前の値を保存しておく
if (this._currentScale !== 0 && clamped === 0) {
this._prevScaleBeforePause = this._currentScale;
}
this._currentScale = clamped;
game.timeScale = clamped;
if (this.logOnChange) {
const prefix = label ? `[TimeScaler] ${label}` : '[TimeScaler]';
console.log(`${prefix} timeScale set to ${clamped}`);
}
}
/**
* 一時停止トグル
* - 現在 0 のとき: 直前の速度に戻す
* - 現在 0 以外のとき: 0 にする
*/
private _togglePause() {
if (this._currentScale === 0) {
// 直前の速度に戻す。ただし現在の min/max にもクランプする
const restored = this._clampScale(this._prevScaleBeforePause || this.normalScale);
this._setTimeScale(restored, 'resume');
} else {
this._setTimeScale(0, 'pause');
}
}
/**
* 設定値の簡易バリデーション
* - minScale <= maxScale に補正
* - slow/normal/fast を min/max でクランプ
*/
private _validateScales() {
if (this.minScale > this.maxScale) {
const tmp = this.minScale;
this.minScale = this.maxScale;
this.maxScale = tmp;
console.warn(
'[TimeScaler] minScale was greater than maxScale. Values were swapped.',
`min=${this.minScale}, max=${this.maxScale}`
);
}
this.slowScale = this._clampScale(this.slowScale);
this.normalScale = this._clampScale(this.normalScale);
this.fastScale = this._clampScale(this.fastScale);
}
/**
* timeScale を min/max でクランプするユーティリティ
*/
private _clampScale(value: number): number {
// Cocos の clamp を使用(cc.clamp)
return clamp(value, this.minScale, this.maxScale);
}
}
コードのポイント解説
- onLoad
_validateScales()でプロパティの値を簡易チェックし、minScale > maxScaleの場合は自動で修正。- シーン開始時に
game.timeScaleをnormalScaleにリセットし、他シーンからの影響を断ちます。 showInitLogが true の場合、現在の設定と初期値をログ出力。
- onEnable / onDisable / onDestroy
enableOnStartが true のときだけ、キー入力リスナーを登録。- コンポーネントが無効化されたり破棄されたときには、必ずリスナーを解除してリークや二重登録を防止。
- _onKeyDown
- 押されたキーが
keyNormal / keySlow / keyFast / keyTogglePauseのいずれかなら、対応する処理を呼び出し。 - キーは
KeyCodeで比較しているため、インスペクタから自由に変更可能。
- 押されたキーが
- _setTimeScale
_clampScale()を通してminScale〜maxScaleの範囲に収めた上でgame.timeScaleを更新。- 0 以外 → 0 の遷移時には
_prevScaleBeforePauseに直前の値を保存し、トグル復帰時に利用。 logOnChangeが true の場合、変更内容をconsole.logに出力。
- _togglePause
- 現在
_currentScale === 0なら、直前の速度(_prevScaleBeforePause)に戻す。 - そうでなければ、
_setTimeScale(0)で一時停止。
- 現在
- _validateScales / _clampScale
- 防御的プログラミングとして、インスペクタからの誤入力をある程度吸収。
- slow/normal/fast の値も min/max に収まるよう自動補正。
使用手順と動作確認
1. スクリプトファイルの作成
- エディタ上部メニュー、または Assets パネルで操作します。
- Assets パネルで任意のフォルダ(例:
assets/scripts)を右クリックします。 - Create > TypeScript を選択します。
- 新しく作成されたファイル名を
TimeScaler.tsに変更します。 TimeScaler.tsをダブルクリックしてエディタで開き、本文をすべて削除して、上記の TypeScript コードをそのまま貼り付けて保存します。
2. テスト用ノードの作成
TimeScaler はどのノードにアタッチしても動作しますが、ここでは分かりやすく 2D シーン上のスプライトに付けてみます。
- Hierarchy パネルで右クリックします。
- Create > 2D Object > Sprite を選択して、テスト用のスプライトノードを作成します。
- 作成されたノード(例:
Sprite)を選択し、Inspector パネルで名前をDebugControllerなどに変更しても構いません。
3. TimeScaler コンポーネントをアタッチ
- TimeScaler を付けたいノード(先ほど作成した Sprite など)を Hierarchy で選択します。
- Inspector パネルの下部にある Add Component ボタンをクリックします。
- 表示される検索ボックスで
TimeScalerと入力します。 - Custom > TimeScaler が表示されるので、それをクリックしてアタッチします。
4. インスペクタでプロパティを設定
ノードに TimeScaler が追加されると、Inspector に以下のようなプロパティが表示されます。
- Enable On Start: チェックを入れたままにします(デフォルト true)。
- Normal Scale:
1.0(デフォルト)。 - Slow Scale:
0.1(お好みで0.2や0.05に変更しても良い)。 - Fast Scale:
2.0(お好みで3.0などに変更可能。ただし大きくしすぎると挙動が荒くなることがあります)。 - Key Normal: デフォルトで
DIGIT_1(キーボードの 1)。 - Key Slow: デフォルトで
DIGIT_2(キーボードの 2)。 - Key Fast: デフォルトで
DIGIT_3(キーボードの 3)。 - Key Toggle Pause: デフォルトで
SPACE。 - Min Scale:
0.0(0 で完全停止まで許可)。 - Max Scale:
5.0(必要に応じて上限を調整)。 - Log On Change: チェックを入れておくと、速度変更のたびにログが出ます。
- Show Init Log: チェックを入れておくと、シーン開始時に現在の設定がログに出ます。
特に最初は、以下のような設定を推奨します。
- Normal Scale: 1.0
- Slow Scale: 0.1
- Fast Scale: 2.0
- Min Scale: 0.0
- Max Scale: 3.0
- Log On Change: ON
- Show Init Log: ON
5. ゲームを再生して動作確認
- エディタ上部の Play(▶)ボタン をクリックして、シーンをプレビューします。
- ゲームビューが表示されたら、キーボードで以下のキーを押してみます(デフォルト設定の場合):
- 1 キー(DIGIT_1):
game.timeScaleがnormalScale(1.0)に設定されます。- コンソールに
[TimeScaler] normal timeScale set to 1のようなログが出力されます。
- 2 キー(DIGIT_2):
game.timeScaleがslowScale(0.1)に設定され、ゲーム全体がスローモーションになります。- アニメーションや物理挙動がゆっくりになることを確認してください。
- 3 キー(DIGIT_3):
game.timeScaleがfastScale(2.0)に設定され、ゲーム全体が倍速になります。
- Space キー:
- 現在の速度が 0 以外なら
timeScale = 0になり、一時停止状態になります。 - もう一度 Space を押すと、直前の速度(slow/normal/fast いずれか)に戻ります。
- 現在の速度が 0 以外なら
ゲームビューの Console タブを開いておくと、速度変更のログをリアルタイムで確認できます。
6. キー設定や倍率をカスタマイズする
自分の開発環境に合わせてキーや倍率を変えたい場合は、Inspector から自由に調整できます。
- 例えば:
- Key Slow を
KeyCode.KEY_Qに変更して、Q キーでスローにする。 - Key Fast を
KeyCode.KEY_Eに変更して、E キーで倍速にする。 - Slow Scale を
0.2にして、あまり遅くなりすぎないようにする。 - Fast Scale を
3.0にして、さらに高速にする(挙動が破綻しない範囲で)。
- Key Slow を
設定を変更したら、もう一度 Play して挙動を確認してください。
まとめ
- TimeScaler コンポーネントは、任意のノードにアタッチするだけで
- スローモーション(slowScale)
- 通常速度(normalScale)
- 倍速(fastScale)
- 一時停止/再開(toggle pause)
をキーボード操作で切り替えられる、完全独立・汎用的なデバッグ用ツールです。
@propertyでキーや倍率、最小/最大値、ログ出力の有無などを柔軟にカスタマイズできるため、プロジェクトごとに最適な設定で再利用できます。- 外部の GameManager やシングルトンに依存せず、
game.timeScaleだけを安全に操作する設計になっているため、既存プロジェクトにも安心して組み込めます。 - 物理挙動の検証、演出のタイミング調整、長いテストプレイの短縮など、開発効率を大きく向上させることができます。
この TimeScaler をベースに、マウスホイールで連続的に timeScale を変えたり、UI ボタンから操作できるように拡張するなど、用途に応じたカスタマイズも容易です。まずはこのシンプルなバージョンをプロジェクトに組み込んで、時間制御付きの快適なデバッグ環境を整えてみてください。




