如何解决Kaboom.js异步资源加载引发的地图与精灵显示问题
Kaboom.js部署后精灵/地图不显示的问题解决
问题诊断
控制台的drawImage错误说明Kaboom尝试使用的图片资源无效,结合本地正常、部署异常的现象,核心原因有两个:
loadSprite接收了URL对象而非字符串URL,部署环境下资源解析失败- 资源加载流程和场景初始化的逻辑顺序错误,导致资源未加载完成就尝试渲染
修复步骤
1. 传递字符串URL给loadSprite
Kaboom的loadSprite期望接收字符串格式的资源路径,而非URL对象。将new URL(...)改为new URL(...).href,确保传递正确的字符串路径:
// 原代码 k.loadSprite ("spritesheet", new URL ('../spritesheet.png', import.meta.url), { ... }) // 修改后 k.loadSprite ("spritesheet", new URL ('../spritesheet.png', import.meta.url).href, { ... }) // 地图资源同理 k.loadSprite ("map", new URL ('../map.png', import.meta.url).href);
2. 修正场景初始化逻辑
场景内部调用k.go("main")会导致无限递归跳转,应该将场景跳转放在k.load()的回调末尾,确保所有资源加载完成后再启动场景:
k.load(() => { k.setBackground(k.Color.fromHex("#311047")); // 定义场景(移除内部的k.go("main")) k.scene("main", async () => { // ... 原场景代码 // 移除这里的k.go("main") }); // 所有资源加载完成后启动场景 k.go("main"); });
3. 合并重复的DOMContentLoaded监听
代码中有两个DOMContentLoaded监听,合并为一个,避免逻辑重复执行:
document.addEventListener("DOMContentLoaded", () => { // Kaboom资源加载和场景初始化逻辑 console.log('import.meta.url:', import.meta.url); k.loadSprite ("spritesheet", new URL ('../spritesheet.png', import.meta.url).href, { sliceX: 39, sliceY: 31, anims: { "idle-down": 936, "walk-down": { from: 936, to: 939, loop: true, speed: 8 }, "idle-side": 975, "walk-side": { from: 975, to: 978, loop: true, speed: 8 }, "idle-up": 1014, "walk-up": { from: 1014, to: 1017, loop: true, speed: 8 }, "bag-open": 128, }, }); k.loadSprite ("map", new URL ('../map.png', import.meta.url).href); k.load(() => { k.setBackground(k.Color.fromHex("#311047")); k.scene("main", async () => { const mapJsonUrl = new URL('../map.json', import.meta.url).href; console.log('Map JSON URL:', mapJsonUrl); const mapData = await (await fetch(mapJsonUrl)).json(); console.log('Map data:', mapData); const layers = mapData.layers; console.log('Layers:', layers); const map = k.add ([ k.sprite ("map"), k.pos (0, 0), k.scale (scaleFactor) ]); const player = k.make ([ k.sprite ("spritesheet", { anim: "idle-down" }), k.area ({ shape: new k.Rect (k.vec2(0, 3), 10, 10), }), k.body (), k.anchor ("center"), k.pos (), k.scale (scaleFactor), { speed: 250, direction: "down", isInDialog: false, }, "player", ]); for (const layer of layers) { if (layer.name == "Boundaries") { for (const boundary of layer.objects) { map.add ([ k.area ({ shape: new k.Rect (k.vec2(0), boundary.width, boundary.height), }), k.body ({isStatic: true}), k.pos (boundary.x, boundary.y), boundary.name, ]); if (boundary.name) { player.onCollide(boundary.name, () => { player.isInDialog = true; displayDialog( dialogData[boundary.name], () => (player.isInDialog = false) ); }); } } continue; } if (layer.name == "Spawnpoint") { for (const entity of layer.objects) { if (entity.name == "player") { player.pos = k.vec2( (map.pos.x + entity.x) * scaleFactor, (map.pos.y + entity.y) * scaleFactor ); k.add(player); continue; } } } } setCamScale(k) k.onResize(() => { setCamScale(k); }); k.onUpdate(() => { k.camPos(player.pos.x, player.pos.y + 100); }); // Player movement k.onMouseDown((mouseBtn) => { if (mouseBtn !== "left" || player.isInDialog) return; const worldMousePos = k.toWorld(k.mousePos()); player.moveTo(worldMousePos, player.speed); const mouseAngle = player.pos.angle(worldMousePos); const lowerBound = 50; const upperBound = 125; // Up if (mouseAngle > lowerBound && mouseAngle < upperBound && player.curAnim() !== "walk-up") { player.play("walk-up"); player.direction = "up"; return; } // Down if (mouseAngle < -lowerBound && mouseAngle > -upperBound && player.curAnim() !== "walk-down") { player.play("walk-down"); player.direction = "down"; return; } // Right if (Math.abs(mouseAngle) > upperBound) { player.flipX = false; if (player.curAnim() !== "walk-side") { player.play("walk-side"); player.direction = "right"; return; } } // Left if (Math.abs(mouseAngle) < lowerBound) { player.flipX = true; if (player.curAnim() !== "walk-side") { player.play("walk-side"); player.direction = "left"; return; } } }); // Stops walking animation k.onMouseRelease(() => { if (player.direction === "down") { player.play("idle-down"); return; } if (player.direction === "up") { player.play("idle-up"); return; } player.play("idle-side"); }); }); k.go("main"); }); // 联系表单逻辑 closeContactForm(); const openButton = document.querySelector('.ui-popup-button'); const closeButton = document.querySelector('#closeContactFormButton'); if (openButton) { openButton.addEventListener('click', openContactForm); } else { console.error("Element with class '.ui-popup-button' not found"); } if (closeButton) { closeButton.addEventListener('click', closeContactForm); } else { console.error("Element with id 'closeContactFormButton' not found"); } });
4. 额外验证点
- 确认部署后的资源路径:检查浏览器控制台输出的URL是否正确,确保资源能通过该URL访问
- 启用Kaboom的调试模式:添加
k.debug()到初始化代码,查看资源加载状态
内容的提问来源于stack exchange,提问作者glitchedb0t
相关产品推荐
相关产品推荐

