Phaser 3.19 “Naofumi“ 版本全解析:Tween 事件系统重构、Shader 离屏渲染与快照能力实战指南
2026/9/19 16:35:40 网站建设 项目流程

Phaser 3.19 "Naofumi" 版本全解析:Tween 事件系统重构、Shader 离屏渲染与快照能力实战指南

【免费下载链接】phaserPhaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.项目地址: https://gitcode.com/gh_mirrors/ph/phaser

本指南以 CHANGELOG-v3.19.md 为核心,系统梳理 Phaser 3.19.0 "Naofumi"(2019 年 8 月 8 日发布)对 Tween 补间系统的重大重构、Spine 插件 3.7 运行时的完整升级,以及 Shader 离屏渲染、Render Texture 快照、WebGL 上下文事件等新能力的落地细节。文中所有功能均结合当前仓库源码逐一印证,读完你将掌握 3.19 引入的新 API 的完整用法、行为变化背后的实现原理,以及在项目中使用这些特性的可复现实战方案。

版本背景与变更总览

Phaser 3.19.0 以动漫角色 "Naofumi" 命名,是继 3.18 "Raphtalia" 之后的一个里程碑版本。3.18 完成了输入系统旧队列模式的清理与鼠标滚轮、鼠标按键状态的原生支持,而 3.19 则把重心放在了三块:

  1. Tween 系统重构:Tween 从普通对象升级为事件发射器,新增start/from/to三段式属性配置与StaggerBuilder交错构建器,并重写了seek逻辑;
  2. Shader 渲染管线增强:Shader 可以离屏渲染到自己的帧缓冲,输出可作为纹理供 Sprite 等其他游戏对象使用,实现了着色器级联;
  3. 渲染快照体系:Render Texture、WebGL 帧缓冲和 Canvas 均支持按像素、按区域取图,可直接保存到 Texture Manager。

此外还包含 Spine 插件 3.7 Runtime 完整升级、输入命中区调试可视化、WebGL 上下文丢失/恢复事件等一系列新特性与数十项 Bug 修复。


Tween 系统重构:从"数据对象"到"事件发射器"

Tween 继承 EventEmitter,事件体系全面落地

3.19 之前,Tween 的生命周期回调主要靠配置对象中的onStartonComplete等函数钩子,行为分散且难以统一监听。3.19 让Tween类直接继承事件发射器,从此可以在补间实例上使用tween.on(...)监听其自身事件。

在源码层面,这一变化体现在 BaseTween.js:BaseTween通过Extends: EventEmitter继承eventemitter3,并在构造函数中调用EventEmitter.call(this)。所有 Tween 与 Timeline 都基于该基类,因此事件能力对两者同时生效。

3.19 新增的 Tween 事件常量位于 src/tweens/events 目录,每个事件对应一个字符串常量:

事件常量监听写法触发时机
Tween.ACTIVE_EVENTtween.on('active', fn)Tween 被 Tween Manager 激活(可能因 delay 尚未实际开始补间)
Tween.START_EVENTtween.on('start', fn)Tween 真正开始补间第一个属性
Tween.UPDATE_EVENTtween.on('update', fn)Tween 属性每次更新
Tween.LOOP_EVENTtween.on('loop', fn)Tween 循环一次,且loopDelay(若有)已到期之后
Tween.REPEAT_EVENTtween.on('repeat', fn)属性重复一次,且repeatDelay(若有)已到期之后
Tween.YOYO_EVENTtween.on('yoyo', fn)属性执行 Yoyo 反弹,且hold延迟(若有)已到期之后
Tween.COMPLETE_EVENTtween.on('complete', fn)Tween 完成或被停止

对应的事件常量文件(如 TWEEN_ACTIVE_EVENT.js、TWEEN_START_EVENT.js)也明确了事件参数:回调会收到(tween, targets),其中targets在补间有多个目标时是目标数组。

一个完整的事件监听示例:

this.tweens.add({ targets: image, x: 500, ease: 'Power1', duration: 3000 }).on('start', function (tween, targets) { // Tween 真正开始改变属性时才触发(delay 结束后) console.log('补间开始'); }).on('update', function (tween, targets) { console.log('当前进度:', tween.progress); }).on('complete', function (tween, targets) { console.log('补间完成'); });

onActive 与 onStart 语义分离

这是 3.19 的一个重要行为修正,也是 变更日志 中标注为 Fix #3330 的问题:此前onStart在 Tween Manager 激活补间的瞬间就会触发,即使补间仍处于 delay 阶段。现在:

  • onActive(对应Tween.ACTIVE_EVENT):Tween Manager 将补间"唤醒"的那一刻触发,即使尚未开始补间任何值;
  • onStart(对应TWEEN_START_EVENT):仅在 Tween 真正开始补间属性值时触发,通常位于 delay 到期之后。

从 Tween.js 的seek实现(src/tweens/tween/Tween.js#L500-L534)可以看到,Tween 在重置、初始化数据后先调用dispatchEvent(Events.TWEEN_ACTIVE, 'onActive'),随后才逐步推进至属性更新,两个回调的先后次序在内部被严格区分。配套的Tween.startDelay属性在初始化时被设置为"最短启动前时间",每帧递减直到归零后触发onStart,变更日志 对这一机制有明确说明。

属性配置升级:start / from / to 三段式

3.19 之前,补间属性只有"从当前值到目标值"一种模型,无法表达"先瞬移再补间"的复杂需求。3.19 为属性配置新增了fromstart键,与既有目标值to组合出三种模式(对应 Fix #4493):

// 模式一:from + to // 先在 delay 到期后把 alpha 瞬移到 0,再从 0 补间到 1 alpha: { from: 0, to: 1 } // 模式二:start + to // 补间一激活就立即把 alpha 置为 0,然后在整个 duration 内补间到 1 alpha: { start: 0, to: 1 } // 模式三:start + from + to // 激活立即置 0;delay 结束后置 0.5;再从 0.5 补间到 1 alpha: { start: 0, from: 0.5, to: 1 }

这套机制在源码中由 TweenData.js 承担:getStartValue负责取start阶段的起始值,getEndValue负责取to目标值,getActiveValue(即TweenData.getActiveValue属性)在非空时提供"激活瞬间立即写入目标属性的值"。TweenData 完成时会把current精确置为startend(取决于播放方向),并把这个终值写入目标属性,保证补间结束后属性值与目标严格一致。

seek 重写:任意时间点精确跳转

Tween.seek在 3.19 被彻底重写(Fix #4409),现在可以在补间未播放或已播放时,跳转到任意时间点——无论补间是否包含 repeat、loop、delay 或 hold 设置。关键设计是:

  • 跳转过程中默认不触发任何事件与回调(通过内部isSeeking标志控制,见 Tween.js#L75-L80);
  • 方法签名为seek(amount, delta, emit),其中delta控制跳转的步长(默认 16.6ms,步长越大跳转越快但精度越低),emittrue时允许跳转过程发出事件;
  • 实现上先重置并重新初始化补间数据,再按Math.floor(amount / delta)迭代步进到目标时间点,详见 Tween.js#L500-L534。
var tween = this.tweens.add({ targets: image, x: 1000, duration: 2000, repeat: 3 }); // 直接跳到补间开始后 1500ms 的位置(不触发任何事件) tween.seek(1500);

新增 StaggerBuilder:多目标交错补间

StaggerBuilder是 3.19 新增的构建器函数(src/tweens/builders/StaggerBuilder.js),它返回一个"交错函数",Tween 系统会为每个目标调用该函数,基于目标索引、总目标数及配置计算出该目标的属性值(如 delay)。它通过this.tweens.stagger(...)暴露给用户,入口见 TweenManager.js#L536-L574。

三种基本用法(源码 JSDoc 与 TweenManager.js 均有完整示例):

// 1) 固定步长:每个目标依次延迟 100ms this.tweens.add({ targets: [spriteA, spriteB, spriteC], scale: 0.2, ease: 'linear', duration: 1000, delay: this.tweens.stagger(100) }); // 2) 区间步长:delay 在 500ms ~ 1000ms 之间在所有目标间均匀分布 delay: this.tweens.stagger([ 500, 1000 ]) // 3) 网格 + 方向 + 缓动:10x6 网格,从中心向外交错,使用 cubic.out 缓动 delay: this.tweens.stagger(500, { grid: [ 10, 6 ], from: 'center', ease: 'cubic.out' })

StaggerConfig支持的关键配置项(对应 StaggerBuilder.js 的参数解析):

配置键说明
start交错结果的起始偏移量,默认0
ease对交错数值应用缓动函数(如'cubic.out'),默认null
grid形如[宽度, 高度]的网格数组,按网格坐标计算欧氏距离而非线性索引
from交错起点:'first'(首个目标)、'last'(末个目标)、'center'(从中心向外),或一个数值索引,默认0

值得注意的实现细节:

  • 网格模式预计算:一旦提供grid,构建器会预先计算每个网格单元到起点的距离并缓存在gridValues二维数组中(StaggerBuilder.js#L79-L130),避免每次更新重复计算;
  • 数值模式from为数字时使用Math.abs(from - index)作为交错索引,因此可以指定任意起始目标;
  • 区间模式[value1, value2]的差值被均分到所有目标上,配合缓动函数可生成非线性交错。

仓库的测试用例 tests/tweens/builders/StaggerBuilder.test.js 覆盖了上述行为:默认数值模式下index 0返回 0、index 1返回value1 * 1start偏移叠加、浮点交错值、单目标(total=1)边界、从 center 向外扩散、from: 'last'从末到首、from数值索引,以及 0 值与负值交错等场景,可直接作为功能契约参考。

回调签名与内部机制的连带调整

3.19 对与 Tween 相关的若干函数签名做了统一,使用自定义函数的用户需要关注:

  • getStart/getEnd自定义属性函数的签名由(target, key, value)扩展为(target, key, value, targetIndex, totalTargets, tween),新参数追加在末尾,旧函数无需改动即可继续工作;
  • LoadValue 生成器函数(如delayrepeat)的签名同步改为同样的六参数形式,若你自定义过此类生成器,需要按新签名修改;
  • TweenData构造函数新增indexgetActive参数(TweenData.js 的类注释有完整说明),直接创建 TweenData 的代码需使用新签名;
  • easeParams此前只对字符串形式的缓动名生效,现在对任何自定义缓动函数同样生效(Fix #3826);
  • GetEaseFunction现在接受更宽松的字符串输入:支持小写(如back),也支持省略方向中的ease前缀(如back.inback.inout);
  • Tween 与 Timeline 的state变更都会先于事件/回调设置,允许你在事件处理器中安全地修改 Tween 状态;
  • TIMELINE_LOOP_EVENT移除了语义错误的loopCounter参数;
  • 通过TweenManager.create创建的补间现在无需手动激活,直接调用play即可启动(Fix #4632);
  • Tween.onLoop/onRepeat回调严格在对应延迟(loopDelay/repeatDelay)到期后触发,Timeline 的onLoop/onComplete也遵循同样的延迟语义。

Shader 离屏渲染:让着色器输出成为纹理

3.19 为 Shader.js 引入了完整的离屏渲染管线,核心是Shader.setRenderToTexture方法(src/gameobjects/shader/Shader.js#L395-L424)。调用后 Shader 不再直接绘制到显示列表,而是渲染到自己的帧缓冲 / WebGLTexture,从而实现:

  1. 着色器级联:把一个 Shader 的输出作为另一个 Shader 的sampler2D输入;
  2. 纹理化:把 Shader 输出注册到 Texture Manager,供 Sprite、Image 等任何基于纹理的游戏对象使用。
var shader = this.add.shader('myShader', x, y, width, height); // 将 shader 离屏渲染,并注册为名为 'doodle' 的纹理 shader.setRenderToTexture('doodle'); // 直接使用该纹理创建 Image this.add.image(400, 300, 'doodle');

源码实现要点(Shader.js#L395-L424):

  • 方法内部创建一个与 Shader 同尺寸的离屏相机和DrawingContextglTexture保存 WebGLTexture 引用;
  • 传入key时会调用scene.sys.textures.addGLTexture(key, this.glTexture)将纹理注册进全局 Texture Manager;
  • 一旦启用,renderToTexture标志置为true,Shader 每帧刷新离屏纹理,因此引用该纹理的 Sprite 会随 Shader 实时更新;
  • 注意它保存的是活动引用:销毁 Shader 前务必清理使用该纹理的对象。

配套 API 一览:

API说明
Shader.setSampler2DBuffer(texture)将某个 WebGLTexture 直接作为 Shader 的 sampler2D uniform 传入,用于多 Shader 互相作为缓冲
Shader.renderToTexture布尔属性,标记 Shader 是否处于离屏渲染状态
Shader.framebuffer保存 WebGLFramebuffer 引用
Shader.glTexture保存 WebGLTexture 引用
Shader.texture保存注册到 Texture Manager 后的 Phaser Texture 引用
TextureManager.addGLTexture(key, glTexture)新方法,见 src/textures/TextureManager.js#L525,把 WebGLTexture 按 key 注册进纹理管理器
TextureSource.isGLTexture布尔属性,标记底层数据是否为 WebGLTexture
TextureTintPipeline.batchSpriteGLTexture 来源的 TextureSource 渲染时自动翻转 UV

渲染快照:从 Render Texture、帧缓冲与 Canvas 取图

3.19 把"截图"能力系统化,覆盖 WebGL 与 Canvas 两条渲染路径,并统一支持"单像素取色"与"区域取图"两种形态。

Render Texture 快照

RenderTexture.js 新增三个方法:

  • snapshot(callback, type, encoderOptions)(src/gameobjects/rendertexture/RenderTexture.js#L646):对整个 Render Texture 当前状态截图,返回 Image 对象;
  • snapshotArea(x, y, width, height, callback, type, encoderOptions)(RenderTexture.js#L615):截取指定区域;
  • snapshotPixel(x, y, callback)(RenderTexture.js#L672):提取单个像素,返回 Color 对象。
// 截取整个 Render Texture 并打印 Image renderTexture.snapshot(function (image) { console.log(image); // 可进一步 this.textures.addImage('saved', image) 存入纹理管理器 }); // 提取 (10, 10) 处的像素颜色 renderTexture.snapshotPixel(10, 10, function (color) { console.log(color.r, color.g, color.b, color.a); });

WebGL 帧缓冲与 Canvas 快照

  • WebGLRenderer.snapshotFramebuffer配合工具函数WebGLSnapshot:可对任意 WebGL 帧缓冲(如 Render Texture 或 Shader 使用的那个)取单像素 Color 或区域 Image,并可选保存到 Texture Manager;
  • CanvasRenderer.snapshotCanvas:对任意 Canvas 对象执行同样的两种取图操作;
  • SnapshotState对象新增isFramebuffer布尔值与bufferWidthbufferHeight整数属性,用于描述快照来源。

纹理管理器的配套能力

  • RenderTexture.glTexture属性直接暴露 Render Texture 底层的 WebGLTexture,方便作为 sampler2D 传给 Shader;
  • TextureManager.getBase64现在对非图像纹理(如 WebGL 纹理)会发出控制台警告;
  • CanvasTexture.update在 WebGL 下会自动调用refreshdrawdrawFrame均如此),Canvas 模式下无需再手动 refresh;
  • CanvasTexture.getPixels默认区域改为"0x0 至 宽 x 高",无参数调用即可取得全部像素。

新特性速查:输入调试、几何类型、上下文事件与更多

输入命中区调试可视化

  • InputPlugin.enableDebug(gameObject, color)(src/input/InputPlugin.js#L2632):为指定游戏对象的命中区创建一个调试形状,实时跟踪该对象,帮助检查命中区大小与位置;
  • InputPlugin.removeDebug(gameObject)(InputPlugin.js#L2751):移除并销毁调试形状;
  • Pointer.locked只读属性:通过 Pointer Lock API 判断指针是否已锁定;
  • Pointer.updateWorldPoint(camera):基于相机变换更新指针的worldX/worldY
  • Pointer.movementX/movementY在指针锁定时直接取自 DOM 事件值(不再增量累加,Fix #4611);
  • Pointer.velocityPointer.midPoint现在每帧更新(无论指针是否移动),基于motionFactor平滑衰减。

WebGL 上下文丢失与恢复

3.19 用事件取代了旧的回调机制:

  • Game.CONTEXT_LOST_EVENT:WebGL 上下文丢失时由 Game 实例派发;
  • Game.CONTEXT_RESTORED_EVENT:上下文恢复时派发;
  • 对应的WebGLRenderer.lostContextCallbacks/onContextLostrestoredContextCallbacks/onContextRestored已移除,事件常量定义于 src/core/events。
this.game.events.on('CONTEXT_LOST', function () { // 保存关键状态,准备恢复 }); this.game.events.on('CONTEXT_RESTORED', function () { // 重新加载纹理、重建缓冲 });

几何类型常量

新增GEOM_CONST常量对象(src/geom/const.js),统一标识各几何体类型。CircleEllipseLinePointPolygonRectangleTriangle均新增只读type属性,用于快速类型比较,配合 src/geom 下各几何模块使用。

其他值得关注的新能力

  • Math.ToXY(index, width, height, out?)(src/math/ToXY.js):把一维索引转换为网格中的Vector2坐标,例如 6×4 网格中索引 16 返回(4, 2),超出范围返回零向量;
  • GroupCreateConfig.quantity:用配置对象创建 Group 时可通过quantity直接指定创建数量,适用于不需要frameQuantity/repeat高级能力的单帧对象批量创建;
  • PluginManager.removeGameObject/GameObjectFactory.remove/GameObjectCreator.remove:自定义游戏对象类型插件销毁时可反注册工厂与创建器,避免污染全局命名空间;
  • Pointer新属性buttonleftButtonReleased/rightButtonReleased/middleButtonReleased/backButtonReleased/forwardButtonReleased方法(由 3.18 引入,3.19 持续完善指针事件处理);
  • Texture.remove(name):按名称从 Texture 中移除 Frame(Fix #4460);
  • WebGLRenderer.currentType/newType/nextTypeMatch:暴露当前渲染对象的类型信息,为跨对象批处理提供判断依据。

Bug 修复与行为修正精选

3.19 修复了大量影响实际开发的问题,以下挑选与日常开发最相关的几类:

渲染与翻转类

  • Sprite 设置flipX/flipY后偏移帧渲染错位、动画抖动的问题(Fix #4636 / #3813);
  • 带自定义枢轴的动画(如 Texture Packer pivot 生成的)翻转后错位(Fix #4155);
  • Arc / Circle 形状在 WebGL 下中心偏移半半径的问题(Fix #4620);
  • Origin.updateDisplayOrigin不再对显示原点做Math.floor,1×1 像素对象可以正确使用 0.x 原点(Fix #4126);
  • TransformMatrix.rotation现在返回正确归一化后的旋转值。

输入与指针类

  • Pointer.getDuration在桌面返回负值 / 移动端返回 NaN(Fix #4612),downTime/upTime/moveTime的 NaN 问题同步修复;
  • InputManager.resetCursor会先检查 canvas 元素是否存在(Fix #4662);
  • pointerdown/pointerup处理器中调用Scene.destroy不再因访问已销毁的管理器而报错(Fix #4436);
  • POINTERLOCK_CHANGE事件恢复由 Input Manager 派发,requestPointerLock()不再报错。

Tilemap 与物理类

  • Tilemap.renderDebug因调用过期的 Graphics API 而失败的问题;
  • Tilemap.createFromObjects现在会使用传入的scene参数;
  • Matter.Factory.constraint/joint/worldConstraint支持零长度约束(长度可设为 0 或省略自动计算);
  • DynamicTilemapLayer.destroy/StaticTilemapLayer.destroy增加幂等保护,不会重复执行销毁序列。

Scale Manager 与生命周期类

  • 全屏模式下报错 'TypeError: this.removeFullscreenTarget is not a function'(Fix #4605);
  • 缩放值无法从其他值改回 1(Fix #4633);
  • ScaleManager._resetZoom新增内部标志,在游戏缩放因子变化时置位;
  • HEADLESS 模式销毁 Scene 时不再因访问 gl renderer 而抛错(Fix #4467)。

Tween 专项修复

  • Tween.restart会把elapsedprogresstotalElapsedtotalProgress归零而不是累加;
  • Animation.setRepeat能正确重置repeatCounter,使已绑定的补间实例同步改变重复次数(Fix #4553);
  • 2 帧动画移除一帧后再渲染不再报错(Fix #4621);
  • Shader.uniforms改用深拷贝(Extend 而非 Clone),避免多个 Shader 实例共享 uniforms(Fix #4641)。

变更带来的迁移注意事项

如果你正从 3.18 或更早版本升级,需要关注以下破坏性 / 行为变化:

  1. onStart语义变化:依赖"激活即触发"逻辑的代码应改用新的onActive
  2. seek默认静默:旧代码中依赖 seek 触发回调的行为不再成立,需要时显式传入emit = true
  3. 自定义生成器签名扩展:自定义delay/repeat生成器与getStart/getEnd函数需要适配新的六参数签名;
  4. 上下文回调移除onContextLost/onContextRestored已被CONTEXT_LOST/CONTEXT_RESTORED事件取代;
  5. Timeline 事件参数变化TIMELINE_LOOP_EVENT不再携带loopCounter参数。

当前仓库的 Tween 实现已演进至 src/tweens(含tween/BaseTween.jstween/Tween.jstween/TweenData.jsbuilders/StaggerBuilder.js等),配套测试位于 tests/tweens,3.19 引入的机制(事件分发、start/from/to 属性解析、stagger 交错计算等)至今仍是 Phaser 3 后续版本 Tween 系统的基础,阅读 tests/tweens/builders/StaggerBuilder.test.js 与 tests/tweens/TweenManager.test.js 可以进一步验证这些行为的边界条件。

【免费下载链接】phaserPhaser is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.项目地址: https://gitcode.com/gh_mirrors/ph/phaser

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询