time-and-timers について
ゲーム内の時間管理やイベント予約を行う開発者向けの Phaser 4 ガイドです。シーンのクロック、タイマー、コールバックやトゥイーン、サウンドを組み合わせるタイムラインを解説します。
主な機能
- 01遅延・反復タイマーの作成
- 02クロックまたは個別タイマーの一時停止
- 03クロックとイベントの時間倍率設定
- 04絶対・相対時刻でのタイムライン予約
- 05タイムラインでのコールバック、トゥイーン、サウンド実行
活用シーン
- 01シーン遷移の遅延実行
- 02一定間隔での敵の生成や弾の発射
- 03トゥイーンとサウンドを含むカットシーンの構成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ phaserjs/ phaser --skill time-and-timersインストール後、Claude Code で/time-and-timersを入力して読み込まれたか確認します。
詳細
時間とタイマー
Phaser 4 における Clock プラグイン、TimerEvent、遅延、ループ、Timeline によるイベントシーケンス、時間の一時停止、タイムスケール、デルタタイムについて。
主要なソースパス: src/time/Clock.js、src/time/TimerEvent.js、src/time/Timeline.js、src/time/typedefs/、src/time/events/
関連スキル: ../scenes/SKILL.md、../tweens/SKILL.md
クイックスタート
// In a Scene's create() method:
// One-shot delayed call (fires once after 1 second)
this.time.delayedCall(1000, () => {
console.log('One second later');
});
// Repeating timer (fires 5 times, once every 500ms)
this.time.addEvent({
delay: 500,
callback: () => { console.log('tick'); },
repeat: 4 // 4 repeats = 5 total fires
});
// Infinite loop timer
this.time.addEvent({
delay: 1000,
callback: this.spawnEnemy,
callbackScope: this,
loop: true
});
this.time はシーンの Clock インスタンスです(キー time で 'Clock' プラグインとして登録されています)。ゲーム時間に基づいてコールバックを発火させる TimerEvent オブジェクトを作成し、管理します。
コアコンセプト
Clock (this.time)
Clock はシーンレベルのプラグインで、ゲーム時間を追跡し、毎フレームすべての TimerEvent を更新します。主なプロパティ:
now-- 現在の時間(ミリ秒単位、シーンのupdateメソッドに渡されるtimeと同等です)。startTime-- シーンが開始されたときのタイムスタンプ。timeScale-- デルタタイムに適用される乗数。デフォルトは1。1 より大きい値ではすべてのタイマーが速くなり、1 より小さい値では遅くなり、0では時間が凍結します。paused--trueのとき、TimerEvent は更新されません。
Clock は PRE_UPDATE(保留中の追加/削除を処理するため)と UPDATE(アクティブなイベントを進めるため)をリッスンします。シーンとともに自動的にシャットダウンされ、破棄されます。
TimerEvent
TimerEvent は毎フレーム経過時間を蓄積します: elapsed += delta * clock.timeScale * event.timeScale。elapsed >= delay になると、コールバックが発火します。すべてのリピートが尽きると、イベントは次のフレームで Clock から削除されます。
設定で指定する主なプロパティ: delay、repeat、loop、callback、callbackScope、args、timeScale、startAt、paused。
Timeline (this.add.timeline)
Timeline は、特定の時点でアクションをスケジュールするためのシーケンサーです。独立したタイマーを管理する Clock とは異なり、Timeline は絶対または相対タイムスタンプをキーとするイベントの線形シーケンスを実行します。
const timeline = this.add.timeline([
{ at: 0, run: () => { /* immediate */ } },
{ at: 1000, run: () => { /* at 1s */ } },
{ at: 2500, tween: { targets: sprite, alpha: 0, duration: 500 } }
]);
timeline.play();
Timeline は常に 一時停止 状態で開始します。開始するには play() を呼び出す必要があります。GameObjectFactory を介して作成され、シーンがシャットダウンすると自動的に破棄されます。
一般的なパターン
遅延呼び出し
// Shorthand -- fires once, no repeat
this.time.delayedCall(2000, () => {
this.scene.start('GameOver');
});
有限回数のリピートタイマー
// repeat: 9 means 10 total fires (1 initial + 9 repeats)
const timer = this.time.addEvent({
delay: 200,
callback: this.fireBullet,
callbackScope: this,
repeat: 9
});
// Check progress
timer.getRepeatCount(); // repeats remaining
timer.getOverallProgress(); // 0..1 across all repeats
無限ループ
const spawner = this.time.addEvent({
delay: 3000,
callback: this.spawnWave,
callbackScope: this,
loop: true
});
// Stop it later
spawner.remove(); // or spawner.paused = true to pause
repeat: -1 を設定することは loop: true と同等です。
startAt による初回発火のショートカット
// First fire happens quickly (after 100ms), then every 2s
this.time.addEvent({
delay: 2000,
callback: this.heartbeat,
callbackScope: this,
loop: true,
startAt: 1900 // pre-fill elapsed so first fire is at 100ms
});
タイマーの停止と削除
const timer = this.time.addEvent({ delay: 1000, loop: true, callback: fn });
// Option 1: Remove from clock (schedules removal next frame)
timer.remove(); // silently expires
timer.remove(true); // fires callback one last time, then expires
// Option 2: Remove via Clock
this.time.removeEvent(timer);
// Option 3: Remove all timers
this.time.removeAllEvents();
一時停止と再開
// Pause the entire Clock (all timers freeze)
this.time.paused = true;
this.time.paused = false;
// Pause a single timer
timer.paused = true;
timer.paused = false;
タイムスケール(スローモーション / 早送り)
// Slow all timers in this scene to half speed
this.time.timeScale = 0.5;
// Speed up a single timer to 2x
timer.timeScale = 2;
// Combined: effective scale = clock.timeScale * event.timeScale
// So 0.5 * 2 = 1x for that specific timer
タイマーの状態を読み取る
timer.getProgress(); // 0..1 for current iteration
timer.getOverallProgress(); // 0..1 across all repeats
timer.getElapsed(); // ms elapsed this iteration
timer.getElapsedSeconds(); // seconds elapsed this iteration
timer.getRemaining(); // ms until next fire
timer.getRemainingSeconds(); // seconds until next fire
timer.getOverallRemaining(); // ms until final fire
timer.getOverallRemainingSeconds(); // seconds until final fire
timer.getRepeatCount(); // repeats left
Timeline: イベントのシーケンス
const timeline = this.add.timeline([
{
at: 0,
run: () => { this.title.setAlpha(1); },
sound: 'intro'
},
{
at: 2000,
tween: { targets: this.title, y: 100, duration: 1000 },
sound: { key: 'whoosh', config: { volume: 0.5 } }
},
{
at: 4000,
set: { alpha: 0 },
target: this.title,
event: 'INTRO_DONE'
}
]);
timeline.on('INTRO_DONE', (target) => { /* custom event */ });
timeline.on('complete', (tl) => { /* all events ran */ });
timeline.play();
Timeline: from と in による相対タイミング
const timeline = this.add.timeline([
{ at: 1000, run: stepOne }, // absolute: 1s from start
{ from: 500, run: stepTwo }, // relative: 500ms after previous (1.5s)
{ from: 1000, run: stepThree } // relative: 1000ms after previous (2.5s)
]);
at-- Timeline の開始からの絶対ミリ秒(デフォルトは 0)。in-- 現在のelapsed時間からのオフセット(実行中の Timeline にイベントを追加するときに便利です)。from-- 前のイベントの開始時間からのオフセット。
優先順位: from は in を上書きし、in は at を上書きします。
Timeline: 条件付きイベント
const timeline = this.add.timeline([
{
at: 5000,
if: () => this.player.health > 0,
run: () => { this.showBonusRound(); }
}
]);
if コールバックが false を返した場合、イベントはスキップされます(完了としてマークされますが、アクションは実行されません)。
Timeline: ループ
const timeline = this.add.timeline([
{ at: 0, run: () => console.log('start') },
{ at: 1000, run: () => console.log('end') }
]);
timeline.repeat().play(); // infinite loop
timeline.repeat(3).play(); // loop 3 additional times (4 total runs)
timeline.repeat(false).play(); // no looping
TimelineEventConfig の loop コールバックは、リピートの反復時(初回の実行ではない)に発火します。
Timeline: 一時停止、再開、停止、リセット
timeline.pause(); // freezes elapsed counter and pauses spawned tweens
timeline.resume(); // resumes elapsed counter and unpauses spawned tweens
timeline.stop(); // sets paused=true, complete=true
timeline.reset(); // elapsed=0, all events marked incomplete, starts playing
timeline.isPlaying(); // true if not paused and not complete
timeline.getProgress(); // 0..1 based on events completed / total events
Timeline: タイムスケール
timeline.timeScale = 2; // double speed
timeline.timeScale = 0.5; // half speed
注意: Timeline の timeScale は、Timeline によって作成されたトゥイーンには影響しません。トゥイーンのタイムスケールは別途設定してください。
Timeline: 完全なカットシーンの例
Timeline は、さまざまなアクション(コールバック、トゥイーン、サウンド、プロパティ設定、カスタムイベント)を組み合わせたカットシーンの振り付けに優れています:
class CutsceneScene extends Phaser.Scene {
create() {
const timeline = this.add.timeline([
{
at: 0,
run: () => { console.log('Start!'); }
},
{
at: 1000,
tween: {
targets: this.player,
x: 400,
duration: 500,
ease: 'Power2'
}
},
{
at: 1500,
sound: 'doorOpen'
},
{
at: 2000,
set: { visible: true },
target: this.door
},
{
from: 500,
run: () => { console.log('Relative timing'); }
},
{
at: 5000,
event: 'cutsceneDone',
stop: true
}
]);
timeline.on('cutsceneDone', () => {
this.scene.start('GameScene');
});
timeline.play();
}
}
タイマーのリセットと再利用
完了した TimerEvent は、新しい設定でリセットし、Clock に再追加できます:
const timer = this.time.addEvent({
delay: 1000,
callback: this.phase1,
callbackScope: this,
repeat: 2
});
// Later, after it completes, reconfigure and re-add:
timer.reset({
delay: 500,
callback: this.phase2,
callbackScope: this,
repeat: 4
});
this.time.addEvent(timer); // must re-add; reset() alone does not schedule it
設定リファレンス
TimerEventConfig
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
delay | number | 0 | コールバックが発火するまでの遅延時間(ミリ秒) |
repeat | number | 0 | 初回発火後に繰り返す回数。無限に繰り返すには -1 を使用します |
loop | boolean | false | true の場合、無期限に繰り返します(repeat: -1 と同じ) |
callback | function | -- | タイマー発火時に呼び出される関数 |
callbackScope | any | TimerEvent | コールバックの this コンテキスト |
args | Array | [] | コールバックに渡される追加引数 |
timeScale | number | 1 | イベントごとの時間乗数 |
startAt | number | 0 | 経過時間を事前に設定します(ミリ秒)(初回発火が早くなります) |
paused | boolean | false | タイマーを一時停止状態で開始します |
TimelineEventConfig
| プロパティ | 型 | デフォルト | 説明 |
|---|---|---|---|
at | number | 0 | タイムライン開始からの絶対時間(ミリ秒) |
in | number | -- | 現在の経過時間からのオフセット(at を上書きします) |
from | number | -- | 前のイベント時刻からのオフセット(at と in を上書きします) |
run | function | -- | イベント発火時に呼び出されるコールバック |
loop | function | -- | 繰り返しのイテレーションで呼び出されるコールバック(初回実行時を除く) |
if | function | -- | ガード関数。false を返すとイベントをスキップします |
event | string | -- | Timeline インスタンスで発行されるイベント名 |
target | any | -- | run/loop/if のスコープ、および set のターゲット |
set | object | -- | イベント発火時に target に適用されるキーと値のペア |
tween | TweenConfig | -- | イベント発火時に作成されるトゥイーンの設定またはインスタンス |
sound | string/object | -- | 再生するサウンドキーまたは { key, config } |
once | boolean | false | 発火後にこのイベントを削除します |
stop | boolean | false | このイベントの発火時にタイムライン全体を停止します |
TimelineEvent(内部)
timeline.events[] に格納される処理済みのイベントオブジェクト。設定に以下を追加したものです。
| プロパティ | 型 | 説明 |
|---|---|---|
complete | boolean | このイベントが発火したかどうか |
time | number | 解決された絶対時間(ミリ秒) |
repeat | number | このイベントが繰り返された回数 |
tweenInstance | Tween/TweenChain | 生成されたトゥイーンへの参照(存在する場合) |
イベント
タイムラインイベント
| イベント | 定数 | リスナーシグネチャ | 発火タイミング |
|---|---|---|---|
'complete' | Phaser.Time.Events.COMPLETE | (timeline) | すべてのタイムラインイベントが実行されたとき |
TimelineEventConfig の event プロパティによるカスタムイベントも、(target) というシグネチャで Timeline インスタンス上に発行されます。
TimerEvent / Clock
TimerEvent と Clock は EventEmitter イベントを発行しません。タイマーは callback プロパティを直接使用します。Clock はシーンライフサイクルイベント(PRE_UPDATE、UPDATE、SHUTDOWN、DESTROY)によって管理されます。
API クイックリファレンス
Clock(this.time)
| メソッド | シグネチャ | 戻り値 | 説明 |
|---|---|---|---|
addEvent | (config | TimerEvent) | TimerEvent | タイマーイベントを作成してスケジュールします |
delayedCall | (delay, callback, args?, scope?) | TimerEvent | 単発の遅延呼び出しの省略記法 |
removeEvent | (event | event[]) | this | 特定のタイマーを削除します |
removeAllEvents | () | this | すべてのアクティブなタイマーの削除をスケジュールします |
clearPendingEvents | () | this | まだアクティブリストに追加されていないタイマーをクリアします |
| プロパティ | 型 | 説明 |
|---|---|---|
now | number | 現在のクロック時間(ミリ秒) |
startTime | number | シーンが開始された時刻 |
timeScale | number | すべてのタイマーのデルタ乗数 |
paused | boolean | すべてのタイマーを凍結します |
TimerEvent
| メソッド | 戻り値 | 説明 |
|---|---|---|
getProgress() | number | 現在のイテレーションの進捗 (0..1) |
getOverallProgress() | number | すべての繰り返しを通した進捗 (0..1) |
getElapsed() | number | このイテレーションの経過時間 (ミリ秒) |
getElapsedSeconds() | number | このイテレーションの経過時間 (秒) |
getRemaining() | number | 次に発火するまでのミリ秒 |
getRemainingSeconds() | number | 次に発火するまでの秒数 |
getOverallRemaining() | number | 最後の発火までのミリ秒 |
getOverallRemainingSeconds() | number | 最後の発火までの秒数 |
getRepeatCount() | number | 残りの繰り返し回数 |
remove(dispatchCallback?) | void | タイマーを終了します (任意でコールバックを発火) |
reset(config) | TimerEvent | 新しい設定で再初期化します |
destroy() | void | コールバック参照を null にします |
Timeline (this.add.timeline)
| メソッド | シグネチャ | 戻り値 | 説明 |
|---|---|---|---|
add | (config | config[]) | this | タイムラインにイベントを追加します |
play | (fromStart?) | this | 再生を開始します (デフォルトでは先頭にリセット) |
pause | () | this | タイムラインと生成されたトゥイーンを一時停止します |
resume | () | this | タイムラインと生成されたトゥイーンを再開します |
stop | () | this | 停止します (一時停止 + 完了に設定) |
reset | (loop?) | this | 経過時間とすべてのイベントを未完了にリセットします |
repeat | (amount?) | this | ループ回数を設定します (-1/true=無限、false=なし) |
clear | () | this | すべてのイベントを削除し、経過時間をリセットして一時停止します |
isPlaying | () | boolean | 一時停止しておらず完了もしていない場合に true |
getProgress | () | number | 完了したイベント数に基づく 0..1 |
| プロパティ | 型 | 説明 |
|---|---|---|
elapsed | number | 現在の経過時間 (ミリ秒) |
timeScale | number | デルタの倍率 (生成されたトゥイーンには影響しません) |
paused | boolean | タイムラインが一時停止しているかどうか |
complete | boolean | すべてのイベントが実行されたかどうか |
loop | number | 追加のループ回数 (0=なし、-1=無限) |
iteration | number | 現在のループイテレーション |
totalComplete | number | 発火したイベントの数 |
events | TimelineEvent[] | 内部のイベント配列 |
注意点
- repeat と total fires の違い。
repeat: 4はコールバックが合計 5 回呼び出されることを意味します(初回 1 回 + リピート 4 回)。これはよくある off-by-one の原因です。 - repeat を指定したゼロ遅延は例外をスローします。
delay: 0と任意の repeat/loop を組み合わせたTimerEventは'TimerEvent infinite loop created via zero delay'をスローします。 - タイムラインは一時停止状態で開始します。 作成後に
timeline.play()を呼び出す必要があります。これを忘れるのはよくある間違いです。 - タイムラインの timeScale はトゥイーンに影響しません。
timeline.timeScaleを設定すると、タイムライン自身の経過カウンターのみがスケールされます。タイムラインによって作成されたトゥイーンは独自の速度で実行されます。トゥイーンのtimeScaleを個別に設定するか、TweenManager を使用してください。 - once イベントは永続的に削除されます。
once: trueのタイムラインイベントは発火後に取り除かれ、reset()やループ時に再び現れることはありません。 - タイマーの追加は遅延されます。
addEvent()はpreUpdateで処理される保留リストにプッシュします。タイマーは次のフレームまでアクティブになりません。 - Clock の paused と timeScale 0 の違い。 どちらもタイマーを凍結しますが、
paused = trueは更新ループを完全にスキップするのに対し、timeScale = 0はデルタをゼロにしてループを実行し続けます。完全に凍結するにはpausedを推奨します。 - callbackScope のデフォルト。
callbackScopeを省略すると、コールバック内のthisはシーンではなく TimerEvent 自体になります。アロー関数を使うか、明示的にcallbackScope: thisを渡してください。 - TimerEvent インスタンスの再利用。
TimerEventオブジェクトをaddEvent()に渡すことはできますが、完了状態であってはなりません。Clock はその経過時間とディスパッチ状態をリセットします。 - タイムラインの
fromチェーン。 各fromオフセットは、前のfrom値ではなく、前のイベントの解決された時間を基準にしています。イベントは配列の順序で処理されます。 - シーンの一時停止はタイムラインも一時停止します。 シーンが一時停止されると(
scene.pause())、タイムラインの更新はシーンの更新ステップによって駆動されるため、タイムラインの更新ループも停止します。 timer.reset()は Clock に再追加しません。timer.reset(config)を呼び出すとタイマーは再初期化されますが、スケジュールはされません。リセット後に再度this.time.addEvent(timer)を呼び出す必要があります。
ソースファイルマップ
| ファイル | 説明 |
|---|---|
src/time/Clock.js | シーン Clock プラグイン -- TimerEvent の作成、更新、削除 |
src/time/TimerEvent.js | 個別のタイマー -- delay、repeat、loop、進捗トラッキング |
src/time/Timeline.js | イベントシーケンサー -- スケジュールされたアクション、トゥイーン、サウンド、ループ |
src/time/typedefs/TimerEventConfig.js | addEvent() の設定 typedef |
src/time/typedefs/TimelineEventConfig.js | timeline.add() の設定 typedef |
src/time/typedefs/TimelineEvent.js | 内部イベントオブジェクト typedef |
src/time/events/COMPLETE_EVENT.js | タイムライン用の 'complete' イベント定数 |
src/time/events/index.js | イベント名前空間のバレルファイル |