[JavaScript Toy Project] Mini Game | 가위바위보 게임 만들기 토이프로젝트

이은진·2020년 11월 22일
25

바닐라 자바스크립트로 간단한 가위바위보 게임을 만들어보았습니다. 사용자가 가위, 바위, 보 중에 한 개를 선택하면 컴퓨터 측에서 랜덤으로 선택해, 그 결과를 위에 출력해주는 방식입니다. 애초에 기획했던 비주얼만으로는 난이도 최하일 줄 알았는데 생각보다 시간이 더 소요된 프로젝트였습니다.

contents

  1. Mini Game UI/UX
  2. Main Features
    2-1. How the game works
    2-2. Update status
    2-3. Save data into the local storage
    2-4. Reset button
  3. Details
    3-1. Hover effects
    3-2. Total score stays when refreshed

1. Mini Game UI/UX

가위바위보 게임은 크게 스코어를 알려주는 상단 바, 사용자와 컴퓨터가 선택한 것을 보여주는 박스, 사용자가 선택할 항목이 있는 박스로 나뉘어 있습니다.

2. Main Features

2-1. How the game works

사용자의 측에서 선택한 옵션과 컴퓨터 측에서 랜덤으로 출력한 옵션을 비교해서 일반적인 가위바위보 규칙에 따라 점수가 업데이트되는 방식으로 작동합니다.

//selecting buttons function
const selectImage = () => {
  if (rockBtn.classList.contains('click') && 
  !scissorsBtn.classList.contains('click') &&
  !paperBtn.classList.contains('click')) {
    playerSelectedImg.style.background = bgImage[0]
    playerSelectedText.innerText = selectedText[0]
  } else if (!rockBtn.classList.contains('click') && 
  scissorsBtn.classList.contains('click') &&
  !paperBtn.classList.contains('click')) {
    playerSelectedImg.style.background = bgImage[1]
    playerSelectedText.innerText = selectedText[1]
  } else if (!rockBtn.classList.contains('click') && 
  !scissorsBtn.classList.contains('click') &&
  paperBtn.classList.contains('click')){
    playerSelectedImg.style.background = bgImage[2]
    playerSelectedText.innerText = selectedText[2]
  } else {
    return
  }
}

컴퓨터가 출력할 랜덤 이미지는 Math.floor() 메서드와 Math.random() 메서드로 가져왔습니다.

const bgImage = [
  "url(image/rock.png) no-repeat center",
  "url(image/scissors.png) no-repeat center",
  "url(image/paper.png) no-repeat center"
]
const selectedText = ["Rock", "Scissors", "Paper"]

//get random computer image
const getRandomComputerImage = () => {
  const randomNum = Math.floor(Math.random() * 3)
  computerSelectedImg.style.background = bgImage[randomNum]
  computerSelectedText.innerText = selectedText[randomNum]
}

사용자가 클릭한 엘리먼트에 'click'이라는 클래스가 생성되면 그 결과에 따라 중간 영역의 왼쪽에 이미지가 출력됩니다. 그리고 나서 0.3초 후 컴퓨터가 랜덤한 이미지를 오른쪽에 출력하면 최종 결과가 나오게 됩니다.

//select button event listener
const selectBox = document.getElementById('select-box')
const rockBtn = document.getElementById('rock')
const scissorsBtn = document.getElementById('scissors')
const paperBtn = document.getElementById('paper')

//select button event listener
selectBox.addEventListener('click', (e) => {
  computerSelectedImg.style.background = ''
  computerSelectedText.innerText = ''
  if (e.target === rockBtn) {
    rockBtn.classList.add('click')
    scissorsBtn.classList.remove('click')
    paperBtn.classList.remove('click')
  } else if (e.target === scissorsBtn) {
    rockBtn.classList.remove('click')
    scissorsBtn.classList.add('click')
    paperBtn.classList.remove('click')
  } else if (e.target === paperBtn) {
    rockBtn.classList.remove('click')
    scissorsBtn.classList.remove('click')
    paperBtn.classList.add('click')
  } else {
    return false
  }
  selectImage()
  setTimeout(() => {
    getRandomComputerImage()
    updateStatus()
    updateLocalstorage()
  }, 300);

})

2-2. Update status

updateStatus() 함수는 게임의 결과를 크게 세 가지 경우로 나누어, 결과를 status라는 객체에 push() 메서드로 추가해 줍니다. 사용자가 선택한 옵션, 컴퓨터가 선택한 옵션, 어느 쪽이 이겼는지에 대한 내용이 배열에 포함돼 있습니다. 사용자가 옵션을 클릭했을 때 이 함수가 실행됩니다.

//updating status
const updateStatus = () => {
  if ( ( (playerSelectedText.innerText === selectedText[0]) && 
  (computerSelectedText.innerText === selectedText[1]) ) ||
  ( (playerSelectedText.innerText === selectedText[1]) && 
  (computerSelectedText.innerText === selectedText[2]) ) || 
  ( (playerSelectedText.innerText === selectedText[2]) && 
  (computerSelectedText.innerText === selectedText[0]) ) ) {
    const result = {
      playerSelect: playerSelectedText.innerText,
      player: 'win',
      computerSelect: computerSelectedText.innerText,
      computer: 'lose',
    }
    results.push(result)
    playerStatusEl.innerText++

  } else if ( ( (playerSelectedText.innerText === selectedText[0]) && 
  (computerSelectedText.innerText === selectedText[2]) ) ||
  ( (playerSelectedText.innerText === selectedText[1]) && 
  (computerSelectedText.innerText === selectedText[0]) ) || 
  ( (playerSelectedText.innerText === selectedText[2]) && 
  (computerSelectedText.innerText === selectedText[1]) )) {

    const result = {
      playerSelect: playerSelectedText.innerText,
      player: 'lose',
      computerSelect: computerSelectedText.innerText,
      computer: 'win',
    }
    results.push(result)
    computerStatusEl.innerText++

  } else {
    results.push({
      playerSelect: playerSelectedText.innerText,
      player: 'draw',
      computerSelect: computerSelectedText.innerText,
      computer: 'draw',
    })
  }
}

나중에 게임 결과를 저장하고 출력할 때 이 객체의 키와 값에 접근하여 데이터를 활용합니다.

const playerStatusEl = document.getElementById('status-result__player')
const computerStatusEl = document.getElementById('status-result__computer')
//rendering status by localstorage data
const renderStatus = () => {
  const results = getLocalStorageData
  playerStatusEl.innerText = results
            .map((result) => result["player"])
            .filter((word) => word === "win").length
  computerStatusEl.innerText = results
            .map((result) => result["computer"])
            .filter((word) => word === "win").length
}

2-3. Save data into the local storage

로컬스토리지에 데이터가 저장됩니다. localStorage.setItem(), localStorage.getItem() 메서드로 데이터를 넣고 가져옵니다. 원래 굳이 로컬스토리지에 정보를 저장하지 않고 결과에 따라 스코어를 +1 하는 걸로 만족하려고 했는데, 나중에 기능을 또 추가하게 되면 이전 정보를 활용할 수 있어야 하기 때문에 로컬스토리지를 활용하기로 했습니다.

let results = []
//update data in local storage
const updateLocalstorage = () => {
  localStorage.setItem('results', JSON.stringify(results))
}

//get data from local storage
const getLocalStorageData = JSON.parse(localStorage.getItem('results'))
console.log(getLocalStorageData)

results = getLocalStorageData // 새로고침 시 로컬스토리지 데이터를 사용하겠다는 의미

2-4. Reset button

스코어를 리셋해주는 버튼입니다. resetStatus() 함수가 실행되면 상단의 스코어와 게임 창이 리셋될 뿐만 아니라 로컬스토리지에서도 모든 게임 정보가 지워집니다.

const playerStatusEl = document.getElementById('status-result__player')
const computerStatusEl = document.getElementById('status-result__computer')
//reset status
const resetBtn = document.getElementById('reset')
const resetStatus = () => {
  playerStatusEl.innerText = 0
  computerStatusEl.innerText = 0
  playerSelectedImg.style.background = ''
  playerSelectedText.innerText = ''
  computerSelectedImg.style.background = ''
  computerSelectedText.innerText = ''
  results = []
  updateLocalstorage()
}

//reset button
resetBtn.addEventListener('click', resetStatus)

3. Details

3-1. Hover effects

약간의 마우스 오버 효과를 주어 사용자가 선택하려 하는 타겟을 잘 알아볼 수 있도록 했습니다.

.reset {
  all: unset;

  width: 100px;
  height: 36px;

  background: #fdfdfd;
  border: 1px solid #ddd;
  border-radius: 18px;

  font-size: 16px;
  font-family: Verdana, Geneva, Tahoma, sans-serif;

  display: flex;
  justify-content: space-evenly;
  align-items: center;

  cursor: pointer;
  padding: 0 6px;
  margin: 0 10px;

  transition: all .2s ease;
}

.reset:hover {
  background: #dec6eb;
  border: 1px solid #777;
}
.rock, .scissors, .paper {
  width: 80px;
  height: 80px;
  background: #aaa;

  border: 1px solid #444;
  border-radius: 40px;

  margin-right: 10px;
  cursor: pointer;

  transition: all .1s ease;
}

.rock:hover, .scissors:hover, .paper:hover{
  background-size: 56px 56px;
}

.rock {
  background: url("image/rock.png") center no-repeat;
  background-size: 50px 50px;
}

.scissors {
  background: url("image/scissors.png") center no-repeat;
  background-size: 50px 50px;
}

.paper {
  background: url("image/paper.png") center no-repeat;
  background-size: 50px 50px;
}

3-2. Total score stays when refreshed

로컬스토리지에 저장된 데이터를 새로고침했을 때 불러오도록 했습니다. 새로고침했을 때는 로컬스토리지에 데이터가 남아있지만 게임을 다시 이어나가면 데이터가 초기화되는 문제가 생겨서 꽤 오랜 시간 동안 고민을 했습니다. 고민끝에 마지막에 results = getLocalStorageData 한 줄을 추가해 주니 새로고침 후 다시 게임을 시작했을 때 로컬스토리지에서 가져온 results에 추가로 배열을 넣어줄 수 있게 됐습니다.

let results = []

//get data from local storage
const getLocalStorageData = JSON.parse(localStorage.getItem('results'))
console.log(getLocalStorageData)

results = getLocalStorageData
profile
빵굽는 프론트엔드 개발자

9개의 댓글

comment-user-thumbnail
2020년 11월 26일

안녕하세요 쓰신 글 많이 봤는데, 하시는 토이 프로젝트마다 UI가 너무 예쁘네요
인터랙티브 웹 개발자가 되고 싶은 사람으로써 많이 배우고 갑니다!

1개의 답글
comment-user-thumbnail
2020년 11월 27일

토이프로젝트로 연습한번 해봐야겠어요 동기부여 감사합니다!

1개의 답글
comment-user-thumbnail
2020년 12월 10일

안녕하세요~ 지나가다가 본 썸네일이 너무 아기자기하고 도전하고 싶은 맘을 불러일으켜서 댓글 남깁니다!
아무래도 게임이 제일 재밌는 토이 프로젝트 같아요 >_<

1개의 답글
comment-user-thumbnail
2020년 12월 22일

혹시 다음에 토이프로젝트 글 올리시는거면 깃허브에서 gitpage로 배포링크 남겨주실 수 있나요...? 너무 잘만들어서 직접 써보고 싶어요! :)

답글 달기
comment-user-thumbnail
2024년 9월 15일

Jake, a casual poker player, had always enjoyed the occasional hand with friends but never considered himself a serious competitor. One evening, while browsing through an online poker platform, he encountered a tournament with https://jogodotigre.net.br/ an entry fee that was well within his budget. Intrigued by the challenge and motivated by the potential prize pool, Jake decided to enter the tournament. The competition was fierce, with players from around the world showcasing their skills and strategies.

답글 달기