23 KiB
建立一個太空遊戲 第二部分:在畫布上繪製主角與怪物
journey
title 你的 Canvas 繪圖旅程
section 基礎
了解 Canvas API: 3: Student
學習座標系統: 4: Student
繪製基本圖形: 4: Student
section 圖像處理
載入遊戲資源: 4: Student
處理非同步載入: 5: Student
定位精靈: 5: Student
section 遊戲渲染
建立遊戲畫面: 5: Student
建造陣型: 5: Student
優化效能: 4: Student
Canvas API 是網頁開發中最強大的功能之一,能讓你在瀏覽器中直接創建動態且互動的圖形。在本課程中,我們將把空白的 HTML <canvas> 元素變成一個充滿英雄與怪物的遊戲世界。可以把畫布想像成你的數碼繪畫板,程式碼變成了視覺呈現。
我們將延續上一堂課所學的內容,現在深入視覺層面。你將學會如何載入並顯示遊戲精靈 (sprite)、精確定位元素,並為你的太空遊戲建立視覺基礎。這將連結靜態網頁與動態互動體驗之間的差距。
課程結束時,你將擁有一個完整的遊戲場景,主角飛船正確定位,敵軍陣型準備出擊。你會理解現代遊戲如何在瀏覽器中渲染圖像,並獲得創建互動視覺體驗的技能。一起探索畫布圖形,讓你的太空遊戲活起來吧!
mindmap
root((畫布圖形))
Canvas Element
HTML5 功能
2D 上下文
坐標系統
像素控制
Drawing Operations
基本形狀
文字渲染
圖像顯示
路徑繪製
Asset Management
圖像載入
非同步操作
錯誤處理
性能
Game Rendering
精靈位置
陣型佈局
場景組合
幀更新
Visual Effects
顏色與樣式
變換
動畫
圖層
課前小測驗
什麼是 Canvas?
那這個 <canvas> 元素到底是什麼?它是 HTML5 用來在瀏覽器中建立動態圖形和動畫的解決方案。不同於靜態的圖片或影片,canvas 能讓你對螢幕上的每一個像素進行控制。這使它非常適合遊戲、數據視覺化以及互動藝術。它就像是一個可編程的繪畫表面,而 JavaScript 是你的畫筆。
預設情況下,canvas 元素看起來像是頁面上一個空白且透明的矩形。但潛力就在這裡!當你用 JavaScript 畫出形狀、載入圖片、創造動畫並讓畫面回應使用者互動時,它的力量就會顯現。這種方式類似於 1960 年代貝爾實驗室初期電腦圖形先驅,必須逐像素編寫程式來創造第一批數字動畫。
✅ 在 MDN 上閱讀更多關於 Canvas API 的介紹。
它通常會這樣被宣告,在頁面主體中:
<canvas id="myCanvas" width="200" height="100"></canvas>
這段程式碼的作用是:
- 設定
id屬性,好讓你在 JavaScript 中引用這個特定的 canvas 元素 - 定義 寬度,單位為像素,用來控制 canvas 的水平尺寸
- 建立 高度,單位為像素,用來決定 canvas 的垂直大小
繪製簡單幾何圖形
現在你知道 canvas 元素是什麼了,讓我們來探索如何真正地在它上面繪製!canvas 使用的座標系你可能在數學課有聽過,唯獨有一個關於電腦圖形的特別地方。
canvas 使用的是笛卡爾座標系,有一條 x 軸(水平)和 y 軸(垂直)來定位你畫的所有東西。但關鍵差異在於:不同於數學課上的座標系,原點 (0,0) 是從左上角開始,x 值向右增長,y 值則向下增長。這個方法源自早期電腦顯示器,電子束從上往下掃描,因此左上角是自然的起點。
quadrantChart
title 畫布座標系統
x-axis 左 --> 右
y-axis 上 --> 下
quadrant-1 第一象限
quadrant-2 第二象限
quadrant-3 第三象限
quadrant-4 第四象限
Origin Point: [0.1, 0.1]
Hero Center: [0.5, 0.8]
Enemy Formation: [0.3, 0.2]
Power-up: [0.7, 0.6]
UI Elements: [0.9, 0.1]
圖片來自 MDN
在 canvas 元素上繪圖,你會按照以下三個步驟,這是所有 canvas 圖形的基礎。練習幾次之後,你就能輕鬆習慣:
flowchart LR
A[HTML 畫布元素] --> B[取得畫布參考]
B --> C[取得 2D 上下文]
C --> D[繪圖操作]
D --> E[繪製圖形]
D --> F[繪製文字]
D --> G[繪製圖像]
D --> H[套用樣式]
E --> I[渲染到螢幕]
F --> I
G --> I
H --> I
style A fill:#e1f5fe
style C fill:#e8f5e8
style I fill:#fff3e0
- 從 DOM 取得 canvas 元素參考(就像取得其他 HTML 元素一樣)
- 取得 2D 繪圖上下文 — 這提供了所有繪圖方法
- 開始繪製! 利用上下文的內建方法來創建你的圖形
程式碼看起來像這樣:
// 第一步:獲取畫布元素
const canvas = document.getElementById("myCanvas");
// 第二步:獲取2D繪圖上下文
const ctx = canvas.getContext("2d");
// 第三步:設定填充顏色並繪製矩形
ctx.fillStyle = 'red';
ctx.fillRect(0, 0, 200, 200); // x, y, 寬度, 高度
分解步驟:
- 我們用 ID 取得 canvas 元素,並存入變數
- 取得 2D 繪圖上下文 — 這是裝滿繪圖方法的工具箱
- 告訴 canvas 使用
fillStyle屬性設成紅色 - 繪製一個從左上角 (0,0) 開始、寬高皆為 200 像素的矩形
✅ Canvas API 主要聚焦於 2D 形狀,但你也可以在網站上繪製 3D 元素;這時可能會用到 WebGL API。
你可以用 Canvas API 繪製各種東西:
- 幾何圖形,我們已經示範過如何繪製矩形,但其實可以畫更多。
- 文字,你可以使用任何字體和顏色繪製文字。
- 圖片,你可以根據圖片資源,如 .jpg 或 .png,繪製圖像。
✅ 試試看!你知道如何畫矩形,能問問自己怎麼畫一個圓嗎?可以看看 CodePen 上一些有趣的 Canvas 畫作。這裡有個特別令人印象深刻的範例。
🔄 教學檢核點
Canvas 基礎理解:在進入圖片載入前,請確保你會:
- ✅ 解釋 canvas 座標系如何與數學座標系不同
- ✅ 理解繪製 canvas 的三步驟流程
- ✅ 知道 2D 繪圖上下文提供什麼功能
- ✅ 描述 fillStyle 與 fillRect 是如何搭配使用
快速自我測試:如何在 (100, 50) 位置畫一個半徑為 25 的藍色圓形?
ctx.fillStyle = 'blue';
ctx.beginPath();
ctx.arc(100, 50, 25, 0, 2 * Math.PI);
ctx.fill();
你已經知道的 Canvas 繪圖方法:
- fillRect():繪製實心矩形
- fillStyle:設定顏色和圖案
- beginPath():開始新的繪圖路徑
- arc():繪製圓和曲線
載入與繪製圖片資源
繪製基本形狀很好入門,但大多數遊戲需要真正的圖片!精靈圖、背景和材質是遊戲視覺吸引力的關鍵。載入並顯示圖片與繪製幾何形狀的方法不同,但理解過程後相當簡單。
我們需要建立一個 Image 物件,載入圖片檔案(這是非同步過程,也就是「在背景進行」),然後一旦圖片準備好,就繪製到 canvas 上。這種方式可確保圖片正確顯示,且不會阻塞應用程式的運行。
sequenceDiagram
participant JS as JavaScript
participant Img as 圖像物件
participant Server as 檔案伺服器
participant Canvas as 畫布上下文
JS->>Img: new Image()
JS->>Img: 設定 src 屬性
Img->>Server: 請求圖像檔案
Server->>Img: 回傳圖像資料
Img->>JS: 觸發 onload 事件
JS->>Canvas: drawImage(img, x, y)
Canvas->>Canvas: 渲染到螢幕
Note over JS,Canvas: 非同步載入防止介面阻塞
基本的圖片載入
const img = new Image();
img.src = 'path/to/my/image.png';
img.onload = () => {
// 圖像已加載並準備好使用
console.log('Image loaded successfully!');
};
以下這段程式碼的運作原理:
- 建立一個全新的 Image 物件來放置精靈或材質
- 指定要載入的圖片檔案路徑
- 監聽載入事件,確保圖片準備好後才使用
一個更好的圖片載入方法
這邊示範一個更健壯的圖片載入方式,專業開發者常用,將圖片載入包裝成 Promise 函式 — 這種方式在 ES6 讓 JavaScript Promises 標準化後廣受歡迎,程式碼更清晰也能優雅地處理錯誤:
function loadAsset(path) {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = path;
img.onload = () => {
resolve(img);
};
img.onerror = () => {
reject(new Error(`Failed to load image: ${path}`));
};
});
}
// 現代用法,配合 async/await
async function initializeGame() {
try {
const heroImg = await loadAsset('hero.png');
const monsterImg = await loadAsset('monster.png');
// 圖像現已準備好使用
} catch (error) {
console.error('Failed to load game assets:', error);
}
}
我們做了什麼:
- 把整個圖片載入邏輯包裝進 Promise,方便更好的管理
- 加入錯誤處理,確保出錯時會通知
- 使用現代 async/await 語法,讓程式碼更易讀
- 用 try/catch 區塊優雅地處理載入問題
載入完成後,繪製到 canvas 實際上非常簡單:
async function renderGameScreen() {
try {
// 載入遊戲資產
const heroImg = await loadAsset('hero.png');
const monsterImg = await loadAsset('monster.png');
// 獲取畫布和上下文
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
// 將圖像繪製到指定位置
ctx.drawImage(heroImg, canvas.width / 2, canvas.height / 2);
ctx.drawImage(monsterImg, 0, 0);
} catch (error) {
console.error('Failed to render game screen:', error);
}
}
逐步說明:
- 使用 await 同步載入英雄與怪物圖片
- 取得 canvas 元素並抓取 2D 渲染上下文
- 利用簡單的座標數學把英雄圖片定位到正中間
- 把怪物圖片放在左上角,開始排列敵人陣型
- 捕捉載入或繪製過程中可能發生的錯誤
flowchart TD
A[加載資源] --> B{所有圖片已加載?}
B -->|否| C[顯示加載中]
B -->|是| D[獲取畫布上下文]
C --> B
D --> E[清除畫面]
E --> F[繪製背景]
F --> G[繪製敵人陣型]
G --> H[繪製英雄飛船]
H --> I[應用視覺效果]
I --> J[渲染畫面]
subgraph "渲染流程"
K[資源管理]
L[場景組合]
M[繪製操作]
N[畫面輸出]
end
style A fill:#e1f5fe
style J fill:#e8f5e8
style I fill:#fff3e0
現在是開始建立你的遊戲的時候了
現在我們把所有東西整合,建立你的太空遊戲的視覺基礎。你已經對 canvas 基礎和圖像載入技術有所掌握,這個實作單元會引導你創建完整的遊戲畫面,並正確地擺放精靈。
要建立什麼
你將建立一個含有 Canvas 元素的網頁。它要呈現一個寬 1024px、高 768px、背景為黑色的畫面。我們已為你準備了兩張圖片:
推薦開發步驟
在 your-work 子資料夾中你可以找到為你建立的入門檔案。你的專案結構應該包含:
your-work/
├── assets/
│ ├── enemyShip.png
│ └── player.png
├── index.html
├── app.js
└── package.json
你手上的資源有:
- 遊戲精靈 存放於
assets/資料夾,方便管理 - 主 HTML 檔案 用於設定 canvas 元素並準備環境
- JavaScript 檔案 你將在這裡撰寫所有遊戲渲染程式碼
- package.json 用於設置開發伺服器,讓你能在本機測試
打開該資料夾於 Visual Studio Code 開始開發。你需要一個本機開發環境,包含 Visual Studio Code、NPM 以及 Node.js。如果你尚未安裝 npm,可以點這裡了解如何安裝。
透過命令行導航到 your-work 資料夾,啟動你的開發伺服器:
cd your-work
npm start
這條命令能做什麼:
- 啟動 在
http://localhost:5000的本機伺服器,方便你測試遊戲 - 正確服務 你的所有檔案,確保瀏覽器可以順利載入
- 監控檔案 變動,讓你開發時更順暢
- 提供 專業的開發環境體驗
💡 注意:你的瀏覽器一開始會呈現空白頁面 — 這是正常的!隨著你添加程式碼,請刷新瀏覽器查看變動。這種反覆迭代開發的方式很像 NASA 構建阿波羅導航電腦時,先測試單一組件再整合到整體系統。
新增程式碼
在 your-work/app.js 中加入必要程式碼以完成下列任務:
-
繪製黑色背景的 canvas
💡 做法:找到
/app.js裡的 TODO 標記,增加兩行程式碼。設定ctx.fillStyle為黑色,然後用ctx.fillRect()繪製整個 canvas 大小。非常簡單! -
載入遊戲材質
💡 做法:用
await loadAsset()載入玩家與敵人圖片,用變數保存。記得沒有繪製前是看不到的! -
將英雄飛船繪製於底部中間位置
💡 做法:利用
ctx.drawImage()來定位英雄。x 座標用canvas.width / 2 - 45置中,y 座標用canvas.height - canvas.height / 4放在下方區域。 -
繪製 5×5 的敵人隊形
💡 做法:找到
createEnemies函式,設置巢狀迴圈。你需要計算間距和位置,但別擔心 — 我會帶你一步步完成!
首先,設定常數以安排敵人陣型位置:
const ENEMY_TOTAL = 5;
const ENEMY_SPACING = 98;
const FORMATION_WIDTH = ENEMY_TOTAL * ENEMY_SPACING;
const START_X = (canvas.width - FORMATION_WIDTH) / 2;
const STOP_X = START_X + FORMATION_WIDTH;
這些常數的功能分解:
- 設定每列和每行均為 5 個敵人(漂亮的 5×5 格子)
- 定義敵人間距,避免擁擠
- 計算整個陣型的寬度
- 確定起始與結束點,讓陣型看起來置中
flowchart LR
A["畫布寬度:1024px"] --> B["陣型寬度:490px"]
B --> C["起始 X:267px"]
C --> D["敵人間距:98px"]
subgraph "5x5 敵人陣型"
E["第 1 行:Y=0"]
F["第 2 行:Y=50"]
G["第 3 行:Y=100"]
H["第 4 行:Y=150"]
I["第 5 行:Y=200"]
end
subgraph "欄距"
J["第 1 欄:X=267"]
K["第 2 欄:X=365"]
L["第 3 欄:X=463"]
M["第 4 欄:X=561"]
N["第 5 欄:X=659"]
end
style A fill:#e1f5fe
style B fill:#e8f5e8
style C fill:#fff3e0
接著,創建巢狀迴圈繪製敵人陣型:
for (let x = START_X; x < STOP_X; x += ENEMY_SPACING) {
for (let y = 0; y < 50 * 5; y += 50) {
ctx.drawImage(enemyImg, x, y);
}
}
這段巢狀迴圈的作用:
- 外層迴圈從左到右移動陣型
- 內層迴圈從上到下建立列
- 在精準計算出的 x,y 座標繪製每隻敵人精靈
- 保持均勻間距,讓畫面專業且整齊
🔄 教學檢核點
遊戲渲染精通:請確認你理解整體渲染系統:
- ✅ 非同步圖片載入如何避免遊戲啟動時阻塞使用者介面?
- ✅ 為什麼用常數計算敵人陣型位置,比硬編碼更佳?
- ✅ 2D 繪圖上下文在繪圖過程中扮演什麼角色?
- ✅ 巢狀迴圈如何建立有組織的精靈陣型?
效能考量:你的遊戲現已展示:
- 有效的資源載入:採用 Promise 管理圖片
- 有條理的渲染:組織良好的繪製操作
- 精準的數學定位:計算精靈擺放位置
- 錯誤管理:優雅地處理載入失敗
視覺程式設計概念:你已學到:
- 座標系統:將數學轉換為螢幕位置
- 精靈管理:載入及顯示遊戲圖形
- 陣型演算法:用於組織佈局的數學模式
- 非同步操作:現代 JavaScript 以提供順暢的用戶體驗
結果
完成的結果應該看起來像這樣:
解決方案
請先試著自己解決,但如果卡住了,可以看看這個 solution
GitHub Copilot Agent 挑戰 🚀
使用 Agent 模式完成以下挑戰:
描述: 利用已學的 Canvas API 技術,通過加入視覺效果和互動元素來強化您的太空遊戲畫布。
提示: 建立一個名為 enhanced-canvas.html 的新檔案,裡面有一個畫布,背景顯示動畫星星,主角飛船有跳動的生命值條,敵機緩慢向下移動。加入 JavaScript 程式碼,使用隨機位置與透明度繪製閃爍星星,實作根據生命值改變顏色(綠 > 黃 > 紅)的生命值條,並讓敵機以不同速度向下動畫移動。
在這裡了解更多關於 agent mode。
🚀 挑戰
你已學會使用 2D 專注的 Canvas API 畫圖,現在來看看 WebGL API,試著繪製一個 3D 物件。
課後小測
複習與自學
想了解更多 Canvas API,請參考 Canvas API 文件。
⚡ 你可在接下來 5 分鐘完成的事
- 開啟瀏覽器主控台並以
document.createElement('canvas')建立一個畫布元素 - 嘗試用畫布上下文的
fillRect()畫一個矩形 - 使用
fillStyle屬性試驗不同顏色 - 用
arc()方法畫一個簡單的圓形
🎯 你可在這一小時完成的事
- 完成課後測驗並理解 Canvas 基礎
- 實作一個多種形狀和顏色的畫布繪圖應用
- 實現圖片載入與精靈渲染功能
- 建立簡單動畫,讓物件在畫布上移動
- 練習畫布轉換如縮放、旋轉和平移
📅 你一週的 Canvas 旅程
- 完成具備細緻圖形與精靈動畫的太空遊戲
- 精通進階畫布技術,如漸層、圖案及合成
- 使用畫布實作互動數據視覺化
- 學習優化畫布效能技巧以提升流暢度
- 製作多功能繪圖或繪畫應用
- 探索創意編碼模式及利用畫布製作生成藝術
🌟 你一個月的圖形大師路線
- 利用 Canvas 2D 與 WebGL 建置複雜視覺應用
- 學習圖形編程概念與著色器基礎
- 貢獻開源圖形庫與視覺化工具
- 精通圖形高效能優化
- 製作教學內容分享 Canvas 編程與電腦圖學
- 成為協助他人創建視覺體驗的圖形編程專家
🎯 你的 Canvas 圖形大師時間表
timeline
title Canvas API 學習進度
section Canvas Fundamentals (15 minutes)
Basic Operations: 元素參考
: 2D 上下文存取
: 坐標系統
: 簡單圖形繪製
section Drawing Techniques (20 minutes)
Graphics Primitives: 矩形與圓形
: 顏色與樣式
: 文字渲染
: 路徑操作
section Image Handling (25 minutes)
Asset Management: 圖像物件建立
: 非同步加載模式
: 錯誤處理
: 效能優化
section Game Graphics (30 minutes)
Sprite Rendering: 位置演算法
: 陣形計算
: 場景組合
: 幀渲染
section Advanced Techniques (40 minutes)
Visual Effects: 變換
: 動畫
: 分層
: 狀態管理
section Performance (35 minutes)
Optimization: 高效繪製
: 記憶體管理
: 幀率控制
: 資產快取
section Professional Skills (1 week)
Production Graphics: WebGL 整合
: Canvas 函式庫
: 遊戲引擎
: 跨平台考量
section Advanced Graphics (1 month)
Specialized Applications: 資料視覺化
: 互動藝術
: 即時特效
: 3D 圖形
🛠️ 你的 Canvas 圖形工具箱總結
完成本課後,你已具備:
- Canvas API 精通:完全理解 2D 圖形編程
- 座標數學:精確定位與佈局演算法
- 資產管理:專業圖片載入與錯誤處理
- 渲染流程:有條理的場景組合方法
- 遊戲圖形:精靈定位與陣型計算
- 非同步程式設計:現代 JavaScript 模式確保流暢效能
- 視覺程式設計:將數學概念轉換為螢幕圖形
實務應用範圍廣泛:你的 Canvas 技能可直接運用於:
- 資料視覺化:製作圖表、圖形與互動儀表板
- 遊戲開發:2D 遊戲、模擬與互動體驗
- 數位藝術:創意編碼與生成藝術專案
- 介面設計:自訂圖形與互動元素
- 教育軟體:視覺學習工具與模擬器
- 網頁應用:動態圖形與即時視覺化
職場技能提升:你現在能:
- 打造 無需依賴外部函式庫的自訂圖形解決方案
- 優化 渲染效能,打造順暢的用戶體驗
- 除錯 使用瀏覽器開發工具解決複雜視覺問題
- 設計 使用數學原理的可擴展圖形系統
- 整合 Canvas 圖形至現代網頁應用架構
你已精通的 Canvas API 方法:
- 元素管理:getElementById、getContext
- 繪圖操作:fillRect、drawImage、fillStyle
- 資產載入:Image 物件、Promise 模式
- 數學定位:座標計算、陣型演算法
下一階段:你已準備好加入動畫、用戶互動、碰撞偵測,或探索 WebGL 進行 3D 圖形!
🌟 成就解鎖:你已使用基礎 Canvas API 技術製作完整的遊戲渲染系統!
作業
免責聲明:
本文件由 AI 翻譯服務 Co-op Translator 進行翻譯。雖然我們力求準確,但請注意自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵資訊,建議採用專業人工翻譯。我們不對因使用此翻譯而引起的任何誤解或誤釋承擔責任。



