先说结论:行
已验证跑通的能力一览:
渲染(CANVAS / WEBGL 两种模式)
触摸输入(点击变色、拖动跟随)
wx.setStorageSync 持久化存储(跨编译、跨渲染模式切换均保持)
真实图片贴图加载
Graphics.generateTexture 纹理烘焙
JSON 配置文件加载(this.load.json())
音频加载、解码与播放(this.load.audio() + this.sound.play(),原生 AudioContext)
Tween 动画驱动 Container
多 Scene 切换(scene.start)配合相机 fadeOut/fadeIn
两种模式下均确认以下功能正常:渲染、触摸输入(点击变色、拖动跟随)、 wx.setStorageSync 计数在跨编译及切换渲染模式后的持久化、真实图片贴图加载、 Graphics.generateTexture 纹理烘焙(ProceduralIcons.ts 重度依赖的技术)、JSON 配置文件加载(this.load.json(),、音频加载与解码播放(this.load.audio() 配合 this.sound.play(),可 正常听到点击音效——该环境原生提供真实的 AudioContext,Phaser 自动选用了完整 实现的 WebAudioSoundManager,而非降级后的空实现,这是本次验证中较为意外的结果, 因为音频历来被认为是小游戏平台适配中最容易出现问题的部分)、Tween 动画驱动 Container、场景切换(scene.start)配合相机 fadeOut/fadeIn(后者对应SceneTransition.ts 中 goToScene 辅助函数所依赖的核心技术,前者是项目内 按钮悬停、面板弹出、结算数字滚动等 UI 动效的实现基础)。
结论:Phaser 4.0.0 在微信小游戏环境中 两种渲染模式均可正常运行,"是否需要降级至 Phaser 3"这一前提不再成立,目前评估 无需降级。如需切换验证的渲染模式,修改 game.js 中 type: Phaser.WEBGL / Phaser.CANVAS 一行即可。
验证项目git地址
以下为该项目接入小游戏的示例git,已在微信开发者工具中验证可正常运行:
关键文件
game.js:项目入口,创建一个最小化的 Phaser Scene(绘制方块与文字,支持触摸 变色/跟随,点击计数持久化存储)
weapp-adapter.js:核心文件 —— 提供使 Phaser 能够在小游戏环境中正常运行的 最小适配层,完全自行实现,不依赖任何社区版 weapp-adapter
scripts/patch-phaser.js:npm install 后自动执行,用于修补 node_modules/phaser 的 package.json(原因见下文)
如果想简单接入可以直接使用我的git中的weapp-adapter.js,也可以自行编写,自行编写看文章后半
直接使用我提供的weapp-adapter.js
1.复制适配层
复制 weapp-adapter.js 到项目根目录,在入口文件最顶部、require('phaser') 之前 require 它。
2.修复npm构建
复制 scripts/patch-phaser.js 到项目,package.json.scripts 添加:
"postinstall": "node scripts/patch-phaser.js"
3.图片加载开关
new Phaser.Game({
canvas: GameGlobal.WXAPP_MAIN_CANVAS,
loader: { imageLoadType: 'HTMLImageElement' },
...
})
4.补齐配置文件
project.config.json: compileType = "game"
game.json: 补 deviceOrientation 字段
(appid 缺省用 touristappid)
5.装依赖并编译
npm install(触发步骤2补丁)→ 微信开发者工具:构建npm → 编译
这 5 步的完整代码和踩坑细节都在这个仓库的 README 里,中英文都有有问题欢迎评论区讨论。
如果你也要手动搬运写 weapp-adapter.js ,这里有你需要解决的问题
第一步:解决 npm 包不能直接用的问题
Phaser 4 的 package.json 用了新版 exports 字段,微信"构建 npm"工具不认,会退回读 main 字段——而 main 字段指向的是没编译的源码目录,一堆用不到的调试依赖也会被带进来。
解法:装个 postinstall 脚本,npm install 之后自动把 node_modules/phaser/package.json 里的 main 字段改成 ./dist/phaser.js,也就是编译好的产物。三行代码,读文件、改字段、写回去。
第二步:在 Phaser 代码执行之前,先垫一层"假浏览器"
新建一个适配层文件,在 require('phaser') 之前先 require 它。核心工具函数只有一个:safeAssign——遇到运行时已经预置、但是 getter-only 不可写的属性(比如 window、document 本身),就跳过不硬赋值,避免报错炸掉整个加载。
垫的东西按优先级分三类:
直接给值的:self/top/parent/global 全部指向 GameGlobal;navigator 给个假的 userAgent;localStorage 包一层 wx.getStorageSync/setStorageSync。
先判断原生有没有,没有才垫:requestAnimationFrame——这个版本运行时其实自带了,直接用自己的反而会覆盖掉更好的原生实现,踩过这个坑。
不能整体替换,只能打补丁:document 本身也是预置的残缺对象,documentElement 是 undefined,只能在原对象上一个个补字段(body、head、createElement、querySelector、elementFromPoint 这些),整体换掉会被判定为不可覆盖直接跳过。
第三步:单独处理两个高风险点
图片加载:默认走 XHR + Blob,小游戏没有 Blob。不用自己实现兼容,Phaser 自带配置开关,Game 初始化时传 loader: { imageLoadType: 'HTMLImageElement' },直接切到 new Image() 那条路径,配合适配层里把全局 Image 指到 wx.createImage()。
JSON / 音频加载:走的是 XHR 但不涉及 Blob,只能自己实现一个最小 XMLHttpRequest 类——本地相对路径用 wx.getFileSystemManager().readFile() 读包内文件,http(s) 开头的远程地址用 wx.request()。只要覆盖 Phaser 实际用到的那几个方法(open/send/onload/onerror/status/response 等),不用做成通用实现。
第四步:WebGL 模式额外一个坑
如果 instanceof 探测用的构造器(比如 WebGLRenderingContext)随手垫成一个空函数,会导致 Phaser 内部"要不要给 WebGL1 补 bindVertexArray 等方法"的判断永远为 false,切到 WEBGL 模式直接报错。正确做法:用 wx.createCanvas().getContext('webgl') 探测出真实上下文,再取它的 Object.getPrototypeOf(gl).constructor 作为构造器,不能凭空造一个。
收尾:整个适配层加起来不到 300 行,没有依赖任何第三方 weapp-adapter,改造成本主要是"踩坑"而不是"写代码量"。具体每一步报错和对应解法,仓库 README 里按时间顺序记了 11 条。
更多踩坑过程可以看git项目的readme,需要说明的是,这个项目由ai辅助完成。