mcpField
P
phaser-fx

phaser-fx

github.com/yakoub-ai开发2970 次安装9 周前更新
代码仓库

关于 phaser-fx

phaser-fx 是面向 Phaser 4 开发者的参考技能,涵盖滤镜、遮罩、光照、着色和 Beam 渲染器。它说明 Phaser 3 到 Phaser 4 的 API 变化,并介绍效果实现与性能取舍。

主要功能

  1. 01Phaser 4 滤镜目录
  2. 02内部与外部滤镜用法
  3. 03遮罩与模板技术
  4. 04着色模式与颜色效果
  5. 05光照与锥形光
  6. 06Phaser 3 迁移说明

适用场景

  1. 01实现游戏对象与摄像机效果
  2. 02替换 Phaser 3 遮罩和特效
  3. 03制作光照与视野锥效果
  4. 04评估滤镜的性能取舍

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-fx

安装后在 Claude Code 中输入/phaser-fx确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
filters-api.md
lighting.md
renderer-and-shaders.md

详细介绍

Phaser 4 滤镜、遮罩、光照与 Beam 渲染器

Phaser 4 全面替换了 v3 渲染器。FX 和遮罩在 v3 中是两个彼此独立且互不兼容的系统,如今合并为一个名为 Filters 的系统,可用于所有游戏对象和摄像机,没有例外。

如果你正在转换 v3 代码,请从底部的转换表开始。如果你正在编写新代码,请从这里开始。


一条能避免所有人踩坑的规则

游戏对象必须先调用 enableFilters()。摄像机则不需要。

// Game object — filters is null until you enable it
sprite.enableFilters();
sprite.filters!.internal.addGlow(0xff00ff, 4, 0, 1);

// Camera — filters is always there
this.cameras.main.filters.external.addVignette(0.5, 0.5, 0.5, 0.5);

对一个刚创建的精灵调用 sprite.filters.internal.addBlur() 会抛出异常,因为 filters 为 null。这是最常见的 Phaser 4 特效错误,而错误消息(Cannot read properties of null)并没有指出原因。


内部与外部

每个滤镜列表都有两部分,选错部分是第二常见的错误。

filters.internalfilters.external
空间对象自身的空间渲染上下文,通常是全屏
是否随对象移动是否
分辨率对象的尺寸整个画布
开销较低较高
用途精灵发光、面板遮罩全屏色彩分级、暗角
// A glow that follows the enemy: internal.
enemy.enableFilters();
enemy.filters!.internal.addGlow(0xff0000, 8, 0, 1);

// A full-screen red wash when the player is hurt: external, on the camera.
this.cameras.main.filters.external.addColorMatrix();

有些对象无法定义内部空间,会自动回退到外部空间:没有宽度或高度的对象,以及描边超出其报告边界的 Shape 对象。如果形状上的内部滤镜表现得像屏幕特效,原因就在这里。


滤镜目录

每个滤镜都是对 filters.internal 或 filters.external 调用一个 add* 方法:

滤镜功能
addBlur(quality, x, y, strength)高斯模糊
addGlow(color, outerStrength, innerStrength, scale)轮廓发光
addShadow(x, y, decay, power, color, samples, intensity)投影
addBokeh(radius, amount, contrast)景深风格模糊
addPixelate(amount)像素化
addBlocky(amount)块状量化
addVignette(x, y, radius, strength)边缘变暗
addBarrel(amount)镜头挤压/扩张畸变
addDisplacement(texture, x, y)使用纹理进行位移——热浪、水面效果
addColorMatrix()色彩分级(见下文)
addCombineColorMatrix()混合两个色彩矩阵
addGradientMap(...)通过渐变重新映射亮度
addThreshold(...)Alpha/颜色截断
addQuantize(...)减少颜色数量
addMask(source, invert, viewCamera, viewTransform, scaleFactor)遮罩
addKey(...)色度键控
addBlend(...)WebGL 四种原生混合模式之外的混合模式
addTiltShift(...)移轴效果
addPanoramaBlur(...)全景模糊
addSampler(...)从其他纹理取样
addImageLight(...)基于图像的光照
addNormalTools(...)法线贴图处理
addParallelFilters(...)运行两条滤镜链并合并结果
addCustom(...)自定义着色器
ColorMatrix 有一个嵌套属性

颜色相关方法位于 colorMatrix 子属性上,而不在滤镜本身:

const cm = sprite.filters!.internal.addColorMatrix();
cm.colorMatrix.sepia();       // correct
cm.colorMatrix.grayscale();
cm.colorMatrix.night(0.3);
// cm.sepia();                // wrong — this was the v3 shape
复合特效位于 Actions 中

一些 v3 FX 被重建为 Actions,因为它们需要多个滤镜协同工作:

Phaser.Actions.AddEffectBloom(target);   // was the Bloom FX
Phaser.Actions.AddEffectShine(target);   // was the Shine FX
Phaser.Actions.AddMaskShape(target);     // was the Circle FX

遮罩

BitmapMask 已不存在。createGeometryMask() 和 createBitmapMask() 不是游戏对象的方法。GeometryMask 仍作为类保留,但只有已弃用的 Canvas 渲染器会使用它——在 Beam 下完全不起作用。

有两种替代方案,选择取决于形状:

矩形裁剪 → 摄像机视口

滚动面板、小地图、分屏。比滤镜开销更低,而且像素精确。

const panelCam = this.cameras.add(x, y, width, height);
panelCam.setScroll(scrollX, scrollY);
panelCam.ignore(this.children.list.filter(c => c !== panelContent));
this.cameras.main.ignore(panelContent);

Phaser 4 中没有 camera.setScissor()。如果你在指南中看到这个调用,该方法并不存在——请使用 setViewport()。

其他任意形状 → Mask 滤镜
const shape = this.add.graphics();
shape.fillStyle(0xffffff).fillCircle(64, 64, 64);

content.enableFilters();
content.filters!.internal.addMask(shape);        // GameObject or texture key
content.filters!.internal.addMask(shape, true);  // inverted: hide inside, show outside

Mask 源可以是纹理键或游戏对象。如果传入对象,Phaser 会将其渲染到 DynamicTexture,然后使用该纹理——因此动画遮罩和由滤镜输出生成的遮罩都可用。scaleFactor 用于在遮罩精度和内存占用之间进行权衡;如果设置了它,请相应缩放遮罩源。

Stencil——持久遮罩(4.2.0+)

对于需要在多个对象和帧之间保持不变的遮罩,使用 Stencil 比反复运行 Mask 滤镜开销更低。与 v3 的 Stencil 遮罩不同,这些遮罩通用且持久,可接受任何能绘制像素的源。

const stencil = this.add.stencil();
stencil.add(this.add.circle(400, 300, 120, 0xffffff));

模式:addLayer、subtractLayer、clear、clearRegion,其中 add 和 subtract 均可反转。this.add.stencilreference() 会使用不同设置重新渲染现有的 Stencil,可用于复用或移除 Stencil 几何体。

Stencil 内部的常规 alpha 不起作用,因此 alpha 由 stencilAlphaStrategy 控制(见下文)。如果 stencilInvert 看起来没有效果,请使用 4.2.1 或更高版本——它在 4.2.0 中因 alpha 错误而无法正常工作。


着色

Phaser 4 将 tint 颜色 与 tint 模式 分开了。在 v3 中,你可以设置 tintFill = true;该属性现在已不存在。

sprite.setTint(0xff0000);
sprite.setTintMode(Phaser.TintModes.FILL);   // was tintFill = true

模式:MULTIPLY(默认)、FILL、ADD、SCREEN、OVERLAY、HARD_LIGHT、MULTIPLY_TWO。

MULTIPLY_TWO(4.2.0+)会为每个角使用第二种 tint 颜色,因此无需着色器即可实现渐变 tint——火焰、冰霜、反色:

sprite.setTint(0xff2200).setTint2(0xffee00).setTintMode(Phaser.TintModes.MULTIPLY_TWO);

Mesh2D 和 Tile 支持恒定的 tint2。TilemapGPULayer 完全不支持对图块进行 tint。

形状完全没有 tint。 Rectangle、Arc、Triangle 和其他 Shape 对象未实现 Tint 组件——它们没有 setTint() 和 setTintMode() 方法,编译器也会提示这一点。请改用 fillColor: rect.fillColor = 0xff0000。这个问题最常见于原型开发:先用彩色矩形进行测试,之后才发现伤害闪烁模式无法通过编译。

伤害闪烁是每款动作游戏都需要的效果:

hit() {
  this.sprite.setTint(0xffffff).setTintMode(Phaser.TintModes.FILL);
  this.scene.time.delayedCall(80, () => this.sprite.clearTint());
}

优先使用这种方式,而不是白色闪烁滤镜——它只需一次绘制调用,也不需要帧缓冲区。


光照

this.lights.enable().setAmbientColor(0x333333);
const torch = this.lights.addLight(x, y, 200, 0xffaa33, 2);

对象必须选择启用光照;组件和法线贴图设置请参阅 references/lighting.md。

锥形光源(4.2.0+)

手电筒、视野锥、车头灯、探照灯。它们使用同一光照着色器运行——无需遮罩、第二个摄像机或将场景渲染两次:

const beam = this.lights.addConeLight(
  x, y,
  300,        // radius
  0xffffff,   // colour
  2,          // intensity
  rotation,   // radians — the direction the cone points
  0.2,        // inner angle, radians
  0.6,        // outer angle, radians
);

beam.setConeRotation(Phaser.Math.Angle.Between(px, py, mx, my));
beam.setConeAngles(0.15, 0.5);
beam.disableCone();   // back to a normal point light

任何已启用 Phaser 光照的对象都可以受到锥形光源照射。因此,与 v3 的实现方式相比,潜行视野锥和火把机制的开销低得多。


性能

滤镜是帧缓冲区操作。每个滤镜都是一个渲染通道,而消耗就在这些通道上。

  • 闪光和颜色变化优先使用色调,而非滤镜。 色调属于批处理的一部分;滤镜则是单独的通道。
  • 优先使用一个摄像机滤镜,而非 N 个对象滤镜。 如果 20 个敌人各自都使用发光效果,就会多出 20 个通道。如果它们可以共用一个屏幕空间效果,就这样做。
  • 两种方式都可行时,优先使用内部滤镜——内部滤镜按对象尺寸渲染,外部滤镜则按完整画布渲染。
  • 滤镜渲染时绝不会使用 mipmap。 不要指望滤镜输出会通过 mipmap 变得柔和(4.1.0 修复了相反的问题:滤镜之前会错误地使用 mipmap)。
  • 只启用一次滤镜,不要每帧都启用。 在 create() 中调用 enableFilters(),然后在 update() 中调整控制器的属性。每帧调用 add* 会不断叠加滤镜,直到耗尽帧预算——这是很容易意外造成的泄漏,而且看起来像性能问题,而非逻辑问题。
  • Alpha 策略使用 discard, 这是设计使然,效率不高。dither 和 threshold 用于特效和模板,不是性能优化手段。

请使用 playtest 测试工具验证以上内容,而不是凭肉眼判断——负载下的帧率骤降会体现滤镜的开销,单次读数无法捕捉到这一点,而 sample 步骤可以:

{ action: 'sample', expression: `game.loop.actualFps`,
  duration: 3000, interval: 100, expect: { stat: 'min', atLeast: 50 } }

读取无头模式下的 FPS 时,只能用于比较,不能当作绝对数值。 测试工具通过 SwiftShader 运行 WebGL,而滤镜恰恰是软件渲染最不擅长的部分:包含发光、暗角、颜色矩阵和受光粒子发射器的场景,在无头模式下测得的帧率约为 12–20fps,而在任何真实 GPU 上都能以 60fps 运行。请在同一台机器上将一次运行与之前的运行进行比较;不要为滤镜密集型场景设置绝对阈值,也不要将无头模式下的数值报告为游戏性能。


从 Phaser 3 迁移

v3v4
sprite.preFX.addGlow()sprite.enableFilters(); sprite.filters!.internal.addGlow()
sprite.postFX.addBlur()sprite.enableFilters(); sprite.filters!.external.addBlur()
camera.postFX.addVignette()camera.filters.external.addVignette()(无需启用步骤)
new BitmapMask(...)、setMask()filters.internal.addMask(source)
createGeometryMask()摄像机视口或 addMask()
sprite.tintFill = truesprite.setTintMode(Phaser.TintModes.FILL)
setPipeline('X')渲染节点——customRenderNodes、RenderConfig#renderNodes
Bloom FXPhaser.Actions.AddEffectBloom()
Shine FXPhaser.Actions.AddEffectShine()
Circle FXPhaser.Actions.AddMaskShape()
Gradient FXGradient 游戏对象
colorMatrixFilter.sepia()filter.colorMatrix.sepia()
Mesh / PlaneMesh2D(4.2.0+)

在确认代码没有问题前,先运行迁移 grep:

grep -rn "preFX\|postFX\|BitmapMask\|createGeometryMask\|tintFill\|setPipeline\|setScissor" src/

附加资源

参考文件
  • references/filters-api.md — 所有滤镜的完整参数列表、详细示例,以及每种效果的内部/外部选择建议。
  • references/lighting.md — 光照模型、法线贴图、锥形光、Lighting 组件和性能范围。
  • references/renderer-and-shaders.md — Beam、渲染节点、自定义着色器、Mesh2D、SpriteGPULayer、Stencil、CustomContext 和 Alpha 策略。
相关内容
  • skills/phaser-migrate/ — 完整的 v3→v4 变更列表和发行说明
  • skills/phaser-particles/ — 粒子效果,通常会与这些内容搭配使用
  • skills/phaser-analyze/references/performance-playbook.md — 特效何时会消耗帧时间

评论

支持 Markdown