【實作記錄】Pacman Game | Vanilla JS

Live Demo
Github
CodePen

Pacman Game

簡介

這是一款使用 JavaScript 製作的小精靈遊戲。玩家將所有點點吃完便會獲勝、被精靈吃掉遊戲便結束。點點分數為 1 分、大點點 (power pallet) 為 10 分、吃掉害怕狀態的精靈增加 100 分。其中也添加了音效,默認為關閉,可點擊右上的 “volume icon” 打開。

功能

  • 使用上下左右鍵移動 Pacman
  • 吃了 Power Pallet 便可以吃掉顏色為橘色的精靈
  • 每個動作都有音效,可以透過右上的 “volume icon” 關閉
  • 吃完點點便會宣佈獲勝
  • 被精靈吃掉,遊戲便結束

小筆記

Pacman 的移動

Pacman 的上下移動邏輯使用 switch 處理。判斷 pacman 在移動的時候是否碰到墻壁,若沒有碰到墻壁/沒有碰到精靈則可以往按鍵的方向行動。

  • 判斷是否碰到墻壁:如果要往下的那個沒有 “wall” 這個 class ,表示沒有
  • 判斷是否碰到精靈:如果要往下的那個沒有 “ghost” 這個 class ,表示沒有
keycode 移動方式 判斷方式
40 往下鍵 pacman 所在的位置 + width 判斷是否已經到最底層:如果 pacman 所在 index 加上 width 小於整個 layout 的最大 index, 表示沒有超出邊界
38 往上鍵 pacman 所在的位置 - width 判斷是否已經到最上層:如果 pacman 所在 index 減掉 width 小於整個 0, 表示沒有超出邊界
37 往左鍵 pacman 所在的位置 -1 判斷是否已經到最左邊:如果 pacman 所在 index 除 width 的餘數不等於 0, 表示沒有超出邊界
38 往由鍵 pacman 所在的位置 +1 判斷是否已經到最左邊:如果 pacman 所在 index 除 width 的餘數小於 width - 1, 表示沒有超出邊界
1
2
3
4
5
6
7
8
9
10
11
switch(e.keyCode) {
case 40 :
console.log("pressed down")
if (
pacmanCurrentIndex + width < width * width &&
!squares[pacmanCurrentIndex + width].classList.contains("wall")&&
!squares[pacmanCurrentIndex + width].classList.contains("ghost-liar")
)
pacmanCurrentIndex += width

break

精靈的 class

由於 4 個精靈是一組的,都有 className 、起始位置、移動速度、當下位置、是否為害怕狀態、計時器,因此使用 class 來處理。需要每一隻精靈執行共同的程式碼就使用 for Each.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Ghost{
constructor(className, startIndex, speed){
this.className = className
this.startIndex = startIndex
this.speed = speed
this.currentIndex = startIndex
this.isScared = false
this.timerId = NaN
}
}

//傳入需要的參數:classname, 起始位置,速度
const ghosts = [
new Ghost("blinky" , 347, 250),
new Ghost("pinky" , 376, 400),
new Ghost("inky" , 352, 300),
new Ghost("clyde" , 379, 500),
]

  • 把精靈畫進 grid 裡
    1
    2
    3
    ghosts.forEach (ghost => {
    squares[ghost.currentIndex].classList.add(ghost.className)
    squares[ghost.currentIndex].classList.add("ghost")

精靈移動

  • 使用 Math.random() 決定精靈移動的方向,因此方向是沒有邏輯的

    1
    2
    const directions = [-1 , +1 , -width , +width]
    let direction= directions[Math.floor(Math.random() * directions.length)]
  • 沒有碰到碰到精靈或墻壁,就可以往隨機選出來的方向前進一格

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    if (
    !squares[ghost.currentIndex + direction].classList.contains("wall") &&
    !squares[ghost.currentIndex + direction].classList.contains("ghost")
    ){
    squares[ghost.currentIndex].classList.remove(ghost.className)
    squares[ghost.currentIndex].classList.remove("ghost", "scared-ghost")
    ghost.currentIndex += direction

    squares[ghost.currentIndex].classList.add(ghost.className)
    squares[ghost.currentIndex].classList.add("ghost")
    }
  • 在吃了 power pallet 之後,精靈會轉為 scared 狀態,就可以被吃

    • this.isScared = true
    • 會轉為橘色
      1
      2
      3
      4
      5
      6
      7
      8
      9
      if (
      ghost.isScared && squares[ghost.currentIndex].classList.contains("pacman")
      ){
      squares[ghost.currentIndex].classList.remove(ghost.className,"scared-ghost")
      ghost.currentIndex = ghost.startIndex
      score += 100
      squares[ghost.currentIndex].classList.add(ghost.className , "ghost")
      eatGhostSound()
      }
  • 遊戲結束

    • 碰到精靈遊戲便會結束
    • 計時器會被清除,按鍵被移除,game over 字樣出現
      1
      2
      3
      4
      5
      6
      7
      8
      9
      if (
      !ghost.isScared && squares[ghost.currentIndex].classList.contains("pacman")
      ){
      ghosts.forEach(ghost => clearInterval(ghost.timerId))
      document.removeEventListener("keydown", control)
      gameOver.style.display = "block"
      overlay.style.display="block"
      deathSound()
      }

玩家贏了

  • 吃完所有點點就宣佈獲勝
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let dot = 305

    function win(){
    if( dot === 0){
    ghosts.forEach(ghost => clearInterval(ghost.timerId))
    document.removeEventListener("keydown", control)
    gameWin.style.display = "block"
    overlay.style.display="block"
    winSound()
    }
    }

小結

這個小遊戲中練習了 class 以及 switch. 這裡沒有處理精靈移動的邏輯,原本應該追著 / 包抄 pacman, 但因為是使用隨機方向,因此精靈們看起來很笨(扶額)。這個部分等我找到比較好的方式再來看看怎麼讓他們聰明一些。畫地圖是使用 excel 先畫一遍,再把他們轉為 index,才填上顏色。

這篇筆記為個人學習記錄,若有錯誤或是可以改進的地方再麻煩各位大大指點(鞠躬

Comments