mcpField
T
time-and-timers

time-and-timers

github.com/phaserjs开发40.6k810 次安装10 周前更新
代码仓库

关于 time-and-timers

Phaser 4 时间与计时器指南,面向需要安排游戏内事件和时间流程的开发者。涵盖场景时钟、定时器及可组合回调、补间和声音等动作的时间线。

主要功能

  1. 01创建延迟与重复计时器
  2. 02暂停时钟或单个计时器
  3. 03调整时钟及事件时间倍率
  4. 04按绝对或相对时间编排时间线
  5. 05在时间线中触发回调、补间和声音

适用场景

  1. 01延迟触发场景切换
  2. 02按固定间隔生成敌人或发射子弹
  3. 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 创建的补间动画。请单独设置补间动画的 timeScale。

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
属性类型默认值描述
delaynumber0回调触发前的延迟毫秒数
repeatnumber0首次触发后重复的次数。使用 -1 表示无限
loopbooleanfalse如果为 true,则无限重复(等同于 repeat: -1)
callbackfunction--计时器触发时调用的函数
callbackScopeanyTimerEvent回调的 this 上下文
argsArray[]传递给回调的额外参数
timeScalenumber1每个事件的时间乘数
startAtnumber0预填充已流逝的毫秒数(使首次触发更早发生)
pausedbooleanfalse以暂停状态启动计时器
TimelineEventConfig
属性类型默认值描述
atnumber0从时间线开始的绝对毫秒时间
innumber--相对于当前已流逝时间的偏移(覆盖 at)
fromnumber--相对于上一事件时间的偏移(覆盖 at 和 in)
runfunction--事件触发时调用的回调
loopfunction--重复迭代时调用的回调(首次运行时不调用)
iffunction--守卫函数;返回 false 则跳过该事件
eventstring--在 Timeline 实例上发出的事件名称
targetany--run/loop/if 的作用域,以及 set 的目标
setobject--事件触发时应用于 target 的键值对
tweenTweenConfig--事件触发时创建的补间配置或实例
soundstring/object--要播放的音效键或 { key, config }
oncebooleanfalse事件触发后移除此事件
stopbooleanfalse此事件触发时停止整个时间线
TimelineEvent(内部)

存储在 timeline.events[] 中经过处理的事件对象。在配置基础上扩展了:

属性类型描述
completeboolean此事件是否已触发
timenumber解析后的绝对毫秒时间
repeatnumber此事件已重复的次数
tweenInstanceTween/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清除尚未加入活动列表的计时器
属性类型描述
nownumber当前时钟毫秒时间
startTimenumber场景启动的时间
timeScalenumber所有计时器的增量乘数
pausedboolean冻结所有计时器
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清空回调引用
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 进度
属性类型描述
elapsednumber当前已流逝时间,以毫秒为单位
timeScalenumber增量倍数(不影响生成的补间动画)
pausedboolean时间线是否已暂停
completeboolean是否所有事件均已运行
loopnumber额外循环次数(0=不循环,-1=无限)
iterationnumber当前循环迭代
totalCompletenumber已触发事件的数量
eventsTimelineEvent[]内部事件数组

注意事项

  1. repeat 与总触发次数。 repeat: 4 表示总共触发 5 次回调(1 次初始 + 4 次重复)。这是常见的差一错误来源。
  2. repeat 配合零延迟会抛出异常。 带有 delay: 0 且任意 repeat/loop 的 TimerEvent 会抛出 'TimerEvent infinite loop created via zero delay'。
  3. Timeline 创建后处于暂停状态。 创建后必须调用 timeline.play()。忘记调用是常见错误。
  4. Timeline 的 timeScale 不影响 tween。 设置 timeline.timeScale 只会缩放 Timeline 自身的已用时间计数器。由该 Timeline 创建的 tween 会按自己的速度运行。请单独设置 tween 的 timeScale,或使用 TweenManager。
  5. once 事件会被永久移除。 带有 once: true 的 Timeline 事件在触发后会被移除,不会在 reset() 或循环时重新出现。
  6. Timer 添加会被延迟。 addEvent() 会将事件推入待处理列表,并在 preUpdate 中处理。Timer 要到下一帧才会生效。
  7. Clock 的 paused 与 timeScale 0。 两者都会冻结 Timer,但 paused = true 会完全跳过更新循环,而 timeScale = 0 仍会以零增量运行循环。若要完全冻结,优先使用 paused。
  8. callbackScope 默认值。 如果省略 callbackScope,回调内部的 this 将是 TimerEvent 本身,而不是 scene。请使用箭头函数,或显式传入 callbackScope: this。
  9. 复用 TimerEvent 实例。 可以将 TimerEvent 对象传给 addEvent(),但它不能处于已完成状态。Clock 会重置其 elapsed 和 dispatch 状态。
  10. Timeline 的 from 链式关系。 每个 from 偏移量都相对于前一个事件解析后的时间,而不是前一个 from 的值。事件按数组顺序处理。
  11. Scene 暂停会暂停 Timeline。 当 scene 被暂停(scene.pause())时,Timeline 的更新循环也会停止,因为 Timeline 的更新由 scene 的 update 步骤驱动。
  12. timer.reset() 不会重新添加到 Clock。 调用 timer.reset(config) 会重新初始化 Timer,但不会调度它。重置后必须再次调用 this.time.addEvent(timer)。

源文件映射

文件描述
src/time/Clock.jsScene Clock 插件 —— 创建、更新、移除 TimerEvent
src/time/TimerEvent.js单个 Timer —— 延迟、repeat、loop、进度跟踪
src/time/Timeline.js事件序列器 —— 计划动作、tween、声音、循环
src/time/typedefs/TimerEventConfig.jsaddEvent() 的配置类型定义
src/time/typedefs/TimelineEventConfig.jstimeline.add() 的配置类型定义
src/time/typedefs/TimelineEvent.js内部事件对象类型定义
src/time/events/COMPLETE_EVENT.jsTimeline 的 'complete' 事件常量
src/time/events/index.js事件命名空间的 barrel 文件

评论

支持 Markdown