12 KiB
スペースゲーム構築プロジェクト その 4: レーザーを追加して衝突を検出する
レッスン前の小テスト
このレッスンでは、JavaScript でレーザーを撃つ方法を学びます! 私たちはゲームに2つのものを追加します。
- レーザー: このレーザーはあなたのヒーローの宇宙船から垂直に上向きに撃たれます
- 衝突検出、発射する能力を実装する一環として、我々はまたいくつかの素敵なゲームのルールを追加します
- レーザーが敵に当たる: レーザーが当たると敵が破壊される
- レーザーがトップ画面に当たる: 画面上部に当たるとレーザーが破壊されます
- 敵とヒーローの衝突: 敵とヒーローがぶつかると破壊されます
- 敵が画面の下に当たる: 敵が画面下に当たると敵とヒーローが破壊されます
要するに、あなた - ヒーロー - は画面の下部に移動するために管理する前に、レーザーですべての敵をヒットする必要があります。
✅ これまでに書かれた最初のコンピュータゲームについて少し調べてみてください。その機能は何だったのでしょうか?
一緒にヒーローになりましょう!
衝突検出
どのようにして衝突を検出するのか? ゲームオブジェクトを移動する長方形と考える必要があります。なぜでしょうか? ゲームオブジェクトの描画に使われる画像は矩形です。x
、y
、width
、height
を持っています。
2 つの長方形、つまりヒーローと敵が交差すると、衝突します。そのときに何が起こるかは、ゲームのルール次第です。したがって、衝突検出を実装するためには以下のものが必要です。
-
ゲームオブジェクトの矩形表現を取得する方法。こんな感じです
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); }
オブジェクトをどうやって破壊するか
ゲーム内のオブジェクトを破壊するには、一定の間隔で発生するゲームループの中で、このアイテムをペイントしないようにゲームに知らせる必要があります。これを行うには、以下のように、何かが起こったときにゲームオブジェクトを dead としてマークする方法があります。
// 衝突が発生します
enemy.dead = true
その後、画面を再描画する前に、このように、dead オブジェクトをソートしていきます。
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) {
// レーザーを作る
this.cooldown = new Cooldown(500);
} else {
// 何もしない-まだクールダウンしていない。
}
}
}
✅ スペースゲームシリーズのレッスン1を参照して、クールダウンについて思い出してください。
何を構築するか
前回のレッスンで使用した既存のコード (クリーンアップしてリファクタリングしたはずのコード) を使用して、それを拡張します。 パート II のコードから始めるか、パートIII - スターターのコードを使用してください。
ヒント: 作業に使用するレーザーはすでにアセットフォルダにあり、コードで参照されています。
- 衝突検出を追加します、レーザーが何かに衝突するときは、次のルールを適用する必要があります
- レーザーが敵に当たる: レーザーが当たると敵が破壊される
- レーザーが画面上部に当たる: レーザーが画面の上部に当たると破壊される
- 敵とヒーローの衝突: 敵とヒーローがぶつかると破壊される
- 画面下に敵が当たる: 敵が画面下部に当たると敵とヒーローが破壊される
推奨される手順
あなたのために作成されたファイルを your-work
サブフォルダ内で探します。以下のファイルが含まれているはずです。
-| assets
-| enemyShip.png
-| player.png
-| laserRed.png
-| index.html
-| app.js
-| package.json
次のコマンドをタイピングして、あなたのプロジェクトを your_work
フォルダから開始します。
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, }; }
-
衝突をチェックするコードの追加 これは、2 つの矩形が交差するかどうかをテストする新しい関数になります
function intersectRect(r1, r2) { return !( r2.left > r1.right || r2.right < r1.left || r2.top > r1.bottom || r2.bottom < r1.top ); }
-
レーザー発射機能の追加
-
キーイベントメッセージを追加します。スペースキーはヒーローの宇宙船の真上にレーザーを作成する必要があります。Messages オブジェクトに3つの定数を追加します
KEY_EVENT_SPACE: "KEY_EVENT_SPACE", COLLISION_ENEMY_LASER: "COLLISION_ENEMY_LASER", COLLISION_ENEMY_HERO: "COLLISION_ENEMY_HERO",
-
スペースキーを処理します。スペースを扱うために、
window.addEventListener
のキーアップ関数を編集します} 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; })
-
オブジェクトを移動させ、レーザーが徐々に画面上部に移動するようにします。先ほどと同じように
GameObject
を継承した Laser クラスを新たに作成します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"); // レーザーが当たる 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); }
必ず
window.onload
のゲームループにupdateGameObjects()
を追加してください。 -
レーザーにクールダウンを実装して、それがそんなに頻繁にしか発射できないようにします
最後に、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 のタイミングイベントについての詳細はこちら。