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.
- 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
- 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>
- Coding the Game
Create a snake.js
file where we’ll write our game code. Key functions and classes include:
setup()
: Initial game setupdraw()
: Game loopmousePressed()
: Handle click/tap inputskeyPressed()
: Handle keyboard inputstouchStarted()
andtouchEnded()
: Handle swipe gesturesSnake
class: Manage snake behavior and state
- 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
- 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;
}
- 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
- 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
- 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);
}
}
}