Search code examples
javascriptp5.js

why does this cause extreme lag in p5.js?


when running the p5.js code, it seems to break, it works for 1 frame then breaks and lags no matter what device (ChatGPT and bard were unable to debug) (btw no error message is given)

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)
}

It breaks and lags extremely. How do i fix this, i have narrowed it down to be something to do with crearing an NPC, but nothing more.


Solution

  • The actual problem was a bug in p5.js that was solved in version 1.5.0. The bug #5816 consisted in the fact that, in certain conditions, an image loaded during setup resulted in the function setup being called again (multiple times); in your case, the line this.sprite.img = 'Images/npc.png' (from NPC's constructor) resulted in the setup function being called on an infinite recursion.

    So the immediate solution is, if possible, to upgrade the link to p5.js in your index.html to the latest version (1.6.0) -- it took me a while to realise that your replit was using version 1.4.0.

    Regardless of that, it is a good practice to preload images, as you already did with fonts. I'll repeat the steps from my message (adapted from preload reference):

    1. declare in the outer scope var npcImg; (or let npcImg if you change to ES6)
    2. in preload function do load the image npcImg = loadImage('Images/npc.png');
    3. in setup function add npcImg.loadPixels();
    4. replace this.sprite.img = 'Images/npc.png' with this.sprite.img = npcImg; (in NPC's constructor)