You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/zh-TW/6-space-game/3-moving-elements-around
localizeflow[bot] a5481015bd
chore(i18n): sync translations with latest source changes (chunk 2/2, 261 changes)
7 months ago
..
README.md chore(i18n): sync translations with latest source changes (chunk 2/2, 261 changes) 7 months ago
assignment.md chore(i18n): sync translations with latest source changes (chunk 2/2, 261 changes) 7 months ago

README.md

建造太空遊戲第三部分:新增移動功能

journey
    title 你的遊戲動畫旅程
    section 移動基礎
      了解運動原理: 3: Student
      學習座標更新: 4: Student
      實作基本移動: 4: Student
    section 玩家控制
      處理鍵盤事件: 4: Student
      防止預設行為: 5: Student
      創建回應式控制: 5: Student
    section 遊戲系統
      建立遊戲迴圈: 5: Student
      管理物件生命週期: 5: Student
      實作發布/訂閱模式: 5: Student

想想你最喜愛的遊戲——吸引人的不只是漂亮的畫面,更是所有事物隨著你的操作移動和回應的方式。現在你的太空遊戲像是一幅漂亮的畫,但我們將新增讓它栩栩如生的移動效果。

當NASA的工程師為阿波羅任務編寫導航電腦程式時面臨相似的挑戰如何讓太空船在維持自動修正航線的同時對飛行員的輸入做出反應我們今天要學的原則即反映了這些概念——管理玩家控制的移動和自動系統行為。

在本課程中,你將學會如何讓太空船滑動穿梭畫面、響應玩家指令並創造流暢的移動模式。我們會把所有內容拆解成易於理解的概念,並自然地彼此銜接。

最後,你將能讓玩家駕駛英雄飛船在畫面上飛行,同時敵艦在上方巡邏。更重要的是,你將理解驅動遊戲移動系統的核心原理。

mindmap
  root((遊戲動畫))
    Movement Types
      玩家控制
      自動移動
      物理基礎
      指定路徑
    Event Handling
      鍵盤輸入
      滑鼠事件
      觸控控制
      預設防止
    Game Loop
      更新邏輯
      渲染畫面
      清除畫布
      幀率控制
    Object Management
      位置更新
      碰撞檢測
      生命週期管理
      狀態追蹤
    Communication
      發佈/訂閱模式
      事件發射器
      訊息傳遞
      鬆散耦合

課前測驗

課前測驗

理解遊戲移動

遊戲之所以生動,是因為事物開始在畫面上移動,這基本上有兩種方式:

  • 玩家控制移動:當你按鍵或點擊滑鼠,某個物件移動。這是你與遊戲世界的直接連結。
  • 自動移動:遊戲自己決定物體移動,例如那些敵艦無論你是否操作,都會在畫面巡邏。

讓物體在電腦畫面上移動比你想像的更簡單。還記得數學課上的 x 和 y 座標嗎這正是我們用的東西。當伽利略在1610年觀測木星衛星時實際上也是做同樣的事——紀錄位置隨時間的變化以理解運動模式。

在畫面上移動物件就像製作翻頁動畫——你需要遵守這三個簡單步驟:

flowchart LR
    A["影格 N"] --> B["更新位置"]
    B --> C["清除畫布"]
    C --> D["繪製物件"]
    D --> E["影格 N+1"]
    E --> F{繼續嗎?}
    F -->|是| B
    F -->|否| G["遊戲結束"]
    
    subgraph "動畫循環"
        H["1. 計算新位置"]
        I["2. 擦除前一影格"]
        J["3. 渲染新影格"]
    end
    
    style B fill:#e1f5fe
    style C fill:#ffebee
    style D fill:#e8f5e8
  1. 更新位置 — 改變你的物件應該在的位置可能是向右移動5個像素
  2. 清除舊畫面 — 清除畫面避免看到殘影
  3. 繪製新畫面 — 把物件畫在新位置

足夠快地執行這三步,你就能得到讓玩家感覺自然的流暢移動。

以下是程式碼示範:

// 設定英雄的位置
hero.x += 5;
// 清除英雄所在的矩形區域
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重新繪製遊戲背景和英雄
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "black";
ctx.drawImage(heroImg, hero.x, hero.y);

這段程式碼做了什麼:

  • 更新英雄的 x 座標水平向右移動5像素
  • 清除整個畫布內容以移除前一幀
  • 填滿畫布背景為黑色
  • 重新繪製英雄圖像在新的位置

你能想出為什麼每秒多幀繪製英雄會耗損效能嗎?可以閱讀關於此模式的替代方案

處理鍵盤事件

這是我們將玩家輸入連結到遊戲動作的地方。當玩家按下空白鍵發射雷射、或按方向鍵閃避小行星時,遊戲需要偵測並回應這些輸入。

鍵盤事件會在視窗層級發生,意味著整個瀏覽器視窗都在聆聽按鍵。滑鼠點擊則可以綁定到特定元素(例如按鈕)。對我們的太空遊戲而言,我們將專注於鍵盤控制,讓玩家有經典街機的感覺。

這讓我想到1800年代的電報員必須將摩斯密碼翻譯成有意義訊息——我們正在做類似的工作將按鍵轉譯成遊戲指令。

要處理事件,你需要使用 window 的 addEventListener() 方法,並提供兩個輸入參數。第一個參數是事件名稱,例如 keyup。第二個參數是當事件發生時要執行的函式。

以下示範:

window.addEventListener('keyup', (evt) => {
  // evt.key = 按鍵的字串表示
  if (evt.key === 'ArrowUp') {
    // 做某事
  }
});

解析發生了什麼:

  • 監聽整個視窗的鍵盤事件
  • 取得事件物件,包含哪個鍵被按下的資訊
  • 判斷按下的鍵是否為特定鍵(這裡是向上箭頭)
  • 執行當條件滿足時的程式碼

鍵盤事件有兩個屬性能判斷按了什麼鍵:

  • key — 按鍵的字串表示,例如 'ArrowUp'
  • keyCode — 數字表示,例如 37,代表 ArrowLeft

鍵盤事件處理技術在遊戲開發外也很實用。你還想到哪些用途?

sequenceDiagram
    participant User
    participant Browser
    participant EventSystem
    participant GameLogic
    participant Hero
    
    User->>Browser: 按下 上箭頭 鍵
    Browser->>EventSystem: keydown 事件
    EventSystem->>EventSystem: preventDefault()
    EventSystem->>GameLogic: emit('KEY_EVENT_UP')
    GameLogic->>Hero: hero.y -= 5
    Hero->>Hero: 更新位置
    
    Note over Browser,GameLogic: 事件流程阻止瀏覽器預設行為
    Note over GameLogic,Hero: 發布/訂閱模式促進良好溝通

特殊按鍵小提醒!

有些按鍵有內建瀏覽器行為,會干擾你的遊戲。方向鍵會滾動頁面,空白鍵會跳頁——這並非你想要玩家駕駛飛船時出現的行為。

我們可以阻止這些預設行為,讓遊戲自己處理輸入。這類似早期程式設計師覆寫系統中斷來自訂行為——只是我們是在瀏覽器層級操作。方法如下:

const onKeyDown = function (e) {
  console.log(e.keyCode);
  switch (e.keyCode) {
    case 37:
    case 39:
    case 38:
    case 40: // 方向鍵
    case 32:
      e.preventDefault();
      break; // 空白鍵
    default:
      break; // 不要阻擋其他按鍵
  }
};

window.addEventListener('keydown', onKeyDown);

理解此阻止程式碼:

  • 判斷可能造成干擾瀏覽器行為的特定按鍵碼
  • 阻止方向鍵與空白鍵的預設動作
  • 允許其他按鍵正常運作
  • 使用 e.preventDefault() 停止瀏覽器內建行為

🔄 教學檢核點

事件處理理解:在進入自動移動前,確保你能:

  • 解釋 keydownkeyup 的差異
  • 理解為何要阻止瀏覽器預設行為
  • 描述事件監聽器如何連結使用者輸入與遊戲邏輯
  • 辨識哪些鍵可能干擾遊戲控制

快速自我測試:若不阻止方向鍵預設行為會怎樣? 答案:瀏覽器會滾動頁面,干擾遊戲移動

事件系統架構:你已知:

  • 視窗層級監聽:捕捉整個瀏覽器的事件
  • 事件物件屬性key 字串與 keyCode 數字
  • 預設行為阻止:阻擋不想要的瀏覽器反應
  • 條件邏輯:回應特定按鍵組合

遊戲自動移動

現在來談談沒有玩家輸入也會移動的物體。想像敵艦在畫面巡航、子彈筆直飛行,或雲朵在背景飄動。這種自主移動讓遊戲世界即便沒人操作也顯得活潑。

我們利用JavaScript內建定時器定期更新位置。這概念類似擺鐘運作——透過規律機構觸發恆定、規律行動。簡單範例如下

const id = setInterval(() => {
  // 在 y 軸上移動敵人
  enemy.y += 10;
}, 100);

此移動程式碼的作用:

  • 建立每100毫秒運作一次的計時器
  • 更新敵人y座標每次增加10像素
  • 儲存interval ID以便日後停止
  • 自動將敵人向畫面下方移動

遊戲循環

這是把所有東西串起來的概念—遊戲循環。如果遊戲是一部電影,遊戲循環就像投影機,一次接一次顯示畫面,使一切看起來連續平滑。

每個遊戲背後都會運作這樣的循環。這是一個函式,更新所有物件,重畫畫面,並不斷重複。它會追蹤英雄、敵人、飛舞的雷射——整個遊戲狀態。

這概念讓我想到早期動畫師像華特·迪士尼一樣,必須一幀一幀重畫角色,製造移動的錯覺。我們做的也是一樣,只是用程式碼取代畫筆。

以下是遊戲循環典型模樣,程式碼表示:

flowchart TD
    A["開始遊戲循環"] --> B["清除畫布"]
    B --> C["填充背景"]
    C --> D["更新遊戲物件"]
    D --> E["繪製英雄"]
    E --> F["繪製敵人"]
    F --> G["繪製介面元素"]
    G --> H["等待下一幀"]
    H --> I{遊戲運行中?}
    I -->|是| B
    I -->|否| J["結束遊戲"]
    
    subgraph "幀率控制"
        K["60 幀/秒 = 16.67 毫秒"]
        L["30 幀/秒 = 33.33 毫秒"]
        M["10 幀/秒 = 100 毫秒"]
    end
    
    style B fill:#ffebee
    style D fill:#e1f5fe
    style E fill:#e8f5e8
    style F fill:#e8f5e8
const gameLoopId = setInterval(() => {
  function gameLoop() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = "black";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    drawHero();
    drawEnemies();
    drawStaticObjects();
  }
  gameLoop();
}, 200);

理解遊戲循環架構:

  • 清除整個畫布移除前一幀
  • 填滿背景實色
  • 繪製所有遊戲物件於當前位置
  • 每200毫秒重複此程序打造流暢動畫
  • 控制執行頻率調控幀率

繼續太空遊戲

現在我們把前面靜態場景加上移動功能。將它從一張截圖變成互動體驗,一步步確保每個部分都連貫建構。

拿取前一課留下的程式碼(或若需重新開始,使用Part II- starter資料夾裡的程式碼)。

今天要實現的功能:

  • 英雄控制:使用方向鍵駕駛太空船在螢幕移動
  • 敵人移動:外星飛船開始前進

我們開始實作這些功能。

推薦步驟

找到 your-work 子資料夾裡已建立的檔案,應包含以下內容:

-| assets
  -| enemyShip.png
  -| player.png
-| index.html
-| app.js
-| package.json

你可以在 your-work 資料夾開始專案,執行:

cd your-work
npm start

這指令的作用:

  • 切換至專案目錄
  • 啟動HTTP伺服器位址為 http://localhost:5000
  • 提供遊戲檔案供瀏覽器測試

以上會啟動HTTP伺服器在 http://localhost:5000。打開瀏覽器輸入該位址,現在應能呈現英雄與所有敵人;但尚未開始移動!

新增程式碼

  1. 新增專屬物件heroenemygame object,它們應該有 xy 屬性。(回想繼承或組合部分)

    提示 game object 應該是有 xy 並且可以將自己繪製到畫布上的物件。

    提示:先新增一個新的 GameObject 類別,建構子如下示範,並繪製到畫布:

    class GameObject {
      constructor(x, y) {
        this.x = x;
        this.y = y;
        this.dead = false;
        this.type = "";
        this.width = 0;
        this.height = 0;
        this.img = undefined;
      }
    
      draw(ctx) {
        ctx.drawImage(this.img, this.x, this.y, this.width, this.height);
      }
    }
    

    理解此基底類別:

    • 定義所有遊戲物件共用的屬性(位置、尺寸、圖片)
    • 包含一個 dead 標誌以追蹤是否要移除
    • 提供 draw() 方法繪製物件於畫布
    • 設定預設值,供子類覆寫
classDiagram
    class GameObject {
        +x: number
        +y: number
        +dead: boolean
        +type: string
        +width: number
        +height: number
        +img: Image
        +draw(ctx)
    }
    
    class Hero {
        +speed: number
        +type: "英雄"
        +width: 98
        +height: 75
    }
    
    class Enemy {
        +type: "敵人"
        +width: 98
        +height: 50
        +setInterval()
    }
    
    GameObject <|-- Hero
    GameObject <|-- Enemy
    
    class EventEmitter {
        +listeners: object
        +on(message, listener)
        +emit(message, payload)
    }
接下來,從 `GameObject` 擴展建立 `Hero` 和 `Enemy`

```javascript
class Hero extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 98;
    this.height = 75;
    this.type = "Hero";
    this.speed = 5;
  }
}
```

```javascript
class Enemy extends GameObject {
  constructor(x, y) {
    super(x, y);
    this.width = 98;
    this.height = 50;
    this.type = "Enemy";
    const id = setInterval(() => {
      if (this.y < canvas.height - this.height) {
        this.y += 5;
      } else {
        console.log('Stopped at', this.y);
        clearInterval(id);
      }
    }, 300);
  }
}
```

**這些類別的重點:**
- **繼承**自 `GameObject` 使用 `extends` 關鍵字
- **呼叫**上層建構子 `super(x, y)`
- **設定**各類物件專屬尺寸與屬性
- **使用** `setInterval()` 實現敵人自動移動
  1. 新增鍵盤事件處理器以控制鍵盤導航(讓英雄上下左右移動)

    記得座標系為笛卡兒座標,左上為 0,0。也記得加入程式碼阻止預設行為

    提示:建立 onKeyDown 函式並綁定到 window

    const onKeyDown = function (e) {
      console.log(e.keyCode);
      // 新增上課程中的程式碼以阻止預設行為
      switch (e.keyCode) {
        case 37:
        case 39:
        case 38:
        case 40: // 方向鍵
        case 32:
          e.preventDefault();
          break; // 空白鍵
        default:
          break; // 不阻擋其他按鍵
      }
    };
    
    window.addEventListener("keydown", onKeyDown);
    

    此事件處理器做了什麼:

    • 監聽整個視窗的 keydown 事件
    • 記錄按鍵碼幫助調試
    • 阻止方向鍵與空白鍵的預設瀏覽器行為
    • 允許其他按鍵正常運作

    在此階段檢查瀏覽器控制台,可以看到按鍵記錄。

  2. 實作發布-訂閱模式,保持後續程式碼乾淨整潔。

    發布-訂閱模式幫助分離事件偵測和事件處理,使程式更模組化,易於維護。

    完成最後這部分你可以:

    1. 新增事件監聽器於 window

      window.addEventListener("keyup", (evt) => {
        if (evt.key === "ArrowUp") {
          eventEmitter.emit(Messages.KEY_EVENT_UP);
        } else if (evt.key === "ArrowDown") {
          eventEmitter.emit(Messages.KEY_EVENT_DOWN);
        } else if (evt.key === "ArrowLeft") {
          eventEmitter.emit(Messages.KEY_EVENT_LEFT);
        } else if (evt.key === "ArrowRight") {
          eventEmitter.emit(Messages.KEY_EVENT_RIGHT);
        }
      });
      

    此事件系統功能:

    • 偵測鍵盤輸入並轉為自訂遊戲事件
    • 分離輸入偵測與遊戲邏輯
    • 方便日後更改控制方式而不影響遊戲主要邏輯
    • 允許多系統回應同一輸入
flowchart TD
    A["鍵盤輸入"] --> B["視窗事件監聽器"]
    B --> C["事件發射器"]
    C --> D["按鍵鬆開事件"]
    C --> E["按鍵按下事件"]
    C --> F["按鍵左移事件"]
    C --> G["按鍵右移事件"]
    
    D --> H["英雄移動"]
    D --> I["音效系統"]
    D --> J["視覺效果"]
    
    E --> H
    F --> H
    G --> H
    
    style A fill:#e1f5fe
    style C fill:#e8f5e8
    style H fill:#fff3e0
  1. 創建 EventEmitter 類別以發布與訂閱訊息:

    class EventEmitter {
      constructor() {
        this.listeners = {};
      }
    
      on(message, listener) {
        if (!this.listeners[message]) {
          this.listeners[message] = [];
        }
        this.listeners[message].push(listener);
      }
    
    
  2. 新增常數並設定 EventEmitter

    const Messages = {
      KEY_EVENT_UP: "KEY_EVENT_UP",
      KEY_EVENT_DOWN: "KEY_EVENT_DOWN",
      KEY_EVENT_LEFT: "KEY_EVENT_LEFT",
      KEY_EVENT_RIGHT: "KEY_EVENT_RIGHT",
    };
    
    let heroImg, 
        enemyImg, 
        laserImg,
        canvas, ctx, 
        gameObjects = [], 
        hero, 
        eventEmitter = new EventEmitter();
    

理解此設定:

  • 定義訊息常數以避免錯字並方便重構
  • 宣告影像、畫布上下文與遊戲狀態變數
  • 建立發布-訂閱系統的全域事件發射器
  • 初始化 一個陣列來存放所有遊戲物件
  1. 初始化遊戲

    function initGame() {
      gameObjects = [];
      createEnemies();
      createHero();
    
      eventEmitter.on(Messages.KEY_EVENT_UP, () => {
        hero.y -= 5;
      });
    
      eventEmitter.on(Messages.KEY_EVENT_DOWN, () => {
        hero.y += 5;
      });
    
      eventEmitter.on(Messages.KEY_EVENT_LEFT, () => {
        hero.x -= 5;
      });
    
    
  2. 設置遊戲循環

    重構 window.onload 函數以初始化遊戲並以適當間隔設置遊戲循環。你還會加入一道雷射光束:

    window.onload = async () => {
      canvas = document.getElementById("canvas");
      ctx = canvas.getContext("2d");
      heroImg = await loadTexture("assets/player.png");
      enemyImg = await loadTexture("assets/enemyShip.png");
      laserImg = await loadTexture("assets/laserRed.png");
    
      initGame();
      const gameLoopId = setInterval(() => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = "black";
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        drawGameObjects(ctx);
      }, 100);
    };
    

    了解遊戲設定:

    • 等待 頁面完全載入後才開始
    • 取得 canvas 元素及其 2D 繪製上下文
    • 非同步載入 所有圖片資源,使用 await
    • 啟動 遊戲循環,間隔為 100 毫秒10 FPS
    • 清除並重繪 每一幀的整個畫面
  3. 加入程式碼 以特定間隔移動敵人

    重構 createEnemies() 函數,建立敵人並將它們推入新的 gameObjects 類別:

    function createEnemies() {
      const MONSTER_TOTAL = 5;
      const MONSTER_WIDTH = MONSTER_TOTAL * 98;
      const START_X = (canvas.width - MONSTER_WIDTH) / 2;
      const STOP_X = START_X + MONSTER_WIDTH;
    
      for (let x = START_X; x < STOP_X; x += 98) {
        for (let y = 0; y < 50 * 5; y += 50) {
          const enemy = new Enemy(x, y);
          enemy.img = enemyImg;
          gameObjects.push(enemy);
        }
      }
    }
    

    敵人創建的功能:

    • 計算 位置,使敵人置中於畫面
    • 利用巢狀迴圈 創建敵人網格
    • 將敵人圖像指派 給每個敵人物件
    • 將每個敵人加入 全域遊戲物件陣列

    並新增一個 createHero() 函數,為英雄執行類似程序。

    function createHero() {
      hero = new Hero(
        canvas.width / 2 - 45,
        canvas.height - canvas.height / 4
      );
      hero.img = heroImg;
      gameObjects.push(hero);
    }
    

    英雄創建的功能:

    • 將英雄定位 在畫面底部中央
    • 指派英雄圖像 給英雄物件
    • 將英雄加入 遊戲物件陣列以供繪製

    最後,新增一個 drawGameObjects() 函數開始繪製:

    function drawGameObjects(ctx) {
      gameObjects.forEach(go => go.draw(ctx));
    }
    

    了解繪圖函數:

    • 遍歷 陣列中所有遊戲物件
    • 呼叫 每個物件的 draw() 方法
    • 傳遞 canvas 上下文,好讓物件自行繪製

    🔄 教學回顧檢查

    完整遊戲系統理解:確認你對整體架構的掌握:

    • 繼承如何讓 Hero 和 Enemy 共享共同的 GameObject 屬性?
    • 為什麼 pub/sub 模式可以讓程式碼更易維護?
    • 遊戲循環在創造流暢動畫中扮演什麼角色?
    • 事件監聽器如何將使用者輸入連接至遊戲物件行為?

    系統整合:你的遊戲現在展示了:

    • 物件導向設計:帶有專門繼承的基底類別
    • 事件驅動架構:用 pub/sub 模式實現鬆散耦合
    • 動畫框架:遊戲循環持續刷新幀畫面
    • 輸入處理:鍵盤事件與預設行為阻止
    • 資產管理:圖片載入與精靈繪製

    專業模式:你已實作:

    • 職責分離:輸入、邏輯與繪製分開管理
    • 多型性:所有遊戲物件共享共同繪製介面
    • 訊息傳遞:元件間的清晰通訊
    • 資源管理:高效的精靈與動畫控制

    你的敵人應該開始逼近你的英雄太空船了! } }

    
    and add a `createHero()` function to do a similar process for the hero.
    
    ```javascript
    function createHero() {
      hero = new Hero(
        canvas.width / 2 - 45,
        canvas.height - canvas.height / 4
      );
      hero.img = heroImg;
      gameObjects.push(hero);
    }
    

    最後,新增一個 drawGameObjects() 函數開始繪製:

    function drawGameObjects(ctx) {
      gameObjects.forEach(go => go.draw(ctx));
    }
    

    你的敵人應該開始逼近你的英雄太空船了!


GitHub Copilot Agent 挑戰 🚀

這是一個將提升你遊戲品質的挑戰:加入邊界限制與流暢控制。目前你的英雄可以飛出螢幕,且移動感受可能不夠順暢。

你的任務: 透過實作螢幕邊界與流暢移動,使你的太空船移動更加逼真。這類似 NASA 的飛行控制系統防止太空船超過安全操作範圍。

要建置的東西: 創建系統讓英雄太空船維持留在螢幕內,且控制方式流暢連續。當玩家按住方向鍵時,太空船應該持續滑行,而非一步步跳動。考慮在太空船接近螢幕邊界時加入視覺反饋——比如輕微效果顯示遊戲區域邊緣。

在此了解更多關於 agent 模式

🚀 挑戰

隨著專案規模擴大,程式碼組織變得越來越重要。你可能已注意到檔案中函式、變數和類別混雜一起。這讓我想起負責阿波羅計畫代碼的工程師,他們必須創建清晰且可維護的系統,讓多個團隊能同時協作。

你的任務: 當作軟體架構師思考。你要怎麼組織程式碼,好讓六個月後你或隊友依然可以理解?即使現在全部放在同一檔案,也能做出更好的組織:

  • 將相關功能 用清楚註解標頭分組
  • 分離關心事項 - 保持遊戲邏輯與繪製分開
  • 使用一致命名 風格於變數與函式
  • 建立模組 或命名空間來組織遊戲不同面向
  • 添加文件說明 解釋每個主要區塊用途

反思問題:

  • 回來看時,哪些程式碼最難理解?
  • 如何組織程式碼能讓其他人更容易貢獻?
  • 若要新增像強化道具或不同敵人類型,會如何改動程式?

課後小測驗

課後小測驗

複習與自學

我們都是從零搭建起來,這很棒,有助於學習基礎,但這裡有個小秘密 — 有許多很棒的 JavaScript 框架能替你處理大量繁重工作。一旦你對我們涵蓋的基礎感覺熟悉,就值得 探索現有工具

將框架比作一套配備完善的工具箱,而不必每項工具都手工打造。它們能解決許多我們討論的程式碼組織問題,還提供你花數週時間才能開發的功能。

值得探索的方向:

  • 遊戲引擎如何組織程式碼—你會對它們聰明的架構模式驚嘆不已
  • 讓 canvas 遊戲更流暢的效能優化技巧
  • 讓程式碼更清潔且易維護的現代 JavaScript 功能
  • 管理遊戲物件及其關係的不同方法

🎯 你的遊戲動畫掌握時間表

timeline
    title 遊戲動畫與互動學習進程
    
    section 運動基礎20 分鐘)
        動畫原理: 基於影格的動畫
                 : 位置更新
                 : 座標系統
                 : 平滑運動
        
    section 事件系統25 分鐘)
        使用者輸入: 鍵盤事件處理
                 : 預設行為阻止
                 : 事件物件屬性
                 : 窗口級監聽
        
    section 遊戲架構30 分鐘)
        物件設計: 繼承模式
                 : 基底類別建立
                 : 專門化行為
                 : 多型介面
        
    section 通訊模式35 分鐘)
        發佈/訂閱實作: 事件發射器
                      : 訊息常數
                      : 鬆散耦合
                      : 系統整合
        
    section 遊戲循環精通40 分鐘)
        即時系統: 影格率控制
                 : 更新/繪製循環
                 : 狀態管理
                 : 性能優化
        
    section 進階技術45 分鐘)
        專業功能: 碰撞檢測
                 : 物理模擬
                 : 狀態機
                 : 元件系統
        
    section 遊戲引擎概念(一週)
        框架理解: 實體元件系統
                 : 場景圖
                 : 資產管線
                 : 性能分析
        
    section 製作技能(一個月)
        專業發展: 程式碼組織
                 : 團隊協作
                 : 測試策略
                 : 部署優化

🛠️ 你的遊戲開發工具包總結

完成本課後,你已精通:

  • 動畫原理:基於幀的移動與流暢過渡
  • 事件驅動程式設計:鍵盤輸入處理與完善事件管理
  • 物件導向設計:繼承階層與多型介面
  • 通訊模式:使用 pub/sub 架構維持可維護程式碼
  • 遊戲循環架構:實時更新與繪製週期
  • 輸入系統:使用者控制映射與預設行為阻止
  • 資產管理:精靈載入與高效繪製技巧

接下來五分鐘你可以做的事

  • 開啟瀏覽器主控台,嘗試 addEventListener('keydown', console.log) 觀察鍵盤事件
  • 建立一個簡單 div 元素,用方向鍵移動它
  • 嘗試用 setInterval 建立連續移動
  • 嘗試用 event.preventDefault() 阻止預設行為

🎯 本小時你可以達成的目標

  • 完成課後測驗並理解事件驅動程式
  • 建構帶有完整鍵盤控制的移動英雄太空船
  • 實作流暢的敵人移動模式
  • 加入邊界限制防止遊戲物件離開畫面
  • 創建基礎碰撞偵測系統

📅 你的一週動畫旅程

  • 完成完整太空遊戲,優化移動與互動體驗
  • 加入進階移動模式,如曲線、加速度與物理效果
  • 實作流暢過渡與緩動函式
  • 建立粒子特效與視覺反饋系統
  • 優化遊戲效能,達成流暢 60fps 播放
  • 新增行動裝置觸控操作與響應式設計

🌟 你的一個月互動開發計畫

  • 建造複雜互動應用與進階動畫系統
  • 學習動畫函式庫如 GSAP 或打造自有動畫引擎
  • 貢獻開源遊戲開發與動畫專案
  • 精通圖形密集應用的效能優化
  • 創作遊戲開發與動畫的教學內容
  • 建立展示高階互動編程技巧的作品集

實務應用範圍:你的遊戲動畫技巧直接適用於:

  • 互動網頁應用:動態儀表板與即時介面
  • 資料視覺化:動畫圖表與互動圖形
  • 教育軟體:互動模擬與學習工具
  • 行動開發:觸控遊戲與手勢控制
  • 桌面應用Electron 應用搭配流暢動畫
  • 網頁動畫CSS 與 JavaScript 動畫函式庫

獲得專業技能:你現在能夠:

  • 架構 隨複雜度擴展的事件驅動系統
  • 實作 利用數學原理打造流暢動畫
  • 除錯 使用瀏覽器開發工具調查複雜互動系統
  • 優化 適應不同裝置與瀏覽器的遊戲效能
  • 設計 採用驗證模式維護良好程式架構

掌握遊戲開發概念

  • 幀率管理:理解 FPS 與時間控制
  • 輸入處理:跨平台鍵盤及事件系統
  • 物件生命週期:建立、更新與銷毀模式
  • 狀態同步:保持遊戲狀態跨幀一致
  • 事件架構:遊戲系統間解耦式通訊

下一步:你已準備好新增碰撞偵測、計分系統、音效,或探索諸如 Phaser、Three.js 等現代遊戲框架!

🌟 成就解鎖:你已打造出具有專業架構模式的完整互動遊戲系統!

作業

註解你的程式碼


免責聲明
本文件係使用人工智慧翻譯服務 Co-op Translator 所翻譯而成。雖然我們致力於確保翻譯的準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應視為權威來源。對於重要資訊,建議採用專業人工翻譯。本公司不對因使用本翻譯所導致的任何誤解或錯誤詮釋承擔責任。