My Boundary As Much As I Experienced

p5.js) 스네이크 게임 구현하기 본문

Interactive(HTML Canvas ・three.js ・game)/CSS・Canvas ・ p5.js ・ Pixi.js

p5.js) 스네이크 게임 구현하기

Bumang 2024. 6. 15. 13:29

 

게임을 구현한다는건 언제나 복잡한 과제인거 같다.

모든 task를 절차적으로 하기보단 모듈화하여 적재적소에 활용하는 능력이 매우 중요하다는 것을 느꼈고,

이를 p5.js에서 어떻게 사용하는지를 알 수 있는 좋은 튜토리얼이었다.

 

let cols; let rows;
let size = 25;
let board = [];
let food; let head; 
let dir; // head의 진행방향
let length = 1;
let gameOver = false;

function setup() {
  createCanvas(400, 400);
  frameRate(5); // 너무 빠르지 않게 프레임을 낮춰줌
  cols = width/size;
  rows = height/size;

  for (let i=0; i<cols; i++){
    board[i] = [];
    for (let j=0; j<rows; j++){
      board[i][j] = 0; // 0이 꽉 차있는 2차월 배열을 생성
    }
  }
  
  food = createVector(int(random(0, cols)), int(random(0, rows))); // 0 ~ 칸 갯수 사이의 난수로 x,y 음식 좌표 생성. int는 p5.js 내장 함수인가 봄..
  head = createVector(int(random(0, cols)), int(random(0, rows))); // 0 ~ 칸 갯수 사이의 난수로 x,y head 시작위치 좌표 생성.
  dir = createVector(0, 0);
}

function draw() {
  background(220);
  update();
  displayBoard();
  board[food.x][food.y] = -1; 
  if (gameOver == true) {
    textAlign(CENTER,CENTER);
    fill(0);
    textSize(50);
	text("GAME OVER", width/2, height/2);
  }
}

function update(){ // 5프레임마다 업데이트를 함
  head.add(dir); 

  if(dist(head.x, head.y, food.x, food.y) == 0){ // Food를 먹었으면
    length += 1; // length를 +1
    generateFood(); // 랜덤 좌표에 food 다시 생성
  } 
  
  if (head.x < 0 || head.x > cols-1 || head.y < 0 || head.y > rows-1){ // head가 board 바깥으로 넘어갔다면 게임오버
    console.log("Game Over: Run Into Border");
    gameOver = true;
  } else if (board[head.x][head.y] > 1) { // head가 닿은 곳이 1 이상이면 자기 몸체에 닿은거기 때문에 게임오버
    console.log("Game Over: Run Into Itself");
    gameOver = true;
  	dir.set(0, 0); // 진행 멈추기
  } else {
    board[head.x][head.y] = 1 + length; // 현재 head에다가 length + 1을 더함
    removeTail(); // 모든 board칸에서 -1을 해주는 함수
  }
  
}

function displayBoard(){ // 0일 때, -1일 때, 1일 때 각각 칸 색상을 다르게 지정
  for (let i=0; i<cols; i++){
    for (let j=0; j<rows; j++){
      if (board[i][j] == 0){
        fill(255); // Color the board white
      } else if(board[i][j] == -1){
        fill(255, 0, 0); // Color the food red
      } else {
        fill(255, 255, 0); // Color the snake yellow
      }
      rect(i*size, j*size, size, size);
      // displayText(i, j);
    }
  }
}

function generateFood(){ // food를 랜덤 좌표에 생성
  while(true){
    food = createVector(int(random(0, cols-1)), int(random(0, rows-1)));
    if (board[food.x][food.y] == 0){ // food.x, food.y가 0이라면 ok
      break;
    }
  }
}

function removeTail(){ // 모든 칸에 -1을 해주는 함수
  for (let i=0; i<cols; i++){
    for (let j=0; j<rows; j++){
      if(board[i][j] > 0){
        board[i][j] -= 1;
      }
    }
  }
}

function displayText(x, y){ // gameover 텍스트를 띄워줌
  textAlign(CENTER, CENTER);
  fill(0);
  textSize(10);
  text(board[x][y], x*size+size/2, y*size+size/2);
}

function keyPressed(){ // 키보드 입력에 따라 vector 방향을 바꿔줌
  if (keyCode === LEFT_ARROW) {
    dir = createVector(-1, 0);
  } else if (keyCode === RIGHT_ARROW) {
    dir = createVector(1, 0);
  } else if (keyCode === DOWN_ARROW) {
    dir = createVector(0, 1);
  } else if (keyCode === UP_ARROW) {
    dir = createVector(0, -1);
  }
}