(내가 만든 쿠~ 키가 아닌 가위바위보 게임 🎶)
바닐라 자바스크립트로 간단한 가위바위보 게임을 만들어봤습니다.
토이 프로젝트로 가위바위보를 만들어봐야겠다고 결심한 발단 전개 위기 절정 결말 및 야무진 에필로그까지 쌈박하게 정리해보겠습니다.
UX/UI
게임을 시작하기 전 간단하게 게임에 대한 설명을 보여주고 Play 버튼을 누르면 게임 화면으로 넘어갑니다.
[You / Computer 의 점수], [가위바위보 버튼], [라운드별 결과], [유저가 선택한 패와 컴퓨터의 패]를 보여주는 순으로 나눠져 있습니다.
유저 또는 컴퓨터가 5점을 획득하면 종료 팝업이 뜨고, 최종 성적에 따라 종료 팝업의 텍스트를 변경하는 함수를 사용했습니다.
종료 팝업에는 [다시하기] 버튼과 [공유하기] 버튼이 있고, 버튼을 누르면 단순 종료 및 새로고침 또는 url 복사 후 새로고침이 됩니다.
플레이 전 화면 구현
게임 시작 전 이게 무슨 게임이고 어떻게 하면 끝나는지 간략하게 적어주었습니다.
통통 튀는 이미지들은 별도로 애니메이션을 적용하지 않았고 https://emojipedia.org/ 에서 다운로드 받았습니다.
<div class="start">
<div class="title">
컴퓨터와 가위바위보!
</div>
<div class="rsp">
<img src="./img/paper.png">
<img src="./img/rock.png">
<img src="./img/scissors.png">
</div>
<div class="desc">
컴퓨터와 가위바위보를 해보세요. <p>
5점을 먼저 얻은 쪽이 승리합니다.
</div>
<button class="playbtn"> Play </button>
</div>
Play를 누르면 start의 display가 none으로 바뀌고, 게임 화면으로 만들어준 ing의 display를 flex로 바꿔주었습니다.
게임 시작
게임 화면으로는 class명 ing를 만들었습니다.
1) .score : 유저의 점수와 컴퓨터의 점수를 표시했습니다.
2) .btns : 가위바위보 버튼들을 모아두었습니다. 이후 각 버튼별로 class를 다시 만들었습니다.
3) .result : 게임 시작 전엔 버튼을 눌러 달라는 안내 멘트 > 버튼을 누르면 게임 결과가 나오게 만들었습니다.
4) .choose : 유저와 컴퓨터의 패를 보여줍니다. score와 똑같이 왼쪽/오른쪽으로 나누었습니다.
<div class="ing">
<div class="score">
<div class="user-score">
<h2> You </h2>
<p> 0 </p>
</div>
<div class="computer-score">
<h2> Computer</h2>
<p> 0 </p>
</div>
</div>
<div class="btns">
<button class="redbtn"> 가위 </button>
<button class="bluebtn"> 바위 </button>
<button class="greenbtn"> 보 </button>
</div>
<div class="result">
버튼을 눌러주세요!
</div>
<div class="choose">
<div class="user-choose">
</div>
<div class="computer-choose">
</div>
</div>
</div>
패를 선택할 때
가위를 누르면 가위 모양이 나오고, 컴퓨터는 임의의 패를 냅니다.
맨 처음에는 단순히 '가위' 라고 텍스트로 보여줬는데 너무 심심해서 이미지를 넣어보기로 했습니다.
이미지를 넣으려면 해당 부분에 innerHTML = 이미지 소스를 넣어줘야 해서, 해당 부분의 중복을 피하고자 이미지 경로를 만들었습니다.
/* 이미지 경로 */
const imageList = {
scissors: "<img src='./img/scissors.png'>",
rock: "<img src='./img/rock.png'>",
paper: "<img src='./img/paper.png'>",
};
또한 처음에는 각 버튼을 누르면 버튼 별로 함수를 만들어 동일한 내용 여러 차례 중복되었습니다.
이를 개선하기 위해 함수에 인자를 넣어주는 방식으로 리팩토링했습니다...만 여전히 중복되는 부분들이 있네요.
/* 가위바위보 */
function play(userChoice) {
const computerChoice =
computerList[Math.floor(Math.random() * computerList.length)];
userChoose.innerHTML = imageList[userChoice];
computerChoose.innerHTML = imageList[computerChoice];
if (computerChoice === userChoice) {
result.innerText = "비겼어요!";
} else if (
(computerChoice === "scissors" && userChoice === "rock") ||
(computerChoice === "rock" && userChoice === "paper") ||
(computerChoice === "paper" && userChoice === "scissors")
) {
result.innerText = "이겼어요!";
userScore.innerText = parseInt(userScore.innerText) + 1;
} else {
result.innerText = "졌어요!";
computerScore.innerText = parseInt(computerScore.innerText) + 1;
}
if (
parseInt(userScore.innerText) >= 5 ||
parseInt(computerScore.innerText) >= 5
) {
endgame.style.display = "flex";
saygoodbye();
}
}
각 버튼을 누르면 play함수가 실행되면서 버튼에 해당되는 패가 userChoice라는 파라미터로 들어갑니다.
컴퓨터의 패는 computerList에서 임의로 추출하였고, imageList에서 그 값을 찾아 innerHTML에 이미지 소스를 넣어주었습니다.
(객체를 많이 사용해보지 않아 인덱스가 없는 상황이 어려워 indexOf를 쓴 요상한 코드를 지금 또 찾았네요 🥹)
switch가 아니라 조건문으로 결과를 만들었는데, 각 경우에 따라 result.innerText로 결과를 적어주었습니다.
점수 구현
문제는 점수를 더해주는 것이었는데, 아무 생각 없이 +1을 해주면 되는 거 아닌가 생각했으나 구현이 되지 않았습니다.
userScore가 처음에 문자로 있기 때문에, 이걸 parseInt로 숫자화 해주고 1점씩 더해주었습니다.
컴퓨터든 유저든 어느 한 쪽의 점수가 5점 이상이 되면 게임이 종료됩니다.
endgame이라는 class의 display가 none > flex로 바뀌면서 saygoodbye라는 함수를 실행시킵니다.
최종 성적에 따라 saygoodbye 함수로 endgame의 멘트를 수정합니다.
컴퓨터가 이겼다면 위와 같이, 유저가 이겼다면 축하한다는 멘트가 나올 수 있도록 innerText를 바꿔줬습니다.
function saygoodbye() {
if (parseInt(userScore.innerText) >= 5) {
endment.innerText = "축하합니다! 당신이 승리했어요 🥳";
} else {
endment.innerText = "아쉽지만 컴퓨터가 승리했어요 🥺";
}
}
종료 팝업이 뜨고 난 후
종료 팝업에는 [다시하기]와 [공유하기] 버튼이 있습니다.
function end() {
endgame.style.display = "none";
setTimeout("location.reload(true)", 1000);
}
function share() {
let url = window.location.href;
navigator.clipboard.writeText(url).then(() => {
alert("URL이 복사되었습니다.");
});
endgame.style.display = "none";
setTimeout("location.reload(true)", 1500);
}
[다시하기]를 누르면 종료 팝업의 display가 다시 none으로 바뀌면서, setTimeOut을 통해 1000밀리 초 후 새로고침 됩니다.
[공유하기]를 누르면 현재 url을 찾고, navigator 메서드를 사용해 클립보드에 복사한 다음 alert이 뜨게 만들었습니다.
alert이 뜨고 나면 마찬가지로 종료 팝업이 사라지고 1500밀리 초 후에 새로고침하도록 했습니다.
Rock Scissors Paper!
게임 종료 승리한 내용 다시하기 공유하기
summermong.github.io
미니 프로젝트로 가위바위보를 만든 이유
며칠 전 회고에도 작성했지만, 내 손으로 무언가를 만들었다고 말할 수 있는 것인지 회의감이 들었다.
특히 CSS는 거의 알지 못하는 상황에서 뭔가 활용할 수 있는 걸 스스로 만들어봐야겠다고 생각했다.
너무 어렵지 않으면서 흥미로워 했던 것, 이전에 색을 랜덤으로 추출하는 노마드코더의 챌린지를 하면서 Math.random() 메소드로 가위바위보를 만들어볼 수 있지 않을까? 생각했었던 게 떠올라서 가위바위보를 해기로 했다.
CSS가 원래 어려운 거였나요...
첫 번째 고비, 유저와 컴퓨터의 스코어를 한 줄에 일정한 간격을 두고 올리고 싶은데 안된다. ^^
뭐가 문제냐고 울부짖어도 안됨. 아니 그냥 안돼요. 였는데 justify-content space-around로 해결했다.
간격을 조금 더 벌리고 싶은데... 어떻게 하는지 모르겠어서 일단 여기로 쇼부 봤음 (신경 쓰여)
두 번째 고비, 유저와 컴퓨터의 패가 나와야 하는데 이미지가 안 나온다. ^^
생각해보니 이미지를 삽입해본 적이 없음. 기껏해야 맨날 빈 공간에 innerText = '가위' 만 써봤지 진짜 가위 이미지는 안 넣어봤던 것임.
이미지를 넣기 위해 어떻게 해야 하나 구글링을 했을 때 createElement('img')를 하면 된다고...
오 일리 있어! 했으나 가위를 누르면 리셋이 되지 않고 계속 가위 이미지가 나와서 가위가 한 5개가 찍혔음.
if문으로 해당 부분에 img가 있으면 더 나오지 않게 하자! 고 했는데 이러니까 다음 라운드 진행이 안됐다...
역시 포기하고 그냥 정직하게 '가위' 따위를 쓰는 게 나을까 했는데... 좀 더 알아보니 그냥 이미지 소스가 보이면 되는 것이 아닌가.
그렇다면 innerHTML로 이미지 소스를 보여주자! 했더니 드디어 구현이 되었다! 🥹
배우게 된 것
1. CSS... 특히 hover나 scale 같이 실습 예제로만, 눈으로만 봤던 기능들을 직접 고민하고 배치해봤다. 물론 여전히 flex는 얘 왜 안되지? 이러다가 찾곤 하지만 원하는 모양대로 구현한 것에 의의를 둔다.
2. innerText, innerHTML, textContent... 셋 다 비슷비슷하게 생겨서 몰랐는데 보여지는 부분이 어디까지인지 다르다는 걸 알게 됐다. 특히 습관적으로 innerText를 썼는데, 이번에 소스를 보여주려고 할 때는 innerHTML을 사용하면서 스스로 배우는 과정이 재밌었다.
3. 링크 복사를 하면서 비동기에 대해 확 이해했다. promise가 뭔지는 아직 잘 모르겠지만, 복사가 되면 콜백 함수가 실행되는 navigator 부분을 보면서 막연하게만 느껴졌던 요런... 이걸 뭐라고 할까? API? 를 써먹을 수 있어서 신기했다. setTimeOut이나, location 부분도 사용해보면서 공식 문서도 좀 들여다 보고, 그 과정에서 확실히 뭔가 넓어졌다는 생각이 들었다.
4. 함수 리팩토링의 중요성. 클론코딩을 하면 알아서 다음 수업에서 리팩토링을 해주다 보니 왜 하는지는 알겠으나 어떻게 해야 하는지 다시 훑어본 적이 없었다. (반성) 이번엔 내새끼라는 생각으로 계속 들여다보니 여기가 이상하다, 여기가 너무 중복된다는 생각으로 직접 고쳐보면서 유지보수의 중요성을 몸소 깨달았다...
5. 실질적으로 사용하진 않았지만 document.CreateElement로 뭘 만들어봤을 때 비로소 노마드 코더에서 배웠던 게 생각났다. 이외에 속성을 부여할 때 setAttribute() ... document.body.appendChild() 를 쓰면서 아 이렇게 쓰라고 한거였구나 를 깨달았다. 물론 오류가 있어서 사용하진 않았다 ^-^...
6. 친구들한테 해보라고 했는데 모바일이 많다 보니 웹사이트가 깨졌다. media query로 영차영차 맞췄는데 희열 쩔음... ㅜㅜ
아쉬운 점
1. 여전히 함수가 하나 이상의 역할을 하는 것 같다. 함수는 하나의 기능을 하는 게 제일 좋다고 들었는데, 너무 많은 기능을 하고 있는 것 같아 쪼개고 싶었지만 어떻게 쪼개야 할 지를 모르겠다.
2. 애니메이션을 내가 직접 넣어봤다면 어떨까. 현재는 이미지가 통통 튀는 모션을 이미 갖고 있는데, 친구가 아이디어를 준 것처럼 하나 둘 셋 하면 동시에 나오는 그런 느낌으로 만들어봐도 좋을 것 같았다.
3. 내비게이터나 로케이션에 대해서도 공부를 해봐야겠다. 공부까진 아니더라도 뭔가 어떻게 쓰는 건지?
글 쓸 때까지만 해도 흑흑 눈물 나겠구만 했는데 오히려 차가워짐...
2일 전엔 진짜 원형 그 잡채로 날 것 그대로였는데 이미지를 넣고 싶다는 생각으로 다시 만들어봤고...
오늘 아침까진 잘 된다~ 하다가 오후부터 사이즈 이슈로 고생고생하면서 드디어 끝냈는데...
끝낼 때만 해도 너무 뿌듯하고 기뻤는데 또 다시 절망
개발을 하게 되면 계속 이 감정기복의 연장이겠지?
뭔가를 구현했다는 희열과 부족하다는 갈망으로 천국과 지옥을 오갈 것 같은... ㅋㅋㅋㅋㅋㅋ
천국 맛 봤으니 이제 중독되겠다 하하
아무튼 내손내코 !! 바닐라JS로 만들어본 가위바위보 끝~~~
'🛠️ 프로젝트 > 🧸 토이 프로젝트' 카테고리의 다른 글
[HTML + CSS] 네이버 로그인 폼 클론 코딩 (0) | 2023.07.07 |
---|---|
[바닐라JS 토이프로젝트] MBTI 전생 테스트 만들기 (4) | 2023.05.15 |