↑
마우스로 클릭 또는 터치를 누르세요.
안녕하세요! 오늘은 JavaScript 라이브러리인 p5.js를 사용하여 모바일 기기에서도 즐길 수 있는 클래식 지렁이 게임을 만드는 방법을 알아보겠습니다. 이 튜토리얼을 통해 반응형 게임 개발의 기본 개념과 p5.js의 활용법을 배울 수 있습니다.
- 게임 소개
우리가 만들 지렁이 게임은 다음과 같은 특징을 가집니다:
- 350×350 픽셀 캔버스에서 플레이
- 클릭 또는 터치로 게임 시작
- 키보드 방향키 또는 스와이프로 지렁이를 조종
- 빨간 사각형(음식)을 먹으면 지렁이가 길어지고 점수가 올라감
- 벽이나 자신의 몸에 부딪히면 게임 오버
- 준비하기
먼저 p5.js 라이브러리를 포함하는 HTML 파일을 만듭니다:
<!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>
- 게임 코드 작성
snake.js
파일을 만들고 게임 코드를 작성합니다. 주요 함수와 클래스는 다음과 같습니다:
setup()
: 게임 초기 설정draw()
: 게임 루프mousePressed()
: 클릭/터치 입력 처리keyPressed()
: 키보드 입력 처리touchStarted()
및touchEnded()
: 터치 스와이프 처리Snake
클래스: 지렁이의 동작 및 상태 관리
- 주요 기능 설명
- 게임 시작/재시작: 클릭 또는 터치로 제어
- 지렁이 이동: 방향키 또는 스와이프로 제어
- 충돌 감지: 벽 또는 자신의 몸과 충돌 시 게임 오버
- 점수 시스템: 음식을 먹을 때마다 점수 증가
- 코드 하이라이트
터치 스와이프 처리를 위한 함수:
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;
}
- 모바일 최적화
게임을 모바일 친화적으로 만들기 위해 다음과 같은 변경을 했습니다:
- 캔버스 크기를 350×350으로 축소
- 터치 스크린 조작 지원
- 게임 시작과 재시작을 클릭/터치로 가능하게 함
- 마무리 및 발전 방향
이렇게 해서 모바일에서도 즐길 수 있는 지렁이 게임이 완성되었습니다! 게임을 더 발전시키기 위해 다음과 같은 기능을 추가해 볼 수 있습니다:
- 난이도 조절 (속도 증가)
- 다양한 타입의 음식
- 고득점 저장 및 표시
- 소리 효과 추가
p5.js를 사용하면 데스크톱과 모바일 모두에서 작동하는 반응형 게임을 쉽게 만들 수 있습니다. 여러분도 이 튜토리얼을 바탕으로 자신만의 크로스 플랫폼 게임을 만들어보세요!
전체 코드는 아래에서 확인할 수 있습니다. 즐거운 코딩 되세요!
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);
}
}
}