Vanilla JS | Game Snacks

dev_hee·2021년 11월 13일
2

프로젝트

목록 보기
2/5
post-thumbnail

🚩 Game Snacks 프로젝트

📆 프로젝트 기간

2021/10/19 ~ 2021/10/21

📌 프로젝트 개요

스낵 게임은 별도의 설치 없이 언제 어디서든 이용 가능한 무료 미니 게임입니다.
어렸을 때 하던 스낵 게임을 자바스크립트로 구현해보며 프로그램 구현 실력을 향상 시켰습니다.
저는 지뢰 찾기 게임을 설계 및 구현과 전체적인 디자인 시안을 맡아 진행하였습니다.

🎮 게임 목록

⛑ 페이지 구조

메인 페이지

  • 게임 3종류를 카드로 보여주어 사용자가 하고싶은 게임을 선택할 수 있게한다.

게임 페이지

  • 헤더에는 게임 이름과, 게임 난이도, 게임 라운드, 점수를 보여준다.
  • 메인에는 게임을 할 수 있는 공간이다.
  • 푸터는 메인 페이지로 돌아갈 수 있는 버튼과, 게임을 다시 시작할 수 있는 리셋 버튼이 있다.
  • 해당 UI는 게임별로 공유하고, 메인 부분에 들어가는 게임을 통일성을 고려해 구현한다.

🖥 프로젝트 시연 영상

Game Snacks 시연 영상

🔗 Game Snack 링크


💣 지뢰찾기 구현 로직

플레이어와 상호작용

  1. 게임 시작
  2. 좌클릭
  3. 우클릭
  4. 게임 종료
  5. 모드 선택
  6. 초기화 선택

1. 게임 시작

// 이벤트
window.addEventListener('DOMContentLoaded', minesweeperGame.renderNewGame);
// minesweeperGame
renderNewGame() {
      gameBoard = createBoard(SQUARE.NORMAL);
      mineInfoBoard = createBoard(0);
      plantMineInGameBoard();
      renderGameBoard();
    }
  • gameBoard: 플레이어의 게임 상태를 저장할 배열
  • mineInfoBoard: 지뢰의 위치와 지뢰 개수 정보를 저장할 배열
  • plantMineInGameBoard: gameBoard 배열에 지뢰를 저장하는 함수
  • renderGameBoard: 플레이어에게 게임판을 렌더할 함수

2. 우클릭 (깃발)

// 우클릭
document.querySelector('.minesweeper-board').oncontextmenu = e => {
  e.preventDefault();
  const $col = e.target.closest('.col');
  if (!$col) return;
  minesweeperGame.handleRightClick($col);
};
  • oncontextmenu : 우클릭 이벤트
  • handleRightClick : 우클릭을 처리할 함수

우클릭 로직

  • 이미 열려있는 칸 ⇒ 대기
  • 닫힌 칸
    • 우클릭 소리 발생
    • 깃발 ⇒ 깃발 제거
    • 깃발X ⇒ 깃발 꽂음

3. 좌클릭

// 좌클릭
document.querySelector('.minesweeper-board').onclick = e => {
  const $col = e.target.closest('.col');
  if (!$col) return;
  minesweeperGame.handleLeftClick($col);
};
  • handleLeftClick : 좌클릭을 처리할 함수

좌클릭 로직

  • 열린칸 또는 깃발 ⇒ 대기
  • 닫힌칸
    • 좌클릭 소리 발생한다.
    • 지뢰 ⇒ 패배, 게임 종료
    • 지뢰X ⇒ 지뢰 전까지 연다.
      • 지뢰 빼고 전부 열었다면 ⇒ 승리, 게임종료

4. 게임 종료

  • 승리했을 경우
    • 승리 소리 발생한다.
    • 승리 메세지 출력한다.
    • 모든 지뢰를 보여준다.
    • ROUND + 1, SCORE + 100

  • 패배했을 경우
    • 패배 소리 발생한다.
    • 실패 메세지 출력한다.
    • 모든 지뢰 정보를 보여준다.
    • ROUND + 1, SCORE - 100

5. 모드 선택

// 모드 선택
document.querySelector('.game-mode').onclick = e => {
  if (!e.target.classList.contains('game-mode-button')) return;

  minesweeperGame.changeMode(e.target.dataset.mode);
  [...document.querySelectorAll('.game-mode-button')].forEach($button =>
    $button.classList.toggle('current-mode', e.target === $button)
  );

  minesweeperGame.renderNewGame();
};
  • changeMode : 모드를 바꿔주는 함수
  • renderNewGame : 새로운 게임 시작하는 함수
// Mode
const MODE = {
    EASY: { ROW: 10, COL: 10, MINE_NUM: 10 },
    NORMAL: { ROW: 20, COL: 20, MINE_NUM: 60 },
    HARD: { ROW: 25, COL: 25, MINE_NUM: 140 }
  };

// state
let { ROW, COL, MINE_NUM } = MODE.NORMAL;

changeMode(mode) {
      const codeMode = mode.toUpperCase();
      ({ ROW, COL, MINE_NUM } = MODE[codeMode]);
    }
  • 초기엔 NORMAL모드다.
  • 사용자가 입력한 모드로 바꿔준다.

6. 초기화 버튼

document.querySelector('.restart').onclick = minesweeperGame.renderNewGame;
  • renderNewGame : 새로운 게임 시작하는 함수

🤔 프로젝트 회고

아쉬운 점 🥺😭😓

  1. 게임의 복잡한 로직의 어려움
    • 사용자가 게임의 좌클릭, 우클릭 이벤트가 발생했을 때, 현재 게임의 상태에 따라 다른 처리를 해야했다.
  2. 클린 코드의 어려움
    • 로직이 복잡해지다보니 함수가 한가지 일을 수행하도록 만들기가 어려워졌다.
    • 코드가 길어지다보니 가독성이 떨어졌다.
    • 가독성을 높이려고 코드를 수정하면 새로운 버그가 발생하는 황당한 상황이 계속 벌어졌다.

⇒ 1,2 번의 어려움은 앞으로 개인적인 미니 프로젝트를 진행하면서 연습하며 해결해나갈 계획이다.

🚩 미니프로젝트 주제 모음

GitHub - florinpop17/app-ideas: A Collection of application ideas which can be used to improve your coding skills.

  1. 사용자 요구 수용의 어려움
    • 처음엔 사용자의 좌클릭, 우클릭에 따라 칸을 열거나, 깃발을 꽂거나 뽑거나, 게임을 이기거나 지거나로 나눠서 만들었다.
    • 2기 수강생들이 게임을 해보고 다음과 같은 요구사항이 생겼다.
      1. 게임이 종료되면 폭탄을 전부 보여주세요.
      2. 이겼을 때는 폭탄이 초록색으로, 졌을 때는 빨간색으로 보여주세요.
      3. 꽂을 수 있는 깃발의 수를 보여주고, 그 횟수를 제한해주세요.
      4. 게임이 경과한 시간을 보여주세요.
      5. 게임이 종료되면 랭킹을 보여주세요.
    • 1,2번 요구사항은 반영했지만 나머지는 반영하기에 시간이 부족했다.
    • 기존에 코드에 2번 요구사항을 반영하면서 졌을 때도 폭탄이 초록색으로 보여지는 버그가 발생했다. 그를 통해 유지보수가 좋은 코드를 작성해야, 새로운 기능이 추가되어도 버그가 발생하지 않겠구나, 라는 사실을 느낄 수 있었따.
  2. html & css의 어려움
    • 처음엔 table을 사용하여 구현하려 했으나 CSS 스타일링의 어려움 때문에 div로 구현했다.
    • 게임이다 보니 접근성을 고려한 마크업을 구현하기 어려웠다.
      • 마우스가 아닌 키보드 사용자를 위한 접근을 구현하지 못한다.
      • 모바일에서 접속했을 때, 우클릭이 잘 되지 않아 깃발 기능을 사용하지 못하는 유저가 발생했따.

배운점 🤓😎😁

  1. 지뢰찾기는 복잡한 로직이다 보니, 플로우 차트를 그리니까 전체적인 그림이 머리속에 그려져서 좋았다.
  2. 자주 사용할 값을 상수로 선언하여, 사용자 게임 상태를 처리하는 함수를 가독성 좋게 구현할 수 있었다.
const SQUARE = {
    NORMAL: -1, // 닫힌칸, 지뢰X
    FLAG: -2, // 닫힌칸, 지뢰X, 깃발
    MINE: -3, // 닫힌칸, 지뢰O
    FLAG_MINE: -4, // 닫힌칸, 지뢰O, 깃발
    OPENED: 0 // 열린칸
  };
  1. 여러가지 기능을 구현하며 구현 능력을 향상시킬 수 있었다.

    • 지뢰찾기에서 가장 어려웠던 로직인 좌클릭시 열 수 있는 칸을 모두 열어주는 기능을 알고리즘에서만 사용했던 DFS를 사용하여 구현하였다.
    • 랜덤으로 색상을 골라오는 로직을 만들어 볼 수 있었다.

    랜덤 색상 JS

  2. 객체 디스럭처링의 재할당시 명시적으로 표현식임을 알려야 함을 알 수 있었다.

객체 디스럭처링 이슈

  1. Audio() Web API 를 사용해서 이벤트가 발생했을 때, 소리가 재생되도록 사용해볼 수 있었다.

Audio() - Web APIs | MDN

profile
🎨그림을 좋아하는 FE 개발자👩🏻‍💻

0개의 댓글