더시드 14층 구현(javascript)

Sming·2021년 7월 14일
3

html,css로 마크업을 한뒤 이제 동적인 요소를 추가할차례다.
내가 넣고 싶은기능은 다음과 같다.

📂 기능
1. 몬스터들의 랜덤한 자리배치
2. 처음시작시 'START'팝업나온후 없어지면서 게임시작
3. 랜덤으로 바뀌는 배경화면 & 배경음악
4. 에임이미지과 사용자의 커서에 맞춰이동
5. 클릭을 할때마다 줄어드는 총알,클릭하면 에임이 살짝커지며 색이 변화 & 총소리추가
6. 1초마다 감소하는 타이머,0초일시 타이머정지
7. 몬스터를 맞췄을경우 몬스터가 처치이펙트 & 처치시 몬스터 소리
8. 화면에 있는 몬스터를 모두 처치한 경우 CLEAR,총알이 모두 소모할시 FAIL, 시간이 0초가 됬을경우 TIME OVER 팝업이 나타나게 함. +)타이머정지


0. 변수 설정

const limitTime=80;

let timer;
let bulletCount=60;
let mobCount=20;
let started=false;

나중에 이용하기위해 전역변수로 설정하는 녀석들이다.

1. 💿 게임시작


popUpToggle();
popUp.addEventListener('click',()=>{
    if(started){
        return;
    }
    started=true;
    setTimeout(()=>{
        initGame();
    },1000);
})


function popUpToggle(){
    if(!popUp.style.display){
        popUp.style.display='block';
    }else{
        popUp.style.display='';
    }
}

게임 시작을 할때 Toggle을 이용하여 처음에 숨겨두었던 start버튼을 나타나게 한뒤,popUp을 클릭할시 initGame을 바로 시키기보다 1초뒤에 시작시키기로 하였다.

2. 🎃 몬스터의 배치


function initGame(){
    sound.playSound();
    popUpToggle();
    startTimer();
    const x1=window.innerWidth-90;
    const y1=window.innerHeight-190;
    const imgPath='./img/move/move.0.png';
    for(let i=0;i<mobCount;i++){
        const item=document.createElement('div');
        item.setAttribute('class','mob');
        item.style.backgroundImage=`url(${imgPath})`;
        item.style.backgroundRepeat='no-repeat';
        item.style.position='absolute';
        const x=randomNumber(0,x1);
        const y=randomNumber(100,y1);
        item.style.left=`${x}px`;
        item.style.top=`${y}px`;
        setItemClass(item, x<window.innerWidth/2 ? 'left':'right');
        field.appendChild(item);
    }
}

function setItemClass(item,direction){
    item.classList.add(direction);
}


function randomNumber(min,max){
    return Math.random()*(max-min)+min;
}

이 부분에서 startTimer,sound은 뒤에서 알아볼것이고 윈도우의 너비,윈도우의 높이에서 몬스터가 가지는 너비,높이를 뺀값에서 랜덤한 숫자를 돌려서 그 좌표에 몬스터를 넣는것을 구현하였다.setItemClass라는 함수에서 x좌표가 내 화면의 반보다 작다면 'left'라는 class를 크다면 'right'라는 class를 주어 css구현했던 왼쪽일때의 애니메이션과 오른쪽일때의 애니메이션을 실행시킨다.

3. 🎨 배경화면 & 🎧 배경음악

'use strict';

const game=document.querySelector('.game');

const backGroundList=[
    './img/bg.jfif',
    './img/bg2.jfif',
    './img/background.jfif',
    './img/엘리니아.jpg',
    './img/bg3.jfif'
]

const soundList=[
    './sound/MapleLeaf.mp3',
    './sound/TheTuneOfAzureLight.mp3',
    './sound/LachelntheIllusionCity.mp3',
    './sound/WhenTheMorningComes.mp3',
    './sound/ChewChew MainTheme.mp3'
]
const randNum=Math.floor(Math.random()*backGroundList.length);
const backgroundMusic= new Audio(soundList[randNum]);
export function playSound(){
    backgroundMusic.currentTime=0;
    backgroundMusic.volume=0.6;
    backgroundMusic.play();
}

export function stopSound(){
    backgroundMusic.pause();
}

game.style.backgroundImage=`url(${backGroundList[randNum]})`;
game.style.backgroundRepeat='no-repeat';
game.style.backgroundPosition='center';

랜덤한 배경화면과 배경음악을 보여주는 코드이다.배경화면과 음악의 배열요소들의 순서들을 각맵에 맞도록설정하였다.sound는 main함수에서 사용할거니 export를 이용하였다.나중에 main함수에서 import를 이용하여 추가시킬수있도록하자 +)여기서 sound라는 변수는 import할떄 지정한 변수이름이다.

💻사용된 맵

메이플 아일랜드
엘리니아
츄츄아일랜드
레헬른
아르카나

이렇게 5개를 추가하였고 파일만 있다면 맵을 늘리는데는 어렵지않으니 나중에 하나하나 추가해나갈 예정이다.

4. 🧨 에임 이벤트

document.addEventListener('mousemove',handlerTarget);

function handlerTarget(event){
    const x=event.clientX-40;
    const y=event.clientY-50;
    target.style.transform=`translate(${x}px,${y}px)`;
    target.style.color='white';
}

마우스가 움직일때마다 이벤트를 발생시키며 그 이벤트가 발생할때마다 에임의 위치를 변경해주는것이다. 이때 에임의 position은 absolute이여야하고 left,top을 이용해서 조절하게된다면 레이아웃을 다시 만들어버리기에 transorm을 이용하여 최대한 성능을 떨어트리지 않을려고하였다.

5. 🖱 클릭 이벤트

document.addEventListener('click',handlerClick);

function handlerClick(event){
    if(!started){
        return;
    }
    const point=event.target;
    handlerBullet(--bulletCount);
    cursorEffect(event);
    shotWhere(point);
    soundShot();
}

function handlerBullet(bullet){
    if(bullet>=0){
        updateBulletText(bullet);
    }else{
        if(mobCount==0){
            endGame('CLEAR');
        }else{
            endGame('Fail');
        }
    }
}

function updateBulletText(bulletCount){
    bullet.innerText=`${bulletCount}`;
}

function cursorEffect(event){
    const x=event.clientX-40;
    const y=event.clientY-50;
    target.style.transform=`translate(${x}px,${y}px) scale(1.5)`;
    target.style.color='tomato';
}

function shotWhere(point){
    if(point.className=='mob left' || point.className=='mob right'){
        killMob(point);
    }
}

function soundShot(){
    shotSound.currentTime=0;
    shotSound.volume=0.3;
    shotSound.play();
}

가장 중요한 파트이다 보니 코드가 확실히 길다.클릭을 하게 될시 총알이 감소한 총알이 보일수있도록 함수를 사용하였고,내가 몬스터를 쏘는데 성공했다면 killMob이라는 함수로 이동하는데 이는 다음에 나올것이다,그리고 총을 다쏘면 발생하는 endGame()함수도 뒤에서 알아볼것이다.
soundShot은 위에서 shotSound라는 Audio object를 설정해주어 함수에서 재생하도록하였다.

6.⏰ 타이머 이벤트

function startTimer(){
    let remainingTime=limitTime;
    updateTimeText(remainingTime)
    timer=setInterval(function(){
        if(remainingTime<=0){
            stopTimer();
            endGame('Time Over');
            return;
        }
        updateTimeText(--remainingTime);
    },1000)
}


function stopTimer(){
    clearInterval(timer);
}

function updateTimeText(sec){
    const minutes=Math.floor(sec/60).toString();
    const seconds=(sec%60).toString().padStart('2',0);
    minuteTime.innerText=`${minutes}`;
    secondTime.innerText=`${seconds}`;
}

startTimer라는 함수를 만들어 타이머를 제어해주며,timer라는 변수에 setInterval의 반환값을 넣어준다. timer는 0.변수 설정에서 만들었던 변수이며 이것을 전역으로 설정을 해야 우리가 어떤 함수에서든 clearInterval을 통해 타이머를 정지할수가 있다.
타이머가 0초가되면 stopTimer함수를 이용하면 멈추고 endGame()을 통하여 게임을 마무리해준다.

7. ⚔ 몬스터 처치이벤트

function killMob(point){
    mobCount--;
    killSound();
    point.classList.add('died');
    setTimeout(()=>{
        point.remove();
    },700);
    if(mobCount==0){
        endGame('CLEAR');
    }
}

function killSound(){
    diedSound.currentTime=0.4;
    diedSound.play();
}

위의 5.클릭 이벤트에서 몬스터를 클릭하는데 성공하면 발생하는 killMob함수이며 몬스터 수를 줄이며 죽는소리와 died클래스를 주어 사라지는 사진을 없앤뒤에 700ms 뒤에 삭제하는 이유는 Css에서 animation-duration을 700ms로 주었기에 그 뒤에 바로 없애주었다.역시 mobCount=0이 될시 endGame()이 나온다.

8.게임 끝

function endGame(message){
    stopTimer();
    sound.stopSound();
    popUp.innerText=`${message}`;
    popUpToggle();
}

타이머,배경음악을 멈춘뒤 팝업을 보여준다.

📺결과물

클릭시 유튜브 링크로 이동합니다.

📄출처

배경음악 출처
몬스터 죽는소리는 직접 주황버섯 잡으면서 녹음했습니다..

😥 아쉬운점

커서를 클릭시 스케일이 커지고 색깔이 변하는 효과를 넣었는데 mousemove이벤트가 계속발생하여 바로 풀려버려서 잘 보이지 않게 됬다.

몬스터들의 움직임이 자유롭게 곡선으로 갔으면 했는데 구현하는방법을 모르겠다.

총알이미지가 없어서 🧨로 대체하였다.


프로젝트를 마치며..

이번 프로젝트를 완성하는데 8시간정도 걸린것같고 사진추출,음성추출 하는데 시간이 꽤 걸렸던것 같다.

그리고 기능구현만 생각하여 만들어서 코드가 많이 지저분한데 나중에 컴포넌트로 나누어서 리팩토링하여 다시 코드를 올리고 , 추가적으로 사용자가 맵을 고를수있는 기능과 게임끝날시 재시작 버튼을 추가해볼 계획이다.

이번에 처음으로 javascript로 무언가를 만들어보고 한달동안 공부한것으로 구현한것이다 보니 부족한 부분이 많이 있을수도 있습니다.혹시 고치면 좋을점과 잘못된것은 지적해주시면 감사하겠습니다.

profile
딩구르르

2개의 댓글

comment-user-thumbnail
2021년 9월 3일

와 대박입니다.. 그치만 프로필이 아잉이라서 아쉽습니다(?)

답글 달기
comment-user-thumbnail
2022년 3월 27일

괴수님 ㄷㄷ

답글 달기