HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Fun Game</title>
<script
src="https://kit.fontawesome.com/9eb162ac0d.js"
crossorigin="anonymous"
></script>
<link rel="stylesheet" href="style.css" />
<script type="module" src="src/main.js" defer></script>
</head>
<body>
<section class="game">
<header>
<button class="game__button">
<i class="fas fa-play"></i>
</button>
<span class="game__timer">
00:00
</span>
<span class="game__score">0</span>
</header>
<section class="game__field"></section>
</section>
<section class="pop-up hide">
<header>
<button class="pop-up__refresh">
<i class="fas fa-redo"></i>
</button>
<span class="pop-up__message">You have WON!!!</span>
</header>
</section>
</body>
</html>
CSS
body {
text-align: center;
background-color: black;
}
button {
outline: none;
cursor: pointer;
border: none;
background: white;
border-radius: 20%;
padding: 0;
}
.game {
display: flex;
flex-direction: column;
position: relative;
width: 800px;
height: 550px;
margin: auto;
margin-top: 50px;
background: url(img/background.png) center/cover no-repeat;
}
header {
text-align: center;
padding: 8px;
}
.game__button {
width: 60px;
height: 60px;
font-size: 24px;
background-color: wheat;
border: 4px solid black;
transition: transfrom 300ms ease-in;
}
.game__button:hover {
transform: scale(1.1);
}
.game__timer {
display: block;
width: 100px;
margin: auto;
margin-top: 8px;
padding: 0 20px;
background-color: white;
border-radius: 10px;
font-size: 32px;
border: 5px solid black;
}
.game__score {
display: inline-block;
width: 50px;
height: 50px;
font-size: 38px;
margin-top: 4px;
background-color: darksalmon;
color: white;
border-radius: 50%;
border: 3px solid black;
}
.game__field {
width: 100%;
height: 100%;
}
.pop-up {
border-radius: 20px;
width: 400px;
height: 124px;
margin: auto;
background-color: #00000090;
color: white;
transform: translateY(-150%);
}
.pop-up__refresh {
width: 60px;
height: 60px;
font-size: 24px;
background-color: wheat;
border: 2px solid black;
transition: transfrom 300ms ease-in;
}
.pop-up__refresh:hover {
transform: scale(1.1);
}
.pop-up__message {
display: block;
font-size: 38px;
}
.hide {
display: none;
}
.carrot:hover,
.bug:hover {
transform: scale(1.1);
}
main.JS
'use strict';
import PopUp from './popup.js';
import { GameBuilder, Reason } from './game.js';
const game = new GameBuilder()
.gameDuration(60)
.carrotCount(20)
.bugCount(10)
.build();
const gameFinishBanner = new PopUp();
game.setGameStopListener(reason => {
let message;
switch (reason) {
case Reason.win:
message = 'YOU WON 🎉';
break;
case Reason.lose:
message = 'YOU LOST 💩';
break;
case Reason.cancel:
message = 'Replay❓';
break;
default:
throw new Error('not valid reason');
}
gameFinishBanner.showWithText(message);
});
gameFinishBanner.setClickListener(() => {
game.start();
});
PopUp JS
'use strict';
export default class PopUp {
constructor() {
this.popUp = document.querySelector('.pop-up');
this.popUpMessage = document.querySelector('.pop-up__message');
this.popUpRefreshBtn = document.querySelector('.pop-up__refresh');
this.popUpRefreshBtn.addEventListener('click', () => {
this._hide();
this.onClick && this.onClick();
});
}
setClickListener(onClick) { //
this.onClick = onClick;
}
showWithText(text) {
this.popUpMessage.innerText = text;
this.popUp.classList.remove('hide');
}
_hide() {
this.popUp.classList.add('hide');
}
}
Field JS
'use strict';
import * as sound from './sound.js';
const CARROT_SIZE = 80;
const FIELD_TOP_PADDING = 50;
export const ItemType = Object.freeze({
carrot: 'carrot',
bug: 'bug',
});
export class Field {
constructor(carrotCount, bugCount) {
this.carrotCount = carrotCount;
this.bugCount = bugCount;
this.field = document.querySelector('.game__field');
this.fieldRect = this.field.getBoundingClientRect();
this.onFieldClickListener = this.onFieldClickListener.bind(this);
this.field.addEventListener('click', this.onFieldClickListener);
}
init() {
this.field.innerHTML = '';
this.addItem(this.carrotCount, 'img/carrot.png', 'carrot');
this.addItem(this.bugCount, 'img/bug.png', 'bug');
}
setItemClickListener(onItemClick) {
this.onItemClick = onItemClick;
}
addItem(count, imgPath, className) {
const x1 = 0;
const x2 = this.fieldRect.width - CARROT_SIZE;
const y1 = this.field.offsetTop + FIELD_TOP_PADDING;
const y2 = this.field.offsetTop + this.fieldRect.height - CARROT_SIZE;
for (let i = 0; i < count; i++) {
const item = document.createElement('img');
item.setAttribute('class', className);
item.setAttribute('src', imgPath);
item.style.position = 'absolute';
const x = randomNumber(x1, x2);
const y = randomNumber(y1, y2);
item.style.left = `${x}px`;
item.style.top = `${y}px`;
item.style.userDrag = 'none';
this.field.appendChild(item);
}
}
onFieldClickListener(event) {
const target = event.target;
if (target.matches('.carrot')) {
sound.playCarrot();
target.remove();
this.onItemClick && this.onItemClick(ItemType.carrot);
} else if (target.matches('.bug')) {
this.onItemClick && this.onItemClick(ItemType.bug);
}
}
}
function randomNumber(min, max) {
return Math.random() * (max - min) + min;
}
Game JS
'use strict';
import { Field, ItemType } from './field.js';
import * as sound from './sound.js';
export class GameBuilder {
gameDuration(duration) {
this.gameDuration = duration;
return this;
}
carrotCount(num) {
this.carrotCount = num;
return this;
}
bugCount(num) {
this.bugCount = num;
return this;
}
build() {
return new Game(
this.gameDuration, //
this.carrotCount,
this.bugCount
);
}
}
export const Reason = Object.freeze({
win: 'win',
lose: 'lose',
cancel: 'cancel',
});
class Game {
constructor(gameDuration, carrotCount, bugCount) {
this.gameDuration = gameDuration;
this.carrotCount = carrotCount;
this.bugCount = bugCount;
this.field = new Field(this.carrotCount, this.bugCount);
this.field.setItemClickListener(item => this.onItemClick(item));
this.timerIndicator = document.querySelector('.game__timer');
this.scoreText = document.querySelector('.game__score');
this.gameBtn = document.querySelector('.game__button');
this.gameBtn.addEventListener('click', () => {
if (this.started) {
this.stop(Reason.cancel);
sound.playAlert();
} else {
this.start();
}
});
this.started = false;
this.score = 0;
this.timer = undefined;
}
setGameStopListener(onGameStop) {
this.onGameStop = onGameStop;
}
start() {
this.started = true;
this.initGame();
this.showStopButton();
this.showTimerAndScore();
this.startGameTimer();
sound.playBackground();
}
stop(reason) {
this.started = false;
this.hideStartButton();
this.stopGameTimer();
sound.stopBackground();
if (reason === Reason.win) {
sound.playWin();
} else if (reason === Reason.lose) {
sound.playLost();
}
this.onGameStop && this.onGameStop(reason);
}
initGame() {
this.score = 0;
this.updateScoreBoard(this.score);
this.field.init();
}
onItemClick(item) {
if (!this.started) {
return;
}
if (item === ItemType.carrot) {
this.score++;
this.updateScoreBoard(this.score);
if (this.score === this.carrotCount) {
this.stop(Reason.win);
}
} else {
this.stop(Reason.lose);
}
}
startGameTimer() {
let remainingTimeSec = this.gameDuration;
this.updateTimerText(remainingTimeSec);
this.timer = setInterval(() => {
if (remainingTimeSec <= 0) {
clearInterval(this.timer);
if (this.started) {
this.stop(this.score === this.carrotCount ? Reason.win : Reason.lose);
}
return;
}
this.updateTimerText(--remainingTimeSec);
}, 1000);
}
stopGameTimer() {
clearInterval(this.timer);
}
updateScoreBoard(newScore) {
this.scoreText.innerText = this.carrotCount - newScore;
}
showStartButton() {
const icon = this.gameBtn.querySelector('.fas');
icon.classList.remove('fa-stop');
this.gameBtn.style.visibility = 'visible';
}
showStopButton() {
const icon = this.gameBtn.querySelector('.fas');
icon.classList.add('fa-stop');
this.gameBtn.style.visibility = 'visible';
}
hideStartButton() {
this.gameBtn.style.visibility = 'hidden';
}
showTimerAndScore() {
this.timerIndicator.style.visibility = 'visible';
this.scoreText.style.visibility = 'visible';
}
hideTimerAndScore() {
this.timerIndicator.style.visibility = 'hidden';
this.scoreText.style.visibility = 'hidden';
}
updateTimerText(time) {
const minutes = Math.floor(time / 60);
const seconds = time % 60;
this.timerIndicator.innerHTML = `${minutes}:${seconds}`;
}
resetScoreText() {
this.scoreText.innerText = this.carrotCount;
}
}
Sound JS
'use strict';
const carrotSound = new Audio('./sound/carrot_pull.mp3');
const failSound = new Audio('./sound/bug_pull.mp3');
const winSound = new Audio('./sound/game_win.mp3');
const bgSound = new Audio('./sound/bg.mp3');
const alertSound = new Audio('./sound/alert.wav');
export function playCarrot() {
playSound(carrotSound);
}
export function playBug() {
playSound(failSound);
}
export function playWin() {
playSound(winSound);
}
export function playLost() {
playSound(failSound);
}
export function playBackground() {
playSound(bgSound);
}
export function stopBackground() {
bgSound.pause();
}
export function playAlert() {
playSound(alertSound);
}
function playSound(sound) {
sound.currentTime = 0;
sound.play();
}
'Front-end > Javascript 실습' 카테고리의 다른 글
6. 당근게임 - 리팩토링(팝업 클래스) (0) | 2022.04.05 |
---|---|
5. 당근게임 - 당근과 벌레 눌렀을때, 팝업창 핸들링 (0) | 2022.04.04 |
4. 당근게임 - pause, replay 팝업 (0) | 2022.04.04 |
3. 당근게임 - 타이머 만드는 법 (0) | 2022.04.04 |
2. 당근게임 - 재생버튼클릭 /당근과벌레 생성/ timer/score (0) | 2022.04.04 |