Web端Godot优化:多PCK文件如何复用单引擎实例?
问题描述
我正在重构一个原包含Flash游戏的旧网站,用Godot重写了部分游戏,编译Godot源码时禁用了3D物理等不必要模块,当前godot.wasm大小约29MB。但切换游戏时,CPU占用过高会导致页面冻结2-3秒(Mac M2 Max设备),网站有多款游戏,每次加载都会出现这个延迟。
每个游戏都在独立仓库,编译生成单独的PCK文件。我用Vue单页应用(SPA)尝试复用Engine实例避免重复加载Wasm,代码如下:
const engineInstance = shallowRef(new Engine({ /* ... 我的参数,没有mainPack */ executable: '/gamesdata/godot', fileSizes: { '/gamesdata/godot.wasm': 29107076, }, })) const isEngineLoaded = ref(false) const currentPck = ref(null) engineInstance.value.init('/gamesdata/godot').then(() => {isEngineLoaded.value = true}) export function useGodot() { const loadPck = async (pckPath, canvasElement, sizeOfPck, args = []) => { if (!engineInstance.value || !isEngineLoaded.value) { throw new Error('Engine not initialized') } args = ['--main-pack', pckPath, ...args] // 加载新PCK await engineInstance.value.preloadFile(pckPath, pckPath, sizeOfPck) await engineInstance.value.start({ args, canvas: canvasElement.id, mainPack: pckPath, }) currentPck.value = pckPath } }
但发现无论如何都必须调用Engine.init(),请问有没有方案可以避免高CPU占用或重复加载godot.wasm?该如何优化?
优化方案
1. 正确复用Godot Engine实例,避免重复初始化
你当前的代码每次调用start()都会重新触发引擎初始化流程,这是CPU飙升的核心原因。正确的复用逻辑应该是:
- 引擎
init()只执行一次,完成后保留实例 - 切换游戏时先清理当前游戏资源,再加载新PCK启动,而非重复初始化
修改后的核心逻辑示例:
const engineInstance = shallowRef(null) const isEngineLoaded = ref(false) const currentGameInstance = ref(null) // 仅初始化一次引擎 async function initEngine() { if (engineInstance.value) return engineInstance.value = new Engine({ executable: '/gamesdata/godot', fileSizes: { '/gamesdata/godot.wasm': 29107076, }, // 提前禁用非必要模块,降低初始化负载 args: ['--disable-3d', '--no-window'], asyncInit: true // 启用Wasm异步编译,避免阻塞主线程 }) await engineInstance.value.init('/gamesdata/godot') isEngineLoaded.value = true } export function useGodot() { const loadPck = async (pckPath, canvasElement, sizeOfPck, args = []) => { await initEngine() if (!engineInstance.value) throw new Error('Engine init failed') // 清理当前游戏资源 if (currentGameInstance.value) { currentGameInstance.value.stop() // 把卸载操作放到空闲回调,避免阻塞主线程 requestIdleCallback(async () => { await engineInstance.value.unloadFile(pckPath) // 触发浏览器GC,释放闲置内存 typeof gc === 'function' && gc() }) } // 预加载新PCK await engineInstance.value.preloadFile(pckPath, pckPath, sizeOfPck) // 启动新游戏,无需重复调用init() currentGameInstance.value = await engineInstance.value.start({ args: ['--main-pack', pckPath, ...args], canvas: canvasElement.id, mainPack: pckPath, }) } }
2. 优化PCK资源加载
- 预加载高频游戏PCK:在用户浏览网站时,利用空闲时间后台预加载热门游戏的PCK,减少切换时的等待
- 开启PCK压缩:Godot导出时启用「压缩」选项,缩小PCK体积,降低加载和解压耗时
- 提取公共资源:把多款游戏共用的UI、音效等资源拆分到独立公共PCK,仅加载一次,避免重复加载冗余资源
3. 进一步裁剪Godot编译模块
除了3D物理,还可以在编译Godot源码时禁用以下非必要模块,进一步降低Wasm体积和初始化开销:
- 不需要音频的游戏:禁用
module_audio - 不需要网络的游戏:禁用
module_network - 不需要编辑器功能:确保编译的是导出模板而非开发版
4. 浏览器层面优化
- 配置HTTP缓存:确保godot.wasm和PCK文件设置合理的缓存头,避免重复下载
- 限制Wasm线程数:如果游戏不需要多线程,启动时添加
--threads 1参数,减少CPU资源抢占 - 使用Service Worker:预缓存核心资源,加速后续访问时的加载速度
内容的提问来源于stack exchange,提问作者Sw0ut
相关产品推荐
相关产品推荐

