JavaScript-두더지 잡기 게임

Jenna·2022년 12월 29일
1

javascript

목록 보기
16/16

두더지잡기 게임


📌 순서도 그리기


이번 게임에는 이벤트 리스너가 각각 들어가서 순서도가 끊기는 부분이 많다.


📌 코드보기


<style>
        .cell {
            display: inline-block;
            position: relative;
            width: 200px;
            height: 200px;
            background: 'yellow';
            overflow: hidden;
        }

        .gopher,
        .bomb {
            width: 200px;
            height: 200px;
            bottom: 0;
            position: absolute;
            transition: bottom 1s;
        }

        .gopher {
            background: url('/images/gopher.png') center center no-repeat;
            background-size: 200px 200px;
        }

        .dead {
            background: url('/images/dead_gopher.png') center center no-repeat;
            background-size: 200px 200px;
        }

        .bomb {
            background: url('/images/bomb.png') center center no-repeat;
            background-size: 200px 200px;
        }

        .boom {
            background: url('/images/explode.png') center center no-repeat;
            background-size: 200px 200px;
        }

        .hidden {
            bottom: -200px;
        }

        .hole {
            width: 200px;
            height: 150px;
            position: absolute;
            bottom: 0;
            background: url('/images/mole-hole.png') center center no-repeat;
            background-size: 200px 150px;
        }

        .hole-front {
            width: 200px;
            height: 30px;
            position: absolute;
            bottom: 0;
            background: url('/images/mole-hole-front.png') center center no-repeat;
            background-size: 200px 30px;
        }
    </style>
</head>

<body>
    <div>
        <span id="timer">8</span>&nbsp;
        <span id="score">0</span><span id="life">3</span>목숨
        <button id="start">시작</button>
    </div>
    <div id="game">
        <div class="row">
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
        </div>
        <div class="row">
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
        </div>
        <div class="row">
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
            <div class="cell">
                <div class="hole"></div>
                <div class="gopher hidden"></div>
                <div class="bomb hidden"></div>
                <div class="hole-front"></div>
            </div>
        </div>
    </div>
    <script>
        const $timer = document.querySelector('#timer');
        const $score = document.querySelector('#score');
        const $game = document.querySelector('#game');
        const $start = document.querySelector('#start');
        const $$cells = document.querySelectorAll('.cell');
        const $life = document.querySelector('#life');

        const holes = [0, 0, 0, 0, 0, 0, 0, 0, 0];
        let started = false;
        let score = 0;
        let time = 60;
        let life = 3;
        let timerId;
        let tickId;

        $start.addEventListener('click', () => {
            if (started) return; //이미 시작했으면 무시
            started = true;
            console.log('시작');
            const timerId = setInterval(() => {
                time = (time * 10 - 1) / 10; //소수점 계산시 문제 있음
                $timer.textContent = time;
                if (time === 0) {
                    setTimeout(() => {
                        clearInterval(timerId);
                        clearInterval(tickId);
                        alert(`게임오버! 점수는 ${score}`);
                    }, 50);
                }
            }, 100);
            const tickId = setInterval(tick, 1000);
            tick();
        });
        //if의 특성을 생각해서 나오는 비율 누적확률로 해주면 편리하다.
        let gopherPercent = 0.3;
        let bombPercent = 0.5;
        //비동기때문에 오류가 생기면 이벤트 루프로 해석 
        function tick() {
            holes.forEach((hole, index) => {
                if (hole) return; //무언가 일어나고 있으면 return / settimeout이 2초에 한번 실행되게 됨
                const randomValue = Math.random();
                if (randomValue < gopherPercent) {
                    const $gopher = $$cells[index].querySelector('.gopher');
                    holes[index] = setTimeout(() => { //hole이라고 넣어주면 원시값이기 때문에 원시값 재할당 불가 그래서 holes[index]에 넣어준다.
                        $gopher.classList.add('hidden');
                        holes[index] = 0;
                    }, 1000);
                    $gopher.classList.remove('hidden');
                } else if (randomValue < bombPercent) {
                    const $bomb = $$cells[index].querySelector('.bomb');
                    holes[index] = setTimeout(() => { //hole이라고 넣어주면 원시값이기 때문에 원시값 재할당 불가 그래서 holes[index]에 넣어준다.
                        $bomb.classList.add('hidden');
                        holes[index] = 0;
                    }, 1000);
                    $bomb.classList.remove('hidden');

                }
            });
        }
        $$cells.forEach(($cell, index) => {
            $cell.querySelector('.gopher').addEventListener('click', (event) => {
                if (!event.target.classList.contains('dead')) {
                    score += 1;
                    $score.textContent = score;
                }
                event.target.classList.add('dead');
                event.target.classList.add('hidden');
                clearTimeout(holes[index]); //기존 내려가는 타이머 제거
                setTimeout(() => {
                    holes[index] = 0;
                    event.target.classList.remove('dead'); //울고있는 두더지 제거 
                }, 1000);

            });
            $cell.querySelector('.bomb').addEventListener('click', (event) => {
                if (!event.target.classList.contains('boom')) {
                    life--;
                    $life.textContent = life;
                    if (life === 0) {
                        clearInterval(timerId);
                        clearInterval(tickId);
                        setTimeout(() => {
                            alert(`게임오버! 점수는 ${score}`);
                        }, 50);
                    }
                }
                event.target.classList.add('boom');
                event.target.classList.add('hidden');
                clearTimeout(holes[index]);
                setTimeout(() => {
                    holes[index] = 0;
                    event.target.classList.remove('boom');
                }, 1000);
            });
        });
    </script>
</body>

📌 코드 공부하기


컴퓨터의 소수점 계산 : 컴퓨터가 소수점 단위를 계산할 때 나머지 값 때문에 오류가 날 가능성이 있으므로 다음과 같이 계산해준다.

time = (time * 10 - 1) / 10;

이벤트 루프 분석으로 문제 해결하기

function tick() {
            holes.forEach((hole, index) => {
                if (hole) return; //무언가 일어나고 있으면 return / settimeout이 2초에 한번 실행되게 됨
                const randomValue = Math.random();
                if (randomValue < gopherPercent) {
                    const $gopher = $$cells[index].querySelector('.gopher');
                    holes[index] = setTimeout(() => { 
                        $gopher.classList.add('hidden');
                        holes[index] = 0;
                    }, 1000);
                    $gopher.classList.remove('hidden');
                } else if (randomValue < bombPercent) {
                    const $bomb = $$cells[index].querySelector('.bomb');
                    holes[index] = setTimeout(() => { 
                        $bomb.classList.add('hidden');
                        holes[index] = 0;
                    }, 1000);
                    $bomb.classList.remove('hidden');

                }
            });
        }

비동기 코드에서 오류가 나면 이벤트 루프로 문제를 해결한다.
위 코드에서 if (hole) return ;
부분이 빠지면 두더지가 올라오지 않는 오류가 발생한다.
두더지가 올라오자마자 지워지는 이벤트가 실행되기때문이다.
따라서 hole에 무언가 있으면 return해주는 코드를 추가해 오류를 막아준다.


비율 계산하기
30%, 20%, 50% 의 확률로 두더지, 폭탄, 아무것도 올라오지 않을 확률을 세팅한다고 가정했을때 0.3, 0.2, 0.5 로 비율을 세팅할 확률이 높지만
if문의 특성을 생각하여 누적 확률로 세팅해주면 편리하다.

let gopherPercent = 0.3;
let bombPercent = 0.5;
        
        
                if (randomValue < gopherPercent) {
                    const $gopher = $$cells[index].querySelector('.gopher');
                    holes[index] = setTimeout(() => { 
                        $gopher.classList.add('hidden');
                        holes[index] = 0;
                    }, 1000);
                    $gopher.classList.remove('hidden');
                } else if (randomValue < bombPercent) {
                    const $bomb = $$cells[index].querySelector('.bomb');
                    holes[index] = setTimeout(() => { 
                        $bomb.classList.add('hidden');
                        holes[index] = 0;
                    }, 1000);
                    $bomb.classList.remove('hidden');

                }
            });
        
profile
connecting the dots 💫

0개의 댓글