Notice
Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

joyful

[Javascript] 가위바위보 게임 본문

Javascript

[Javascript] 가위바위보 게임

조이풀한 개발자 2023. 1. 19. 17:56

 

 

이 글은 유튜브 제로초님의 가위바위보 게임 강좌를 참고하여 작성한 글입니다. 

 

자바스크립트로 가위바위보 게임을 만들어 보았습니다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>가위바위보 게임</title>

    <style>
        #computer {
            width: 142px;
            height: 200px;
        }
    </style>
</head>
<body>
    <div id="computer"></div>
    <div class="rsp-wrap">
        <button id="scissors" class="btn">가위</button>
        <button id="rock" class="btn">바위</button>
        <button id="paper" class="btn">보</button>
    </div>
    <div id="score"></div>  
</body>
</html>

<script>
    const $computer = document.querySelector("#computer");
    const $score = document.querySelector("#score");
    const $rock = document.querySelector("#rock");
    const $scissors = document.querySelector("#scissors");
    const $paper = document.querySelector("#paper");
    const IMG_URL = './img/rsp.png';
    $computer.style.background = `url(${IMG_URL}) -440px 0`;
    $computer.style.backgroundSize = 'auto 200px';

    //객체(rsp의 X좌표라는 공통점으로 묶기)
    const rspX = {
      scissors: '0', // 가위
      rock: '-220px', // 바위
      paper: '-440px', // 보
    };

    //이미지 변환
    let computerChoice = 'scissors';
    const changeComputerHand = () => {
      if (computerChoice === 'rock') { // 바위면
        computerChoice = 'scissors'; //데이터는 가위로 바뀌고
      } else if (computerChoice === 'scissors') { // 가위면
        computerChoice = 'paper'; //데이터는 보로 바뀌고
      } else if (computerChoice === 'paper') { // 보면
        computerChoice = 'rock'; //데이터는 바위로 바뀌고
      }
      $computer.style.background = `url(${IMG_URL}) ${rspX[computerChoice]} 0`; //화면도 바뀌고
      $computer.style.backgroundSize = 'auto 200px'; //사이즈를 다시 선언하지 않으면 리셋되기 때문에 반드시 다시 선언해줘야 함
    }
    let intervalId = setInterval(changeComputerHand, 100);

    const scoreTable = { //규칙 찾기 위해 가위, 바위, 보를 숫자로 변환
      rock: 0,
      scissors: 1,
      paper: -1,
    };
 
    let clickable = true;
    let score = 0;
    const clickButton = (event) => {
      if (clickable) {
        //버튼 눌렀을 때 화면 멈춤
        clearInterval(intervalId); 
        clickable = false; //버그 수정:1초 동안 버튼이 클릭되지 않게  -버그:여러번 클릭하면서 interval 값이 중복저장되어 빨리 움직이고 버튼 눌렀을 때 멈추지 않음

        //타이머 설정해서 설정시간(1초) 지난 후 재실행
        setTimeout(() => {
          clickable = true; //버그 수정:1초 뒤 버튼 다시 클릭 되게
          intervalId = setInterval(changeComputerHand, 100);
        }, 1000);

        // 점수 계산 및 화면 표시
        const myChoice = event.target.textContent === '바위' //내 선택(텍스트)이 바위면 데이터도 바위
          ? 'rock' 
          : event.target.textContent === '가위' 
            ? 'scissors' 
            : 'paper';
        const myScore = scoreTable[myChoice]; //내 점수
        const computerScore = scoreTable[computerChoice]; //컴퓨터 점수
        const diff = myScore - computerScore; //내 점수에서 컴퓨터 점수를 빼서 나오는 점수대로 승부 결정

        let message;
        // 2, -1은 승리조건이고, -2, 1은 패배조건
        if ([2, -1].includes(diff)) {
          score += 1;
          message = '이겼습니다!';
        } else if ([-2, 1].includes(diff)) {
          score -= 1;
          message = '졌습니다!';
        } else {
          message = '비겼습니다!';
        }
        $score.textContent = `${message}`;       
      }
    }; 
    $rock.addEventListener('click', clickButton);
    $scissors.addEventListener('click', clickButton);
    $paper.addEventListener('click', clickButton);
</script>

만들다 보니 문제점이 하나 있었습니다.

이미지를 변환하는 코드에서 스크립트로 background 이미지의 position 스타일을 지정했는데,

이 게임은 간단한 프로그램이라서 괜찮지만, 이 경우가 많아지면 성능 저하의 원인이 될 수 있습니다.

따라서 스크립트는 스크립트에만! 스타일은 스타일에만! 지정해주는 것이 좋습니다. 

 

그래서 미리 지정할 수 있는 스타일은 스타일에만 지정한 후, 

클래스명을 추가했다 빼주는 방식으로 변경해보았습니다.

 

바로 이렇게요!

전체적인 코드 보여드리고 세부적으로 변경된 부분 정리하겠습니다,

lines (90 sloc)  3.39 KB

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>가위바위보 게임</title>

    <style>
      #computer {
        width: 142px;
        height: 200px;
        background: url(https://i.postimg.cc/7PM3vTRh/rsp.png) -440px 0;
        background-size: auto 200px;
      }
      #computer.is-scissors {
        background-position: 0;
      }
      #computer.is-rock {
        background-position: -220px 0;
      }
      #computer.is-paper {
        background-position: -440px 0;
      }
    </style>
</head>
<body>
    <div id="computer" class="is-scissors"></div>
    <div class="rsp-wrap">
        <button id="scissors" class="btn">가위</button>
        <button id="rock" class="btn">바위</button>
        <button id="paper" class="btn">보</button>
    </div>
    <div id="score"></div>  
</body>
</html>

<script>
  const $computer = document.querySelector("#computer");
  const $score = document.querySelector("#score");
  const $rock = document.querySelector("#rock");
  const $scissors = document.querySelector("#scissors");
  const $paper = document.querySelector("#paper");

  //이미지 변환
  let prevComputerChoice;
  let computerChoice = 'scissors';
  const changeComputerHand = () => {
    prevComputerChoice = computerChoice;
    if (computerChoice === 'rock') { // 바위면
      computerChoice = 'scissors'; //데이터는 가위로 바뀌고
    } else if (computerChoice === 'scissors') { // 가위면
      computerChoice = 'paper'; //데이터는 보로 바뀌고
    } else if (computerChoice === 'paper') { // 보면
      computerChoice = 'rock'; //데이터는 바위로 바뀌고
    }
    $computer.classList.remove(`is-${prevComputerChoice}`);  
    $computer.classList.add(`is-${computerChoice}`);
  }
  let intervalId = setInterval(changeComputerHand, 100);

  const scoreTable = { //규칙 찾기 위해 가위, 바위, 보를 숫자로 변환
    rock: 0,
    scissors: 1,
    paper: -1,
  };

  let clickable = true;
  let score = 0;
  const clickButton = (event) => {
    if (clickable) {
      //버튼 눌렀을 때 화면 멈춤
      clearInterval(intervalId); 
      clickable = false; //버그 수정:1초 동안 버튼이 클릭되지 않게  -버그:여러번 클릭하면서 interval 값이 중복저장되어 빨리 움직이고 버튼 눌렀을 때 멈추지 않음

      //타이머 설정해서 설정시간(1초) 지난 후 재실행
      setTimeout(() => {
        clickable = true; //버그 수정:1초 뒤 버튼 다시 클릭 되게
        intervalId = setInterval(changeComputerHand, 100);
      }, 1000);

      // 점수 계산 및 화면 표시
      const myChoice = event.target.textContent === '바위' //내 선택(텍스트)이 바위면 데이터도 바위
      ? 'rock' 
      : event.target.textContent === '가위' 
      ? 'scissors' 
      : 'paper';
      const myScore = scoreTable[myChoice]; //내 점수
      const computerScore = scoreTable[computerChoice]; //컴퓨터 점수
      const diff = myScore - computerScore; //내 점수에서 컴퓨터 점수를 빼서 나오는 점수대로 승부 결정

      let message;
      // 2, -1은 승리조건이고, -2, 1은 패배조건
      if ([2, -1].includes(diff)) {
        score += 1;
        message = '이겼습니다!';
      } else if ([-2, 1].includes(diff)) {
        score -= 1;
        message = '졌습니다!';
      } else {
        message = '비겼습니다!';
      }
      $score.textContent = `${message}`;       
    }
  }; 
  $rock.addEventListener('click', clickButton);
  $scissors.addEventListener('click', clickButton);
  $paper.addEventListener('click', clickButton);
</script>

 

 

먼저, html에서 computer 박스에 'is-scissors'라는 클래스명을 붙였습니다.

컴퓨터의 데이터가 가위일 때 가위 이미지가 스크립트로 불러오도록 말이죠!

 

그리고 스타일에서 배경이미지의 경로와 사이즈를 선언하고

각각의 'is-~'클래스가 붙을 때 가위, 바위, 보의 이미지 위치를 선언했습니다.

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>가위바위보 게임</title>

    <style>
      #computer {
        width: 142px;
        height: 200px;
        background: url(https://i.postimg.cc/7PM3vTRh/rsp.png) -440px 0;
        background-size: auto 200px;
      }
      #computer.is-scissors {
        background-position: 0;
      }
      #computer.is-rock {
        background-position: -220px 0;
      }
      #computer.is-paper {
        background-position: -440px 0;
      }
    </style>
</head>

 

 

스크립트에서는 배경이미지의 경로와 사이즈, position에 관련된 스타일을 모두 지웠습니다.

그리고 클래스명을 지웠다 추가할 수 있는 

classList.add  classList.remove라는 함수를 사용했습니다!

 

  //이미지 변환
  let prevComputerChoice;
  let computerChoice = 'scissors';
  const changeComputerHand = () => {
    prevComputerChoice = computerChoice;
    if (computerChoice === 'rock') { // 바위면
      computerChoice = 'scissors'; //데이터는 가위로 바뀌고
    } else if (computerChoice === 'scissors') { // 가위면
      computerChoice = 'paper'; //데이터는 보로 바뀌고
    } else if (computerChoice === 'paper') { // 보면
      computerChoice = 'rock'; //데이터는 바위로 바뀌고
    }
    $computer.classList.remove(`is-${prevComputerChoice}`);  
    $computer.classList.add(`is-${computerChoice}`);
  }
  let intervalId = setInterval(changeComputerHand, 100);

 

`is-rock` `is-paper` `is-scissors`라는 클래스들을 지웠다 추가하여 이미지의 위치가 interval되게 한다는 의미이죠!

 

그런데 여기서!

이미 선언된 데이터가 지워져야만 새로운 데이터가 읽히며 가위, 바위, 보 이미지가 변경이 됩니다.

 

따라서, computerChoice라는 컴퓨터의 데이터가 바뀌기 전에  이전 데이터를 저장하고 이를 다시 부르는 저장소가 필요합니다!

 

바로 새로운 변수를 만드는 것이죠!

 

이 저장소를 'prevComputerChoice'라는 변수로 선언하고, classList.remove함수로 지워지도록 했습니다.

 그럼 이전 데이터는 지워지고, 새로운 데이터가 추가되는 동작이 반복되며 가위, 바위, 보 이미지의 위치가 변경되겠죠!

 

'prevComputerChoice=computeChoice'

이 변수는 이미지가 변환되는 동작에 모두 실행되어야 하기 때문에 전역변수로 선언해야 합니다.

따라서 if문 위에 전체적으로 실행되도록 작성해야 합니다.