iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 30
1
自我挑戰組

新手村-30 Day JS Coding Challenge系列 第 30

新手村30 - Whack A Mole

30 - Whack A Mole

俗話說的好,一天一蘋果,醫生遠離我

一天一 JS,What the f*ck JavaScript?

small steps every day - 記錄著新手村日記

完成目標

做一個打地鼠的遊戲

  • 功能
    • 一個關卡要幾分鐘要固定
    • 地鼠冒出來、自動縮下去的時間間隔要不一樣
  • 畫面
    • 畫面要顯示目前的關卡
    • 地鼠要自動冒出來、縮下去
    • 地鼠被點到要縮下去、數量要增加

index_START.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Whack A Mole!</title>
  <link href='https://fonts.googleapis.com/css?family=Amatic+SC:400,700' rel='stylesheet' type='text/css'>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <h1>Whack-a-mole! <span class="score">0</span></h1>
  <button onClick="startGame()">Start!</button>

  <div class="game">
    <div class="hole hole1">
      <div class="mole"></div>
    </div>
    <div class="hole hole2">
      <div class="mole"></div>
    </div>
    <div class="hole hole3">
      <div class="mole"></div>
    </div>
    <div class="hole hole4">
      <div class="mole"></div>
    </div>
    <div class="hole hole5">
      <div class="mole"></div>
    </div>
    <div class="hole hole6">
      <div class="mole"></div>
    </div>
  </div>

<script>
  const holes = document.querySelectorAll('.hole');
  const scoreBoard = document.querySelector('.score');
  const moles = document.querySelectorAll('.mole');

</script>
</body>
</html>

style.css

html {
  box-sizing: border-box;
  font-size: 10px;
  background: #ffc600;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  padding: 0;
  margin: 0;
  font-family: 'Amatic SC', cursive;
}

h1 {
  text-align: center;
  font-size: 10rem;
  line-height: 1;
  margin-bottom: 0;
}

.score {
  background: rgba(255,255,255,0.2);
  padding: 0 3rem;
  line-height: 1;
  border-radius: 1rem;
}

.game {
  width: 600px;
  height: 400px;
  display: flex;
  flex-wrap: wrap;
  margin: 0 auto;
}

.hole {
  flex: 1 0 33.33%;
  overflow: hidden;
  position: relative;
}

.hole:after {
  display: block;
  background: url(dirt.svg) bottom center no-repeat;
  background-size: contain;
  content: '';
  width: 100%;
  height:70px;
  position: absolute;
  z-index: 2;
  bottom: -30px;
}

.mole {
  background: url('mole.svg') bottom center no-repeat;
  background-size: 60%;
  position: absolute;
  top: 100%;
  width: 100%;
  height: 100%;
  transition:all 0.4s;
}

.hole.up .mole {
  top: 0;
}

JS - step by step

首先,我們先將需要的元素選取起來,必須先找到地鼠洞的List、地鼠圖的List、 span 的數量

<script>
  const holes = document.querySelectorAll('.hole');
  const scoreBoard = document.querySelector('.score');
  const moles = document.querySelectorAll('.mole');
  console.log(holes)
  console.log(scoreBoard)
  console.log(moles)

</script>

先來製作一個函式,讓這個函式執行後有一個上下限的隨機時間

<script>
  const holes = document.querySelectorAll('.hole');
  const scoreBoard = document.querySelector('.score');
  const moles = document.querySelectorAll('.mole');

  function randomTime(min, max) {
    let time = Math.floor(Math.random() * (max - min) + min);
    return time
  };
</script>

接下來,我們來取得隨機一個 hole 元素,如果與上一次取到的相同,我們就再次執行一次隨機選取的函式,不同的話就把現在的Hole指定給變數 lastHole

<script>
  const holes = document.querySelectorAll('.hole');
  const scoreBoard = document.querySelector('.score');
  const moles = document.querySelectorAll('.mole');

  function randomTime(min, max) {
    let time = Math.floor(Math.random() * (max - min) + min);
    return time
  };

  let lastHole;
  
  function randomHole(holes) {
    const idx = Math.floor(Math.random() * holes.length);
    const hole = holes[idx];
    if (hole === lastHole) {
      console.log('Ah nah thats the same one bud');
      return randomHole(holes);
    }
    lastHole = hole;
    return hole;
  }
</script>

時間都處理完畢後,我們要來來地鼠可以從地鼠洞跑出來,在前面的CSS當中可以知道,地鼠的一開始是被做了相對定位並 top:100%overflow:hidden 起來了!因此剛開始的時候地鼠其實是偷偷躲在底下的w

現在只要判斷什麼時候加上一個 Class 讓地鼠從地鼠洞出來以及多久會從地鼠洞出來一次,要新增&移除 Class

<script>  
  //上略
  let timeUp = false;

  function peep() {
    const time = randomTime(200, 1000);
    const hole = randomHole(holes);
    hole.classList.add('up');
    setTimeout(() => {
      hole.classList.remove('up');
      if (!timeUp) peep();
    }, time);
  }
</script>  

最後,再加上遊戲開始及點到一個就增加分數吧!這部分比較單純只要將 score 宣告變數出來,以及點到分數++就可以,然後趕緊來打地鼠吧w

<script>  
  //上略  
  moles.forEach(mole => mole.addEventListener('click', bonk));

  function bonk(e) {
    if(!e.isTrusted) return; // cheater!
    score++;
    this.parentNode.classList.remove('up');
    scoreBoard.textContent = score;
  }

  function startGame() {
    scoreBoard.textContent = 0;
    timeUp = false;
    score = 0;
    peep();
    setTimeout(() => timeUp = true, 10000)
  }
</script>  

就大功告成啦!

JS - final

<script>
  const holes = document.querySelectorAll('.hole');
  const scoreBoard = document.querySelector('.score');
  const moles = document.querySelectorAll('.mole');

  function randomTime(min, max) {
    let time = Math.floor(Math.random() * (max - min) + min);
    return time
  };

  let lastHole;

  function randomHole(holes) {
    const idx = Math.floor(Math.random() * holes.length);
    const hole = holes[idx];
    if (hole === lastHole) {
      console.log('Ah nah thats the same one bud');
      return randomHole(holes);
    }
    lastHole = hole;
    return hole;
  }

  function peep() {
    const time = randomTime(200, 1000);
    const hole = randomHole(holes);
    hole.classList.add('up');
    setTimeout(() => {
      hole.classList.remove('up');
      if (!timeUp) peep();
    }, time);
  }

  moles.forEach(mole => mole.addEventListener('click', bonk));

  function bonk(e) {
    if(!e.isTrusted) return; // cheater!
    score++;
    this.parentNode.classList.remove('up');
    scoreBoard.textContent = score;
  }

  function startGame() {
    scoreBoard.textContent = 0;
    timeUp = false;
    score = 0;
    peep();
    setTimeout(() => timeUp = true, 10000)
  }
  
</script>

本刊同步於個人網站:http://chestertang.site/

本次範例程式碼原作者來源:https://tinyurl.com/yavm5f5n


上一篇
新手村29 - Countdown Timer
系列文
新手村-30 Day JS Coding Challenge30

2 則留言

0
appletabby
iT邦新手 5 級 ‧ 2019-10-15 09:40:35

恭喜完賽!

0
阿展展展
iT邦好手 1 級 ‧ 2020-01-25 14:39:44

恭喜完賽 /images/emoticon/emoticon42.gif

我要留言

立即登入留言