P5.js创建NPC引发程序极端卡顿的问题排查与修复求助
问题描述
运行以下p5.js代码时,无论使用何种设备,程序仅正常运行1帧后就出现严重卡顿崩溃,且无任何报错信息。已定位问题与NPC的创建相关,寻求修复方案:
var player; var speed = 5; var HP = 10; var MaxHP = 10; var frames = 0; var VillageX = 0; var VillageY = 0; var NPCs = []; class NPC { constructor() { this.x = getRandomInt(VillageX, VillageX + 500); this.y = getRandomInt(VillageY, VillageY + 500); this.sprite = new Sprite(this.x, this.y, 15); this.sprite.img = "Images/npc.png"; this.sprite.collider = "dynamic"; this.sprite.scale = 3; this.minimapX = 0; this.minimapY = 0; NPCs.push(this); } } function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min) + min); } function preload() { lexendFont = loadFont("Fonts/lexend.ttf") } function setup() { frameRate(60); createCanvas(windowWidth, windowHeight); frames += 1; console.log(frames); VillageX = getRandomInt(-4900, 4400); VillageY = getRandomInt(-4900, 4400); player = new Sprite(0, 0, 15, "circle"); player.addImage("player", loadImage("Images/player.png")); player.scale = 3; player.collider = "dynamic"; topWall = new Sprite(0, -5010, 10000, 10); topWall.visible = false; topWall.collider = "kinematic"; leftWall = new Sprite(-5010, 0, 10, 10000); leftWall.visible = false; leftWall.collider = "kinematic"; bottomWall = new Sprite(0, 5010, 10000, 10); bottomWall.visible = false; bottomWall.collider = "kinematic"; rightWall = new Sprite(5010, 0, 10, 10000); rightWall.visible = false; rightWall.collider = "kinematic"; for (var i = 0; i < 3; i++) { new NPC(); } textSize(32); } function draw() { clear(); background("green"); stroke(0); strokeWeight(8); noFill(); rect(windowWidth - 215, windowHeight - 50, 200, 35, 3); noStroke(); fill("#00cc44"); rect(windowWidth - 215, windowHeight - 50, map(HP, 0, MaxHP, 0, 200), 35, 3); fill("black"); rect(15, windowHeight - 215, 200, 200, 12); var minimapX = map(player.x, -5200, 5200, 15, 215); var minimapY = map(player.y, -5200, 5200, windowHeight - 215, windowHeight - 15); fill("white"); circle(minimapX, minimapY, 8); NPCs.forEach((NP, index) => { NP.minimapX = map(NP.x, -5200, 5200, 15, 215); NP.minimapY = map(NP.y, -5200, 5200, windowHeight - 215, windowHeight - 15) fill("blue"); circle(NP.minimapX, NP.minimapY, 8); }); camera.x = player.x; camera.y = player.y; player.rotation = player.angleTo(mouse) + 90; if (kb.pressing("left")) player.vel.x = -speed; else if (kb.pressing("right")) player.vel.x = speed; else player.vel.x = 0; if (kb.pressing("up")) player.vel.y = -speed; else if (kb.pressing("down")) player.vel.y = speed; else player.vel.y = 0; fill("white"); textFont(lexendFont) textSize(18); text("HEALTH", windowWidth - 205, windowHeight - 27) }
修复方案
核心问题分析
卡顿崩溃的根源是NPC的Sprite图片未预加载,且直接通过字符串路径赋值img属性。p5.play中直接给Sprite的img赋值路径会导致每帧重复尝试加载图片,引发资源占用飙升,最终导致程序崩溃。此外,代码中存在一些逻辑耦合问题,可同步优化。
具体修复步骤
预加载所有资源
在preload函数中统一预加载字体、玩家和NPC图片,确保资源仅加载一次:function preload() { lexendFont = loadFont("Fonts/lexend.ttf"); playerImg = loadImage("Images/player.png"); npcImg = loadImage("Images/npc.png"); }修改NPC类的图片设置与数组添加逻辑
用预加载的图片对象替代字符串路径,并将NPCs.push(this)移出构造函数,避免类逻辑耦合:class NPC { constructor() { this.x = getRandomInt(VillageX, VillageX + 500); this.y = getRandomInt(VillageY, VillageY + 500); this.sprite = new Sprite(this.x, this.y, 15); this.sprite.addImage("npc", npcImg); this.sprite.collider = "dynamic"; this.sprite.scale = 3; this.minimapX = 0; this.minimapY = 0; } }调整NPC创建时的数组添加逻辑
在setup的循环中手动将NPC实例加入数组:for (var i = 0; i < 3; i++) { NPCs.push(new NPC()); }优化draw函数的渲染顺序
将相机位置设置移到background之后、UI绘制之前,确保相机逻辑正确:function draw() { clear(); background("green"); // 先设置相机 camera.x = player.x; camera.y = player.y; // 后续UI、小地图、玩家控制逻辑不变 // ... }修复玩家图片的加载方式
同样使用预加载的图片对象,避免重复加载:player = new Sprite(0, 0, 15, "circle"); player.addImage("player", playerImg);
完整修复后的代码
var player; var speed = 5; var HP = 10; var MaxHP = 10; var frames = 0; var VillageX = 0; var VillageY = 0; var NPCs = []; var lexendFont, playerImg, npcImg; class NPC { constructor() { this.x = getRandomInt(VillageX, VillageX + 500); this.y = getRandomInt(VillageY, VillageY + 500); this.sprite = new Sprite(this.x, this.y, 15); this.sprite.addImage("npc", npcImg); this.sprite.collider = "dynamic"; this.sprite.scale = 3; this.minimapX = 0; this.minimapY = 0; } } function getRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min) + min); } function preload() { lexendFont = loadFont("Fonts/lexend.ttf"); playerImg = loadImage("Images/player.png"); npcImg = loadImage("Images/npc.png"); } function setup() { frameRate(60); createCanvas(windowWidth, windowHeight); frames += 1; console.log(frames); VillageX = getRandomInt(-4900, 4400); VillageY = getRandomInt(-4900, 4400); player = new Sprite(0, 0, 15, "circle"); player.addImage("player", playerImg); player.scale = 3; player.collider = "dynamic"; topWall = new Sprite(0, -5010, 10000, 10); topWall.visible = false; topWall.collider = "kinematic"; leftWall = new Sprite(-5010, 0, 10, 10000); leftWall.visible = false; leftWall.collider = "kinematic"; bottomWall = new Sprite(0, 5010, 10000, 10); bottomWall.visible = false; bottomWall.collider = "kinematic"; rightWall = new Sprite(5010, 0, 10, 10000); rightWall.visible = false; rightWall.collider = "kinematic"; for (var i = 0; i < 3; i++) { NPCs.push(new NPC()); } textSize(32); } function draw() { clear(); background("green"); camera.x = player.x; camera.y = player.y; stroke(0); strokeWeight(8); noFill(); rect(windowWidth - 215, windowHeight - 50, 200, 35, 3); noStroke(); fill("#00cc44"); rect(windowWidth - 215, windowHeight - 50, map(HP, 0, MaxHP, 0, 200), 35, 3); fill("black"); rect(15, windowHeight - 215, 200, 200, 12); var minimapX = map(player.x, -5200, 5200, 15, 215); var minimapY = map(player.y, -5200, 5200, windowHeight - 215, windowHeight - 15); fill("white"); circle(minimapX, minimapY, 8); NPCs.forEach((NP, index) => { NP.minimapX = map(NP.x, -5200, 5200, 15, 215); NP.minimapY = map(NP.y, -5200, 5200, windowHeight - 215, windowHeight - 15) fill("blue"); circle(NP.minimapX, NP.minimapY, 8); }); player.rotation = player.angleTo(mouse) + 90; if (kb.pressing("left")) player.vel.x = -speed; else if (kb.pressing("right")) player.vel.x = speed; else player.vel.x = 0; if (kb.pressing("up")) player.vel.y = -speed; else if (kb.pressing("down")) player.vel.y = speed; else player.vel.y = 0; fill("white"); textFont(lexendFont); textSize(18); text("HEALTH", windowWidth - 205, windowHeight - 27); }
内容的提问来源于stack exchange,提问作者PseudoGhost
相关产品推荐
相关产品推荐

