Snake game

Hello, fellow coders! Today, we’re diving into the world of game development using p5.js, a powerful JavaScript library. We’ll be creating a classic Snake game that’s not only fun to play on desktop but also optimized for mobile devices. This tutorial will walk you through the basics of responsive game development and how to leverage p5.js for cross-platform gaming.

  1. Game Overview

Our Snake game features:

  • A 350×350 pixel canvas for compact play
  • Click or tap to start the game
  • Control the snake using arrow keys or swipe gestures
  • Grow the snake and increase score by eating red squares (food)
  • Game over when the snake hits the wall or itself
  1. Setting Up

First, let’s create an HTML file that includes the p5.js library:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mobile Snake Game</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
    <script src="snake.js"></script>
</head>
<body>
</body>
</html>
  1. Coding the Game

Create a snake.js file where we’ll write our game code. Key functions and classes include:

  • setup(): Initial game setup
  • draw(): Game loop
  • mousePressed(): Handle click/tap inputs
  • keyPressed(): Handle keyboard inputs
  • touchStarted() and touchEnded(): Handle swipe gestures
  • Snake class: Manage snake behavior and state
  1. Key Features Explained
  • Game start/restart: Controlled by click or tap
  • Snake movement: Directed by arrow keys or swipe gestures
  • Collision detection: Game over on wall or self-collision
  • Scoring system: Increment score when food is eaten
  1. Code Highlight

Here’s how we handle swipe gestures for mobile play:

function touchStarted() {
  touchStartX = mouseX;
  touchStartY = mouseY;

  if (!gameStarted) {
    gameStarted = true;
    resetGame();
  }

  return false;
}

function touchEnded() {
  if (gameStarted) {
    let diffX = mouseX - touchStartX;
    let diffY = mouseY - touchStartY;

    if (abs(diffX) > abs(diffY)) {
      snake.setDir(diffX > 0 ? 1 : -1, 0);
    } else {
      snake.setDir(0, diffY > 0 ? 1 : -1);
    }
  }

  return false;
}
  1. Mobile Optimization

To make our game mobile-friendly, we’ve implemented:

  • A compact 350×350 canvas size
  • Touch screen controls support
  • Click/tap to start and restart the game
  • Prevention of unintended scrolling during gameplay
  1. Debugging and Improvements

We’ve also addressed some common issues:

  • Prevented page scrolling when using arrow keys
  • Fixed touch functionality for game start on mobile devices
  • Added return false to input functions to prevent default browser behaviors
  1. Conclusion and Next Steps

Congratulations! You’ve now created a mobile-friendly Snake game using p5.js. To further enhance your game, consider adding:

  • Difficulty levels (increasing speed)
  • Various types of food
  • High score saving and display
  • Sound effects

p5.js makes it easy to create responsive games that work on both desktop and mobile platforms. Use this tutorial as a springboard to develop your own cross-platform games!

The full code for this project can be found at the bottom . Happy coding, and may your snake grow long and prosperous!



let snake;
let food;
let w = 17.5; // 20개의 격자를 만들기 위해 조정
let h = 17.5;
let score = 0;
let gameStarted = false;
let touchStartX, touchStartY;

function setup() {
  createCanvas(350, 350);
  resetGame();
}

function resetGame() {
  snake = new Snake();
  pickLocation();
  score = 0;
  frameRate(10);
}

function pickLocation() {
  let cols = floor(width / w);
  let rows = floor(height / h);
  food = createVector(floor(random(cols)), floor(random(rows)));
  food.mult(w);
}

function draw() {
  background(51);
  
  if (!gameStarted) {
    textAlign(CENTER, CENTER);
    textSize(20);
    fill(255);
    text('Click or Tap to start', width/2, height/2);
    return;
  }
  
  if (snake.eat(food)) {
    pickLocation();
  }
  snake.update();
  snake.show();

  fill(255, 0, 100);
  rect(food.x, food.y, w, h);
  
  textAlign(LEFT);
  textSize(20);
  fill(255);
  text('Score: ' + score, 10, 30);

  if (snake.checkDeath()) {
    gameStarted = false;
    textAlign(CENTER, CENTER);
    textSize(20);
    fill(255);
    text('Game Over! Click or Tap to restart', width/2, height/2);
  }
}

function mousePressed() {
  if (!gameStarted) {
    gameStarted = true;
    resetGame();
  }
}

function keyPressed() {
  if (keyCode === LEFT_ARROW) {
    snake.setDir(-1, 0);
  } else if (keyCode === RIGHT_ARROW) {
    snake.setDir(1, 0);
  } else if (keyCode === DOWN_ARROW) {
    snake.setDir(0, 1);
  } else if (keyCode === UP_ARROW) {
    snake.setDir(0, -1);
  }
}

function touchStarted() {
  touchStartX = mouseX;
  touchStartY = mouseY;
  return false;
}

function touchEnded() {
  let diffX = mouseX - touchStartX;
  let diffY = mouseY - touchStartY;
  
  if (abs(diffX) > abs(diffY)) {
    if (diffX > 0) {
      snake.setDir(1, 0); // right
    } else {
      snake.setDir(-1, 0); // left
    }
  } else {
    if (diffY > 0) {
      snake.setDir(0, 1); // down
    } else {
      snake.setDir(0, -1); // up
    }
  }
  return false;
}

class Snake {
  constructor() {
    this.body = [];
    this.body[0] = createVector(floor(width/2), floor(height/2));
    this.xdir = 0;
    this.ydir = 0;
  }

  setDir(x, y) {
    this.xdir = x;
    this.ydir = y;
  }

  update() {
    let head = this.body[this.body.length-1].copy();
    this.body.shift();
    head.x += this.xdir * w;
    head.y += this.ydir * h;
    this.body.push(head);
  }

  grow() {
    let head = this.body[this.body.length-1].copy();
    this.body.push(head);
  }

  eat(pos) {
    let x = this.body[this.body.length-1].x;
    let y = this.body[this.body.length-1].y;
    if (x === pos.x && y === pos.y) {
      this.grow();
      score++;
      return true;
    }
    return false;
  }

  checkDeath() {
    let x = this.body[this.body.length-1].x;
    let y = this.body[this.body.length-1].y;
    if (x > width-w || x < 0 || y > height-h || y < 0) {
      return true;
    }
    for (let i = 0; i < this.body.length-1; i++) {
      let part = this.body[i];
      if (part.x === x && part.y === y) {
        return true;
      }
    }
    return false;
  }

  show() {
    for (let i = 0; i < this.body.length; i++) {
      fill(0, 255, 0);
      rect(this.body[i].x, this.body[i].y, w, h);
    }
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *