지렁이 게임

마우스로 클릭 또는 터치를 누르세요.

안녕하세요! 오늘은 JavaScript 라이브러리인 p5.js를 사용하여 모바일 기기에서도 즐길 수 있는 클래식 지렁이 게임을 만드는 방법을 알아보겠습니다. 이 튜토리얼을 통해 반응형 게임 개발의 기본 개념과 p5.js의 활용법을 배울 수 있습니다.

  1. 게임 소개

우리가 만들 지렁이 게임은 다음과 같은 특징을 가집니다:

  • 350×350 픽셀 캔버스에서 플레이
  • 클릭 또는 터치로 게임 시작
  • 키보드 방향키 또는 스와이프로 지렁이를 조종
  • 빨간 사각형(음식)을 먹으면 지렁이가 길어지고 점수가 올라감
  • 벽이나 자신의 몸에 부딪히면 게임 오버
  1. 준비하기

먼저 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>
  1. 게임 코드 작성

snake.js 파일을 만들고 게임 코드를 작성합니다. 주요 함수와 클래스는 다음과 같습니다:

  • setup(): 게임 초기 설정
  • draw(): 게임 루프
  • mousePressed(): 클릭/터치 입력 처리
  • keyPressed(): 키보드 입력 처리
  • touchStarted()touchEnded(): 터치 스와이프 처리
  • Snake 클래스: 지렁이의 동작 및 상태 관리
  1. 주요 기능 설명
  • 게임 시작/재시작: 클릭 또는 터치로 제어
  • 지렁이 이동: 방향키 또는 스와이프로 제어
  • 충돌 감지: 벽 또는 자신의 몸과 충돌 시 게임 오버
  • 점수 시스템: 음식을 먹을 때마다 점수 증가
  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;
}
  1. 모바일 최적화

게임을 모바일 친화적으로 만들기 위해 다음과 같은 변경을 했습니다:

  • 캔버스 크기를 350×350으로 축소
  • 터치 스크린 조작 지원
  • 게임 시작과 재시작을 클릭/터치로 가능하게 함
  1. 마무리 및 발전 방향

이렇게 해서 모바일에서도 즐길 수 있는 지렁이 게임이 완성되었습니다! 게임을 더 발전시키기 위해 다음과 같은 기능을 추가해 볼 수 있습니다:

  • 난이도 조절 (속도 증가)
  • 다양한 타입의 음식
  • 고득점 저장 및 표시
  • 소리 효과 추가

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);
    }
  }
}

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다