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-HK/6-space-game/1-introduction/README.md

653 lines
23 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 建立太空遊戲第一部分:介紹
```mermaid
journey
title 你的遊戲開發旅程
section 基礎
學習遊戲架構: 3: Student
理解繼承: 4: Student
探索組合: 4: Student
section 溝通
建立發布/訂閱系統: 4: Student
設計事件流程: 5: Student
連接元件: 5: Student
section 應用
創建遊戲物件: 5: Student
實作設計模式: 5: Student
規劃遊戲結構: 5: Student
```
![太空遊戲動畫展示遊戲玩法](../../../../6-space-game/images/pewpew.gif)
就如同 NASA 的任務控制在太空發射時協調多個系統一樣,我們將建立一個太空遊戲,展示程式不同部分如何無縫協作。在創建一個你能實際玩到的作品的同時,你會學習到適用於任何軟件項目中的基本程式設計概念。
我們將探討組織程式碼的兩種基本方法:繼承與組合。這不僅是學術概念,這些模式正是從電子遊戲到銀行系統所採用的基礎。我們還將實現一個稱為 pub/sub 的通訊系統,類似於航天器中使用的通訊網絡,讓不同組件能分享信息而不產生依賴。
完成此系列後,你將理解如何建立可擴展且可演進的應用程式——無論是開發遊戲、網絡應用還是其他軟件系統。
```mermaid
mindmap
root((遊戲架構))
Object Organization
繼承
組合
類別層級結構
行為混合
Communication Patterns
發佈/訂閱系統
事件發射器
訊息傳遞
鬆散耦合
Game Objects
屬性 (x, y)
行為 (移動, 碰撞)
生命週期管理
狀態管理
Design Patterns
工廠函數
觀察者模式
組件系統
事件驅動架構
Scalability
模組化設計
可維護的程式碼
測試策略
性能優化
```
## 課前測驗
[課前測驗](https://ff-quizzes.netlify.app/web/quiz/29)
## 遊戲開發中的繼承與組合
隨著專案複雜度增加,程式碼組織變得至關重要。從簡單的腳本開始,如果沒有適當結構,很快會變得難以維護——就像阿波羅任務需要在數千個組件之間進行細緻協調一樣。
我們將探討兩種基本的程式碼組織方法:繼承和組合。兩者各有明顯優勢,理解兩者能幫你在不同情境下選擇最合適的方法。我們將透過太空遊戲展現這些概念,裡面有英雄、敵人、強化道具和其他物件需要有效互動。
✅ 最著名的程式設計書之一是關於[設計模式](https://en.wikipedia.org/wiki/Design_Patterns)的。
在任何遊戲中,你會有「遊戲物件」——充滿互動元素的遊戲世界。英雄、敵人、強化道具和視覺效果都是遊戲物件。它們存在於特定的螢幕座標,用 `x``y` 值表示,類似在坐標平面繪點。
儘管視覺不同,這些物件常具備基本行為:
- **它們存在某處** —— 每個物件擁有 x, y 座標,遊戲藉此知道在哪裡繪製它
- **許多會移動** —— 英雄奔跑、敵人追逐、子彈飛過螢幕
- **它們有生命週期** —— 有些持續存在,有些(如爆炸)短暫出現然後消失
- **它們會對事件作出反應** —— 碰撞發生、強化道具被收集、血條更新
✅ 想想看看 Pac-Man 遊戲。你能識別出上述四種物件類型嗎?
```mermaid
classDiagram
class GameObject {
+x: number
+y: number
+type: string
+exists_somewhere()
}
class MovableObject {
+moveTo(x, y)
+can_move_around()
}
class TemporaryObject {
+lifespan: number
+has_lifespan()
}
class InteractiveObject {
+onCollision()
+reacts_to_stuff()
}
GameObject <|-- MovableObject
GameObject <|-- TemporaryObject
GameObject <|-- InteractiveObject
MovableObject <|-- Hero
MovableObject <|-- Enemy
MovableObject <|-- Bullet
TemporaryObject <|-- PowerUp
TemporaryObject <|-- Explosion
InteractiveObject <|-- Collectible
InteractiveObject <|-- Obstacle
```
### 透過程式碼表達行為
既然你了解遊戲物件共通的行為,接下來探討如何用 JavaScript 實現這些行為。你可以透過附加於類別或物件本身的方法來表達物件行為,且有多種方法可選。
**基於類別的方法**
類別與繼承提供一個結構化方式來組織遊戲物件。就像卡爾林奈Carl Linnaeus所發展的分類系統一樣你會從包含共同屬性的基底類開始再建立專門的子類繼承這些基礎並添加專屬功能。
✅ 繼承是很重要的概念。可參考 [MDN 的繼承及原型鏈文章](https://developer.mozilla.org/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) 了解更多。
這是使用類別與繼承實作遊戲物件的方式:
```javascript
// 第一步:建立基礎的 GameObject 類別
class GameObject {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
}
}
```
**我們一步步拆解:**
- 我們建立一個基本範本,讓每個遊戲物件都可使用
- 建構函式儲存物件位置 (`x`, `y`) 和物件種類
- 這成為所有遊戲物件建立的基礎
```javascript
// 第2步透過繼承新增移動能力
class Movable extends GameObject {
constructor(x, y, type) {
super(x, y, type); // 調用父類構造函數
}
// 新增移動到新位置的能力
moveTo(x, y) {
this.x = x;
this.y = y;
}
}
```
**在上述程式碼中,我們:**
- **擴充** GameObject 類來加入移動功能
- **使用** `super()` 呼叫父類建構子以初始化繼承的屬性
- **新增** `moveTo()` 方法更新物件位置
```javascript
// 第3步創建特定遊戲物件類型
class Hero extends Movable {
constructor(x, y) {
super(x, y, 'Hero'); // 自動設定類型
}
}
class Tree extends GameObject {
constructor(x, y) {
super(x, y, 'Tree'); // 樹木不需要移動
}
}
// 第4步使用你的遊戲物件
const hero = new Hero(0, 0);
hero.moveTo(5, 5); // 英雄可以移動!
const tree = new Tree(10, 15);
// tree.moveTo() 會導致錯誤 - 樹木無法移動
```
**理解這些概念:**
- **建立** 專門類型物件並繼承適合的行為
- **展示** 繼承允許選擇性包含特性
- **表示** 英雄可以移動但樹保持靜止
- **說明** 類別階層如何防止不當操作
✅ 花幾分鐘重新想像如何用 JavaScript 撰寫如 Pac-Man 中的英雄(例如 Inky、Pinky 或 Blinky
**組合的方法**
組合遵循模組化設計哲學,類似工程師設計航天器可替換組件。不是繼承父類,而是組合具體行為來創造具所需功能的物件。這種方法提供彈性,避免僵硬的階層限制。
```javascript
// 第一步:建立基本行為物件
const gameObject = {
x: 0,
y: 0,
type: ''
};
const movable = {
moveTo(x, y) {
this.x = x;
this.y = y;
}
};
```
**這段程式碼做了什麼:**
- **定義** 一個有位置與類型屬性的基本 `gameObject`
- **建立** 分離的 `movable` 行為物件,具備移動功能
- **分離** 位置資料與移動邏輯,保持關注點分離
```javascript
// 第2步透過組合行為來組成物件
const movableObject = { ...gameObject, ...movable };
// 第3步為不同物件類型建立工廠函數
function createHero(x, y) {
return {
...movableObject,
x,
y,
type: 'Hero'
};
}
function createStatic(x, y, type) {
return {
...gameObject,
x,
y,
type
};
}
```
**在上述中,我們:**
- **結合** 基本物件屬性與移動行為,使用擴散語法
- **創建** 工廠函式回傳定製化物件
- **使** 物件建立具靈活性,無須嚴格階層結構
- **允許** 物件擁有正好需要的行為
```javascript
// 第4步創建並使用你的組合物件
const hero = createHero(10, 10);
hero.moveTo(5, 5); // 運作完美!
const tree = createStatic(0, 0, 'Tree');
// tree.moveTo() 未定義 - 沒有組合任何移動行為
```
**要點回顧:**
- **透過組合** 混合行為而非繼承來構建物件
- **提供** 比嚴格繼承階層更大彈性
- **使** 物件有精確功能
- **運用** 現代 JavaScript 擴散運算符簡潔合併物件
```
**Which Pattern Should You Choose?**
**Which Pattern Should You Choose?**
```mermaid
quadrantChart
title Code Organization Patterns
x-axis Simple --> Complex
y-axis Rigid --> Flexible
quadrant-1 Advanced Composition
quadrant-2 Hybrid Approaches
quadrant-3 Basic Inheritance
quadrant-4 Modern Composition
Class Inheritance: [0.3, 0.2]
Interface Implementation: [0.6, 0.4]
Mixin Patterns: [0.7, 0.7]
Pure Composition: [0.8, 0.9]
Factory Functions: [0.5, 0.8]
Prototype Chain: [0.4, 0.3]
```
> 💡 **專業提示**:兩種模式在現代 Javascript 開發中各有用途。類別適用明確定義的階層,組合則在需要極致彈性時表現優異。
>
**使用時機:**
- **選擇** 繼承當存在明確「是」關係(例如 Hero *是一個* 可移動物件)
- **挑選** 組合當需要「擁有」關係(例如 Hero *擁有* 移動能力)
- **考慮** 團隊偏好與專案需求
- **記得** 可以在同一專案中混用這兩種方法
### 🔄 **教學小檢核**
**物件組織理解**:在進入通訊模式前,請確保你能:
- ✅ 解釋繼承與組合的差別
- ✅ 分辨何時用類別或工廠函式
- ✅ 理解繼承中 `super()` 關鍵字的用途
- ✅ 認識每種方法對遊戲開發的好處
**快速自我測驗**:如何建立一個會移動且能飛行的 Flying Enemy
- **繼承方法**: `class FlyingEnemy extends Movable`
- **組合方法**: `{ ...movable, ...flyable, ...gameObject }`
**真實應用連結**:這些模式無處不在:
- **React 元件**: Props組合 vs 類別繼承
- **遊戲引擎**: 實體-組件系統採用組合
- **行動應用**: UI 框架常用繼承階層
## 通訊模式Pub/Sub 系統
當應用程式變複雜,管理組件間通訊變困難。發佈-訂閱pub/sub模式解決此問題原理類似無線電廣播——一個發射器可觸及多個接收器且不需知道誰在聽。
想像英雄受到傷害會發生什麼血條更新、音效播放、視覺反饋出現。Pub/sub 允許英雄廣播「受到傷害」訊息。任何需回應該事件的系統都可訂閱此訊息並對應處理,而非英雄直接連結到這些系統。
**Pub/Sub** 是「發布-訂閱」的縮寫
```mermaid
flowchart TD
A[英雄受傷] --> B[發布HERO_DAMAGED]
B --> C[事件系統]
C --> D[血量條訂閱者]
C --> E[聲音系統訂閱者]
C --> F[視覺效果訂閱者]
C --> G[成就系統訂閱者]
D --> H[更新血量顯示]
E --> I[播放受傷音效]
F --> J[顯示紅色閃爍]
G --> K[檢查生存成就]
style A fill:#ffebee
style B fill:#e1f5fe
style C fill:#e8f5e8
style H fill:#fff3e0
style I fill:#fff3e0
style J fill:#fff3e0
style K fill:#fff3e0
```
### 理解 Pub/Sub 架構
Pub/sub 模式讓應用內各部分鬆散耦合,意即它們能合作而不直接相依。這種分離使程式碼更可維護、易測試且彈性高。
**pub/sub 主要角色:**
- **訊息** — 簡單的文字標籤,如 `'PLAYER_SCORED'`,描述事件(可附加額外資訊)
- **發佈者** — 發出「有事情發生!」訊息的物件
- **訂閱者** — 宣告「我關心這事件」並在事件發生時反應的物件
- **事件系統** — 中間人,確保訊息送達相應聽眾
### 建立事件系統
讓我們建立一個簡單卻強大的事件系統,展示這些概念:
```javascript
// 第一步:建立 EventEmitter 類別
class EventEmitter {
constructor() {
this.listeners = {}; // 儲存所有事件監聽器
}
// 為特定訊息類型註冊監聽器
on(message, listener) {
if (!this.listeners[message]) {
this.listeners[message] = [];
}
this.listeners[message].push(listener);
}
// 向所有已註冊的監聽器發送訊息
emit(message, payload = null) {
if (this.listeners[message]) {
this.listeners[message].forEach(listener => {
listener(message, payload);
});
}
}
}
```
**這裡發生的事情分解:**
- **建立** 使用簡單類別的中央事件管理系統
- **以訊息類型存儲** 聽眾於物件中
- **使用 `on()` 註冊** 新的監聽器
- **用 `emit()` 廣播** 訊息給所有關心的監聽者
- **支援** 選擇性資料載荷傳遞相關資訊
### 綜合應用示例
現在看到實作吧!我們建立一個簡單移動系統,展示 pub/sub 如何清晰且靈活:
```javascript
// 步驟 1定義你的消息類型
const Messages = {
HERO_MOVE_LEFT: 'HERO_MOVE_LEFT',
HERO_MOVE_RIGHT: 'HERO_MOVE_RIGHT',
ENEMY_SPOTTED: 'ENEMY_SPOTTED'
};
// 步驟 2建立你的事件系統和遊戲物件
const eventEmitter = new EventEmitter();
const hero = createHero(0, 0);
```
**程式碼說明:**
- **定義** 常數物件避免訊息名稱錯誤
- **建立** 事件發射器實例處理所有通訊
- **初始化** 英雄物件於起始位置
```javascript
// 第3步設置事件監聽器訂閱者
eventEmitter.on(Messages.HERO_MOVE_LEFT, () => {
hero.moveTo(hero.x - 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
eventEmitter.on(Messages.HERO_MOVE_RIGHT, () => {
hero.moveTo(hero.x + 5, hero.y);
console.log(`Hero moved to position: ${hero.x}, ${hero.y}`);
});
```
**上述程式碼中,我們:**
- **註冊** 事件監聽器回應移動訊息
- **根據移動方向** 更新英雄位置
- **加入** 主控台日誌追蹤英雄位置變化
- **將** 移動邏輯與輸入處理分離
```javascript
// 第四步:將鍵盤輸入連接到事件(發布者)
window.addEventListener('keydown', (event) => {
switch(event.key) {
case 'ArrowLeft':
eventEmitter.emit(Messages.HERO_MOVE_LEFT);
break;
case 'ArrowRight':
eventEmitter.emit(Messages.HERO_MOVE_RIGHT);
break;
}
});
```
**理解這些概念:**
- **連結** 鍵盤輸入與遊戲事件,避免緊耦合
- **使** 輸入系統間接與遊戲物件通訊
- **允許** 多個系統同時回應鍵盤事件
- **方便** 更改按鍵綁定或新增輸入方式
```mermaid
sequenceDiagram
participant User
participant Keyboard
participant EventEmitter
participant Hero
participant SoundSystem
participant Camera
User->>Keyboard: 按下左方向鍵
Keyboard->>EventEmitter: emit('HERO_MOVE_LEFT')
EventEmitter->>Hero: 向左移動 5 像素
EventEmitter->>SoundSystem: 播放腳步聲
EventEmitter->>Camera: 跟隨英雄
Hero->>Hero: 更新位置
SoundSystem->>SoundSystem: 播放音效
Camera->>Camera: 調整視窗
```
> 💡 **專業提示**:這模式的美妙之處在於彈性!你可以輕鬆新增音效、螢幕震動或粒子效果,只需添加事件監聽器──不必更改現有鍵盤或移動程式碼。
>
**為何你會喜愛此模式:**
- 新增功能變得超簡單──只要收聽你想關注的事件
- 多個系統可同時反應同一事件,互不干擾
- 測試更直觀,因各部分獨立運作
- 發生錯誤時知道確切檢查位置
### Pub/Sub 為何能有效擴展
Pub/sub 模式保持架構簡潔,即使應用變得龐大。無論管理數十個敵人、動態 UI 更新或音效系統,其模式均能應付規模提升而無需架構變更。新功能可整合進現有事件系統,不影響既有功能。
> ⚠️ **常見錯誤**:不要一開始就創建太多特定訊息類型。先從廣泛類別開始,隨遊戲需求漸明逐漸細化。
>
**最佳實踐:**
- **將** 相關訊息分為合理類群
- **使用** 明確描述事件的名稱
- **保持** 訊息載荷簡潔且聚焦
- **為團隊** 製作訊息類型文件與說明
### 🔄 **教學小檢核**
**事件驅動架構理解**:確認你掌握整套系統:
- ✅ pub/sub 如何避免組件間緊耦合?
- ✅ 為何事件驅動架構更容易新增功能?
- ✅ EventEmitter 在通訊流程中扮演什麼角色?
- ✅ 訊息常數如何防止錯誤並提升可維護性?
**設計挑戰**:這些遊戲場景你會怎麼用 pub/sub 處理?
1. **敵人死亡**:更新分數、播放音效、產生強化道具、從螢幕移除
2. **關卡完成**:停止音樂、顯示 UI、保存進度、載入下一關
3. **強化道具收集**:增強能力、更新 UI、播放特效、啟動計時器
**專業連結**:此模式出現在:
- **前端框架**React/Vue 事件系統
- **後端服務**:微服務通訊
- **遊戲引擎**Unity 事件系統
- **行動開發**iOS/Android 通知系統
---
## GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
**描述:** 使用繼承和 pub/sub 模式創建一個簡單遊戲物件系統。你將實現一個基本遊戲,讓不同物件透過事件通訊,而不需彼此直接認識。
**提示:** 建立 JavaScript 遊戲系統需求如下1) 創建基底 GameObject 類,帶有 x, y 座標與類型屬性。2) 創建 Hero 類繼承自 GameObject並能移動。3) 創建 Enemy 類繼承 GameObject能追蹤英雄。4) 實作 EventEmitter 類作為 pub/sub 模式。5) 設置事件監聽器,當英雄移動時,附近敵人會收到 'HERO_MOVED' 事件,並更新位置朝英雄移動。加入 console.log 陳述顯示物件通訊。
想了解更多 [agent 模式](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode) 請點此。
## 🚀 挑戰
考慮 pub-sub 模式如何加強遊戲架構。識別哪些元件應該發出事件,以及系統應如何回應。設計一個遊戲概念並繪製其元件間的通訊模式。
## 課後小測驗
[課後小測驗](https://ff-quizzes.netlify.app/web/quiz/30)
## 複習與自學
透過[閱讀相關內容](https://docs.microsoft.com/azure/architecture/patterns/publisher-subscriber/?WT.mc_id=academic-77807-sagibbon)深入了解 Pub/Sub。
### ⚡ **你可以在接下來 5 分鐘做的事**
- [ ] 打開任何線上 HTML5 遊戲並用 DevTools 檢查其程式碼
- [ ] 創建一個簡單的 HTML5 Canvas 元素並繪製基本圖形
- [ ] 嘗試使用 `setInterval` 來製作一個簡單的動畫循環
- [ ] 探索 Canvas API 文件並嘗試繪製方法
### 🎯 **你這一小時可以完成的目標**
- [ ] 完成課後測驗並理解遊戲開發概念
- [ ] 建立遊戲專案架構HTML、CSS、JavaScript 檔案)
- [ ] 製作基本遊戲迴圈,持續更新與渲染
- [ ] 在畫布上繪製你的第一批遊戲精靈
- [ ] 實作圖片與音效的基本資源加載
### 📅 **你的週度遊戲製作計劃**
- [ ] 完成具備所有計劃功能的完整太空遊戲
- [ ] 增加精緻的圖形、音效和流暢動畫
- [ ] 實作遊戲狀態(開始畫面、遊戲進行、遊戲結束)
- [ ] 製作計分系統與玩家進度追蹤
- [ ] 使你的遊戲適應各種裝置並具備可及性
- [ ] 在線分享遊戲並從玩家取得回饋
### 🌟 **你的月度遊戲開發規劃**
- [ ] 製作多款不同類型及機制的遊戲
- [ ] 學習 Phaser 或 Three.js 等遊戲開發框架
- [ ] 參與開源遊戲開發專案
- [ ] 精通進階遊戲編程模式與優化技巧
- [ ] 建立展示你的遊戲開發作品集
- [ ] 指導有興趣的遊戲開發者與互動媒體創作者
## 🎯 你的遊戲開發精通時間表
```mermaid
timeline
title 遊戲架構學習進程
section 物件模式20分鐘
代碼組織:類別繼承
:組合模式
:工廠函數
:行為混入
section 通訊系統25分鐘
事件架構:發布/訂閱實作
:訊息設計
:事件發射器
:鬆耦合
section 遊戲物件設計30分鐘
實體系統:屬性管理
:行為組合
:狀態處理
:生命週期管理
section 架構模式35分鐘
系統設計:元件系統
:觀察者模式
:命令模式
:狀態機
section 進階概念45分鐘
可擴展架構:效能優化
:記憶體管理
:模組化設計
:測試策略
section 遊戲引擎概念(一週)
專業發展:場景圖
:資產管理
:渲染管線
:物理整合
section 框架精通(兩週)
現代遊戲開發React遊戲模式
Canvas優化
WebGL基礎
PWA遊戲
section 業界實務(一個月)
專業技能:團隊協作
:程式碼審查
:遊戲設計模式
:效能分析
```
### 🛠️ 你的遊戲架構工具組總結
完成本課程後,你已具備:
- **設計模式掌握**:理解繼承與組合的取捨
- **事件驅動架構**Pub/sub 實作以支持可擴充通訊
- **物件導向設計**:類別階層與行為組合
- **現代 JavaScript**:工廠函式、展開語法與 ES6+ 模式
- **可擴充架構**:鬆耦合與模組化設計原則
- **遊戲開發基礎**:實體系統與元件模式
- **專業模式**:業界標準的程式碼組織方法
**實際應用**:這些模式直接適用於:
- **前端框架**React/Vue 元件架構與狀態管理
- **後端服務**:微服務通訊與事件驅動系統
- **行動開發**iOS/Android 應用架構與通知系統
- **遊戲引擎**Unity、Unreal 與網頁遊戲開發
- **企業軟體**:事件源與分散式系統設計
- **API 設計**RESTful 服務與即時通訊
**專業技能提升**:你現在能:
- **設計** 使用成熟模式的可擴充軟體架構
- **實作** 處理複雜互動的事件驅動系統
- **選擇** 適合不同情境的程式碼組織策略
- **除錯** 與維護鬆耦合系統
- **溝通** 使用業界標準術語說明技術決策
**下一階段**:你已準備好將這些模式套用於真實遊戲、探索進階遊戲開發議題,或將架構概念應用於網頁應用!
🌟 **成就解鎖**:你已精通從簡單遊戲到複雜企業系統核心架構模式!
## 作業
[設計一款遊戲](assignment.md)
---
<!-- CO-OP TRANSLATOR DISCLAIMER START -->
**免責聲明**
本文件由 AI 翻譯服務 [Co-op Translator](https://github.com/Azure/co-op-translator) 所翻譯。雖然我們致力於達到準確性,但請注意自動翻譯可能包含錯誤或不準確之處。原始語言版本的文件應被視為權威來源。對於重要資訊,建議採用專業人工翻譯。我們對因使用本翻譯而引起的任何誤解或誤釋概不負責。
<!-- CO-OP TRANSLATOR DISCLAIMER END -->