javaScript로 간단한 게임을 만들면서 로직 구현도 해보고 js에 더 익숙해질 겸 플러피 버드를 만들기로 했다.
플러피 버드는 클릭만으로 플레이하는 간단한 게임으로 오래 전 굉장히 유행했었다.
게임 제작에 입문하거나 가볍게 만드는 작품으로 많이 이용되서 리소스도 많은 편이나, 직접 만드는 게 더 재밌을 거 같아서 직접 그렸다.
닭 캐릭터와 배경, 위/아래 장애물만 그리면 되서 그림은 금방 그렸다. html로 게임화면으로 사용할 태그를 넣어주고, 그 안에 캐릭터와 장애물을 배치했다.
<div class="background">
<div class="chiken"></div>
<div class="down-wall"></div>
<div class="down-wall"></div>
<div class="down-wall"></div>
<div class="top-wall"></div>
<div class="top-wall"></div>
<div class="top-wall"></div>
</div>
css로 각각, background에 배경 이미지, chiken에 캐릭터 이미지, wall에 각각 위 아래 이미지를 적용시켰다.
닭 캐릭터가 중력을 받아 밑으로 떨어지도록 하는 코드를 작성, 자연스럽게 떨어지는 것을 구현하기 위해 닭의 bottom에 계속 점프력을 담은 변수를 더해준다. 그리고 점프력에 중력값을 빼서 중력을 받는 것처럼 보일 수 있게 코드를 작성했다.
//이런식으로 작성 let jumpPower = 3; let gravity = 0.3; chikenBottom += jumpPower; jumpPower -= gravity; chiken.style.bottom = `${chikenBottom}px`;
클릭 이벤트를 받아서 클릭 시 점프력에 다시 값을 재할당하면 캐릭터가 위로 점프하듯이 위치가 올라간다.
function jump() { jumpPower = 6.5; } document.addEventListener("click", () => { jump(); });
장애물이 움직이고, 캐릭터가 그 사이를 넘어가면 점수를 획득 할 수 있게 html을 추가로 작성했다.
<div class="chiken"></div>
<div class="down-wall anim"></div>
<div class="down-wall anim"></div>
<div class="down-wall anim"></div>
<div class="top-wall check anim"></div>
<div class="top-wall check anim"></div>
<div class="top-wall check anim"></div>
<span class="score">Score : </span>
<div class="start">Touch!</div>
장애물은 css 애니메이션으로 움직이게 하고, js를 통해 위치를 랜덤하게 바꿔줬다.
.top-wall.anim, .down-wall.anim { animation: wallmove 3s infinite linear; } @keyframes wallmove { 0% { } 100% { left: -300px; } }
animationiteration
속성을 이용해 애니메이션이 끝날때, 랜덤하게 위치를 바꾸도록 한다.topWalls.forEach((topWall, idx) => { topWall.addEventListener("animationiteration", () => { randomPos = Math.floor(Math.random() * 160 - 40) + 40; topWall.style.top = `-${randomPos}px`; downWalls[idx].style.top = `${-randomPos + 530}px`; if (topWall.classList.value === "top-wall anim") { topWall.classList.add("check"); } }); });
위에 달린 장애물을 기준으로 애니메이션이 끝나면 랜덤한 위치로 바꿔준다. 그 뒤, 매칭되는 밑 장애물의 위치를 특정 길이만큼 움직인다. 아래 check는 점수를 위한 클래스이다.
요소의 위치값을 얻기 위해 offset속성을 사용할 수 있다. 장애물에는 check라는 클래스를 줘서 점수 획득 후, check를 remove로 없애주어 중복 점수를 방지했다. 그리고 애니메이션이 끝나면 다시 check속성을 add시켜준다. (위 코드 참고)
topWalls.forEach((topWall) => { if ( chiken.offsetLeft + chiken.offsetWidth > topWall.offsetLeft + topWall.offsetWidth ) { if (topWall.classList.contains("check")) { Score++; scoreBoard.innerText = `Score : ${Score}`; topWall.classList.remove("check"); } } }); }
캐릭터의 right값과 장애물의 right값을 구해서, 캐릭터의 right값이 더 커지면(즉 캐릭터가 장애물을 지나갔다면) Score를 증가 시킨다.
offset속성으로 각각의 위치를 체크할 수 있으니 장애물과 닿았을 때 게임오버가 되도록 구현해줄 수 있다.
ex) 캐릭터의 top이 위 장애물의 bottom보다 작으면서 캐릭터의 right가 위 장애물의 left보다 크다면 충돌했음을 알 수 있다.
topWalls.forEach((topwall) => { if ( chiken.offsetLeft <= topwall.offsetLeft + topwall.offsetWidth && chiken.offsetLeft + chiken.offsetWidth >= topwall.offsetLeft && chiken.offsetTop <= topwall.offsetTop + topwall.offsetHeight && chiken.offsetTop + chiken.offsetHeight >= topwall.offsetTop ) { gameOver(); } });
추락하거나, 화면 밖으로 나가게 되면 게임오버
if (chiken.offsetTop + chiken.offsetHeight > 600 || chiken.offsetTop < 0) { gameOver(); }
게임 종료 시 점수와 재시작을 할 수 있는 팝업을 만들어 게임 로직을 완성
<body> <div class="background"> <div class="chiken"></div> <div class="down-wall anim"></div> <div class="down-wall anim"></div> <div class="down-wall anim"></div> <div class="top-wall check anim"></div> <div class="top-wall check anim"></div> <div class="top-wall check anim"></div> <span class="score">Score : </span> <div class="start">Touch!</div> <div class="dimd"> <div class="text-game-over"> <p>게임종료!</p> </div> <div class="score-board"> <p class="best-score"></p> <p class="text-score">점수 :</p> <div class="wrap-btn"> <button type="button" class="btn-restart">재시작</button> </div> </div> </div> </div> </body>
게임 종료 시 최고 점수 갱신
if (bestScore < Score) { bestScore = Score; }
animationPlayState
로 애니메이션 멈춤topWalls.forEach((topwall) => { topwall.style.animationPlayState = "paused"; }); downWalls.forEach((downwall) => { downwall.style.animationPlayState = "paused"; });
종료 팝업켜기
document.querySelector(".dimd").classList.add("on"); document.querySelector(".text-score").innerText = `SCORE : ${Score}`; document.querySelector(".best-score").innerText = `BEST : ${bestScore}`;
그 외 종료 팝업에서 sns 공유를 추가하고 프로젝트를 마무리했다. 다 만들고나서 과정을 쓰려니 쓰기 애매한 것들도 많고 그 때 어떻게 했는지 기억이 안나서 고생했다..
전체적인 코드를 적지는 않았지만 만약 플러피버드를 만들고 싶다면 참고할 부분이 있을 것 같다.
간단한 게임이지만 신경쓸게 많고 생각보다 쉽지 않았다. 일단은 프로젝트를 종료했지만 추가적으로 랭크를 구현하는 것을 최종 목표로 생각 중이다.