My p5.js sketch is freezing for a couple of frames every 2-3 seconds. I know p5 is not performant, but I feel like my sketch is optimized enough to not warrant this.
I think it may have to do with how the particles are handled when driving, so I used a particle pool, but that had no effect on performance. Here is my code
game.js:
const canvasSize = 400;
let car;
function setup() {
frameRate(60);
angleMode(RADIANS);
createCanvas(canvasSize, canvasSize);
noStroke();
car = new Car(200, 200);
}
function draw() {
background(100, 100, 100);
car.drawCar();
car.drawGroundMarks();
car.updatePoints();
let turnDirection = 0;
if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) {
turnDirection = -1; // Turn left
}
if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) {
turnDirection = 1; // Turn right
}
car.rotateCar(turnDirection);
if (keyIsDown(UP_ARROW) || keyIsDown(87)) {
car.accelerateCar(1);
}
if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) {
car.accelerateCar(-1);
}
}
car.js:
class Car {
constructor(x, y, width = 40, height = 20, rotation = 0, speed = 0.05) {
this.carX = x;
this.carY = y;
this.carW = width;
this.carH = height;
this.carSpeed = speed;
this.carRotation = rotation;
this.maxSpeed = 9;
this.carAcceleration = createVector(0, 0);
this.carVelocity = createVector(0, 0);
this.carPoints = [];
this.color = [255, 0, 0];
this.maxTurnSpeed = radians(20);
this.turnSpeed = 0;
this.turnAcceleration = radians(1);
this.friction = 0.98; // Friction to slow down car
this.groundMarks = [];
this.particlePool = new ParticlePool(); // Create the particle pool
}
drawCar() {
// Draw the car body
if (this.carPoints.length == 0)
return;
fill(...this.color);
beginShape();
this.carPoints.forEach(point => vertex(point.x, point.y));
endShape(CLOSE);
//fill(0, 0, 0);
//ellipse(this.carPoints[0].x, this.carPoints[0].y, 5, 5); //back left wheel
//ellipse(this.carPoints[3].x, this.carPoints[3].y, 5, 5); //back right wheel
//ellipse(this.carPoints[2].x, this.carPoints[2].y, 5, 5); //front right wheel
//ellipse(this.carPoints[1].x, this.carPoints[1].y, 5, 5); //front left wheel
let windshieldLeftPoint = createVector((this.carPoints[0].x*.25 + this.carPoints[1].x*.75), (this.carPoints[0].y*.25 + this.carPoints[1].y*.75));
let windshieldRightPoint = createVector((this.carPoints[3].x*.25 + this.carPoints[2].x*.75), (this.carPoints[3].y*.25 + this.carPoints[2].y*.75));
fill(180, 180, 255);
beginShape();
vertex(this.carPoints[2].x, this.carPoints[2].y);
vertex(this.carPoints[1].x, this.carPoints[1].y);
vertex(windshieldLeftPoint.x, windshieldLeftPoint.y);
vertex(windshieldRightPoint.x, windshieldRightPoint.y);
endShape();
}
drawGroundMarks() {
// Draw active particles
this.groundMarks.forEach((mark) => {
mark.draw();
});
// Remove dead particles and return them to the pool
this.groundMarks = this.groundMarks.filter((mark) => {
if (mark.dead) {
this.particlePool.release(mark); // Return dead particles to the pool
return false;
}
return true;
});
//console.log(this.groundMarks.length);
}
updatePoints() {
this.carVelocity.add(this.carAcceleration);
this.carVelocity.limit(this.maxSpeed);
// Apply friction to gradually slow down the car when no acceleration
this.carVelocity.mult(this.friction);
this.carX += this.carVelocity.x;
this.carY += this.carVelocity.y;
this.carAcceleration.mult(0); // Reset acceleration after each frame
let halfW = this.carW / 2;
let halfH = this.carH / 2;
let p1 = createVector(-halfW, -halfH);
let p2 = createVector(halfW, -halfH);
let p3 = createVector(halfW, halfH);
let p4 = createVector(-halfW, halfH);
this.carPoints = [p1, p2, p3, p4];
this.carPoints.forEach(point => {
let x = point.x;
let y = point.y;
let cosA = cos(this.carRotation);
let sinA = sin(this.carRotation);
let rotatedPoint = createVector(x * cosA - y * sinA, x * sinA + y * cosA);
let translatedPoint = rotatedPoint.add(createVector(this.carX, this.carY));
point.set(translatedPoint.x, translatedPoint.y);
});
}
accelerateCar(direction) {
let forwardVelocityX = this.carSpeed * direction * cos(this.carRotation);
let forwardVelocityY = this.carSpeed * direction * sin(this.carRotation);
this.carAcceleration.add(createVector(forwardVelocityX, forwardVelocityY));
// Get recycled particles from the pool instead of creating new ones
if (frameCount % 5 !== 0)
return;
this.groundMarks.push(this.particlePool.get(this.carPoints[0].x, this.carPoints[0].y));
this.groundMarks.push(this.particlePool.get(this.carPoints[3].x, this.carPoints[3].y));
}
rotateCar(direction) {
if (direction !== 0) {
this.turnSpeed += this.turnAcceleration * direction;
this.turnSpeed = constrain(this.turnSpeed, -this.maxTurnSpeed, this.maxTurnSpeed);
} else {
this.turnSpeed *= 0.9;
}
// If the car is moving, allow rotation, else stop turning
if (this.carVelocity.mag() > 0.1) {
this.carRotation += this.turnSpeed * (this.carVelocity.mag() / this.maxSpeed); // More natural turning at speed
}
}
}
drift particle.js
class driftParticle {
constructor(x, y) {
this.reset(x, y);
}
reset(x, y) {
this.x = x;
this.y = y;
this.timeAlive = 0;
this.alpha = 255;
this.dead = false; // Reset dead flag
}
draw() {
if (this.dead) return;
this.alpha = 255 - this.timeAlive * 2;
if (this.alpha <= 0) {
this.dead = true;
}
fill(50, 50, 50, this.alpha);
ellipse(this.x, this.y, 3, 3);
this.timeAlive++;
}
}
particle pool.js
class ParticlePool {
constructor() {
this.pool = [];
}
get(x, y) {
let particle;
if (this.pool.length > 0) {
particle = this.pool.pop(); // Reuse an old particle if available
particle.reset(x, y); // Reset its position and state
} else {
particle = new driftParticle(x, y); // Create a new one if none available
}
return particle;
}
release(particle) {
this.pool.push(particle); // Return particle to the pool for reuse
}
}