10 KiB
建造太空遊戲第四部分:添加雷射並檢測碰撞
課前測驗
在這節課中,你將學習如何使用 JavaScript 發射雷射!我們將為遊戲添加以下兩個功能:
- 雷射:雷射從英雄的飛船垂直向上發射
- 碰撞檢測:作為實現射擊功能的一部分,我們還會添加一些有趣的遊戲規則:
- 雷射擊中敵人:雷射擊中敵人時,敵人會被消滅
- 雷射擊中屏幕頂部:雷射擊中屏幕頂部時會被銷毀
- 敵人與英雄碰撞:敵人與英雄相撞時,雙方都會被銷毀
- 敵人到達屏幕底部:敵人到達屏幕底部時,敵人和英雄都會被銷毀
簡而言之,你——英雄——需要在敵人到達屏幕底部之前,用雷射擊中所有敵人。
✅ 做一些研究,了解第一個被編寫的電腦遊戲。它的功能是什麼?
讓我們一起成為英雄吧!
碰撞檢測
我們如何進行碰撞檢測?需要將遊戲中的物件視為移動的矩形。你可能會問為什麼?因為用來繪製遊戲物件的圖像是一個矩形:它有 x
、y
、寬度
和 高度
。
如果兩個矩形(例如英雄和敵人)相交,就發生了碰撞。碰撞後應該發生什麼取決於遊戲規則。要實現碰撞檢測,你需要以下內容:
-
一種獲取遊戲物件矩形表示的方法,例如:
rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width } }
-
一個比較函數,該函數可以像這樣:
function intersectRect(r1, r2) { return !(r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top); }
我們如何銷毀物件
在遊戲中銷毀物件需要讓遊戲知道在某個時間間隔觸發的遊戲循環中不再繪製該物件。一種方法是當某些事情發生時,將遊戲物件標記為死亡,例如:
// collision happened
enemy.dead = true
然後在重新繪製屏幕之前,篩選出死亡的物件,例如:
gameObjects = gameObject.filter(go => !go.dead);
我們如何發射雷射
發射雷射意味著響應按鍵事件並創建一個向某個方向移動的物件。因此,我們需要執行以下步驟:
- 創建雷射物件:從英雄飛船的頂部開始,創建後立即向屏幕頂部移動。
- 綁定按鍵事件:選擇鍵盤上的某個按鍵來代表玩家發射雷射。
- 創建看起來像雷射的遊戲物件:當按鍵被按下時。
雷射的冷卻時間
雷射需要在每次按下按鍵(例如空格鍵)時發射。為了防止短時間內生成過多雷射,我們需要解決這個問題。解決方法是實現所謂的冷卻時間,即一個計時器,確保雷射只能以一定頻率發射。你可以這樣實現:
class Cooldown {
constructor(time) {
this.cool = false;
setTimeout(() => {
this.cool = true;
}, time)
}
}
class Weapon {
constructor {
}
fire() {
if (!this.cooldown || this.cooldown.cool) {
// produce a laser
this.cooldown = new Cooldown(500);
} else {
// do nothing - it hasn't cooled down yet.
}
}
}
✅ 回顧太空遊戲系列的第一課,重新了解冷卻時間。
要建造什麼
你將使用上一課的現有代碼(應該已清理並重構),並進行擴展。可以從第二部分的代碼開始,或者使用 第三部分的起始代碼。
提示:你要使用的雷射已經在資產文件夾中,並且已被代碼引用。
- 添加碰撞檢測:當雷射與某物件碰撞時,應遵循以下規則:
- 雷射擊中敵人:雷射擊中敵人時,敵人會被消滅
- 雷射擊中屏幕頂部:雷射擊中屏幕頂部時會被銷毀
- 敵人與英雄碰撞:敵人與英雄相撞時,雙方都會被銷毀
- 敵人到達屏幕底部:敵人到達屏幕底部時,敵人和英雄都會被銷毀
建議步驟
找到在 your-work
子文件夾中為你創建的文件。它應包含以下內容:
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
你可以通過輸入以下命令啟動你的項目:
cd your-work
npm start
上述命令會在地址 http://localhost:5000
上啟動 HTTP 伺服器。打開瀏覽器並輸入該地址,目前應該只渲染英雄和所有敵人,還沒有任何移動。
添加代碼
-
設置遊戲物件的矩形表示以處理碰撞 以下代碼允許你獲取
GameObject
的矩形表示。編輯你的 GameObject 類以擴展它:rectFromGameObject() { return { top: this.y, left: this.x, bottom: this.y + this.height, right: this.x + this.width, }; }
-
添加檢測碰撞的代碼 這將是一個新函數,用於測試兩個矩形是否相交:
function intersectRect(r1, r2) { return !( r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top ); }
-
添加雷射發射功能
-
添加按鍵事件消息。空格鍵應在英雄飛船上方創建雷射。在 Messages 物件中添加三個常量:
KEY_EVENT_SPACE: "KEY_EVENT_SPACE", COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER", COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
-
處理空格鍵。編輯
window.addEventListener
的 keyup 函數以處理空格鍵:} else if(evt.keyCode === 32) { eventEmitter.emit(Messages.KEY_EVENT_SPACE); }
-
添加監聽器。編輯
initGame()
函數以確保英雄在按下空格鍵時可以發射雷射:eventEmitter.on(Messages.KEY_EVENT_SPACE, () => { if (hero.canFire()) { hero.fire(); }
並添加新的
eventEmitter.on()
函數以確保當敵人與雷射碰撞時的行為:eventEmitter.on(Messages.COLLISION_ENEMY_LASER, (_, { first, second }) => { first.dead = true; second.dead = true; })
-
移動物件,確保雷射逐漸移動到屏幕頂部。你將創建一個新的 Laser 類,該類擴展了
GameObject
,如之前所做:class Laser extends GameObject { constructor(x, y) { super(x,y); (this.width = 9), (this.height = 33); this.type = 'Laser'; this.img = laserImg; let id = setInterval(() => { if (this.y > 0) { this.y -= 15; } else { this.dead = true; clearInterval(id); } }, 100) } }
-
處理碰撞,實現雷射的碰撞規則。添加一個
updateGameObjects()
函數,用於測試碰撞物件是否命中:function updateGameObjects() { const enemies = gameObjects.filter(go => go.type === 'Enemy'); const lasers = gameObjects.filter((go) => go.type === "Laser"); // laser hit something lasers.forEach((l) => { enemies.forEach((m) => { if (intersectRect(l.rectFromGameObject(), m.rectFromGameObject())) { eventEmitter.emit(Messages.COLLISION_ENEMY_LASER, { first: l, second: m, }); } }); }); gameObjects = gameObjects.filter(go => !go.dead); }
確保將
updateGameObjects()
添加到window.onload
的遊戲循環中。 -
實現雷射的冷卻時間,確保雷射只能以一定頻率發射。
最後,編輯 Hero 類以實現冷卻功能:
class Hero extends GameObject { constructor(x, y) { super(x, y); (this.width = 99), (this.height = 75); this.type = "Hero"; this.speed = { x: 0, y: 0 }; this.cooldown = 0; } fire() { gameObjects.push(new Laser(this.x + 45, this.y - 10)); this.cooldown = 500; let id = setInterval(() => { if (this.cooldown > 0) { this.cooldown -= 100; } else { clearInterval(id); } }, 200); } canFire() { return this.cooldown === 0; } }
-
到這裡,你的遊戲已經具備了一些功能!你可以使用方向鍵移動,用空格鍵發射雷射,並且當雷射擊中敵人時,敵人會消失。做得好!
🚀 挑戰
添加爆炸效果!查看 太空藝術資源庫 中的遊戲資產,嘗試在雷射擊中外星人時添加爆炸效果。
課後測驗
回顧與自學
嘗試調整遊戲中的時間間隔。當你改變它們時會發生什麼?閱讀更多關於 JavaScript 計時事件 的內容。
作業
免責聲明:
本文件已使用 AI 翻譯服務 Co-op Translator 進行翻譯。儘管我們努力確保翻譯的準確性,但請注意,自動翻譯可能包含錯誤或不準確之處。原始文件的母語版本應被視為權威來源。對於關鍵信息,建議使用專業人工翻譯。我們對因使用此翻譯而引起的任何誤解或錯誤解釋不承擔責任。