Znote (recipes)
  Get Znote  

Snake Game with P5 🐍

Use P5 with canvas to create a Snake game

 

Snake Game with P5 🐍

https://p5js.org/

In the NPM Packages popup (bottom right) under the section: Add External Scripts, add P5: name:p5 url:https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js

In this example, P5 is used in instance mode

//hide
const s = p => {
  let gridWidth = 30, gridHeight = 30, gameStarted = false, startingSegments = 10;
  let xStart = 5, yStart = 15, direction = 'right', segments = [], score = 0, fruit;

  p.setup = function() {
    p.createCanvas(500, 500);
    p.frameRate(10);
    p.textAlign(p.CENTER, p.CENTER);
    p.textSize(1);
    startGame();
  }

  p.draw = function() {
    p.background(0);
    p.scale(p.width / gridWidth, p.height / gridHeight);
    gameStarted ? playGame() : showStartScreen();
  }

  function playGame() {
    p.translate(0.5, 0.5);
    showFruit();
    showSnake();
    updateSnake();
    if (checkCollision()) gameOver();
    else if (eatFruit()) growSnake();
  }

  function showStartScreen() {
    p.fill(32);
    p.rect(2, gridHeight / 2 - 5, gridWidth - 4, 10, 2);
    p.fill(255);
    p.text('Click to play', gridWidth / 2, gridHeight / 2);
    p.noLoop();
  }

  p.mousePressed = function() {
      gameStarted = true;
      p.loop();
  }

  function showFruit() {
    p.stroke(255, 64, 32);
    p.point(fruit.x, fruit.y);
  }

  function showSnake() {
    p.noFill();
    p.stroke(96, 255, 64);
    p.beginShape();
    segments.forEach(seg => p.vertex(seg.x, seg.y));
    p.endShape();
  }

  function updateSnake() {
    segments.pop();
    let head = segments[0].copy();
    segments.unshift(moveHead(head));
  }

  function moveHead(head) {
    if (direction === 'right') head.x++;
    if (direction === 'left') head.x--;
    if (direction === 'up') head.y--;
    if (direction === 'down') head.y++;
    return head;
  }

  function checkCollision() {
    let head = segments[0];
    return (
      head.x < 0 || head.x >= gridWidth ||
      head.y < 0 || head.y >= gridHeight ||
      segments.slice(1).some(seg => seg.equals(head))
    );
  }

  function gameOver() {
    p.fill(32);
    p.rect(2, gridHeight / 2 - 5, gridWidth - 4, 10, 2);
    p.fill(255);
    p.text(`Game over!\nScore: ${score}`, gridWidth / 2, gridHeight / 2);
    gameStarted = false;
    p.noLoop();
  }

  function eatFruit() {
    return segments[0].equals(fruit);
  }

  function growSnake() {
    score++;
    segments.push(segments[segments.length - 1].copy());
    updateFruit();
  }

  function updateFruit() {
    do {
      fruit = p.createVector(p.floor(p.random(gridWidth)), p.floor(p.random(gridHeight)));
    } while (segments.some(seg => seg.equals(fruit))); // Prevent the fruit from appearing on the snake
  }

  p.keyPressed = function() {
    if (p.keyCode === p.LEFT_ARROW && direction !== 'right') direction = 'left';
    if (p.keyCode === p.RIGHT_ARROW && direction !== 'left') direction = 'right';
    if (p.keyCode === p.UP_ARROW && direction !== 'down') direction = 'up';
    if (p.keyCode === p.DOWN_ARROW && direction !== 'up') direction = 'down';
  }

  function startGame() {
    segments = Array.from({ length: startingSegments }, (_, i) => p.createVector(xStart - i, yStart));
    updateFruit();
    score = 0;
    direction = 'right';
    gameStarted = false;
    p.noLoop();
  }
}


htmlEl.setAttribute('tabindex', '0'); // make it focusable
htmlEl.addEventListener('keydown', function(event) {
  if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
    event.preventDefault(); // Prevents scrolling behavior
  }
});
htmlEl.focus();

htmlEl.innerHTML = ''; // Resets the HTML element before loading p5
const P5 = new p5(s, htmlEl);

Related recipes