2.3 スペースインベーダーゲームを作る

(1/1)
スペースインベーダーゲームを作る
1970年代に社会現象にまでなった「スペースインベーダー」ゲームを Codex を使って再現する。本項では、前項までで学んだプログラム仕様書の書き方を応用し、シューティングゲームならではの複雑な仕様を記述する方法を学ぶ。

自機・エイリアン・防壁という3種類の「オブジェクト」の動作を定義し、どのオブジェクトがぶつかったときに何が起きるかを「衝突判定テーブル」で整理する。これをプロンプトに含めることで、Codex がゲームを正しく実装できる。
また、一度プログラムを作ったあとに「最初から作り直す」と指示する経験もする。新しい仕様(デモ画面・ハイスコア登録)を追加した再設計では、前の実装より豊かなゲームが完成した。さらに、敵弾の発射位置を少しずらす「名古屋撃ち仕様」(実際には仕様バグ)をマイナーバージョンアップとして実装し、Codexへの変更指示がいかに簡単かを体験する。

目次

ゲームの概要

スペースインベーダー画面イメージ
スペースインベーダー風ゲームの画面イメージ
スペースインベーダー」は1978年(昭和53年)に株式会社タイトーが発売したアーケードゲームだ。画面上から迫り来るエイリアンの集団を、画面下に配置された自機のレーザーで撃ち落とすシューティングゲームで、世界中でブームを巻き起こした。

ゲームの画面は固定されており(スクロールしない)、エイリアンは左右に移動しながら少しずつ画面下へ降りてくる。

自機は左右に動き、レーザーで迎撃する。エイリアンと自機の間には「防壁(トーチカ)」があり、自機や敵の弾を受け止めてくれるが、被弾するたびに削れていく。

今回はこのゲームを JavaScript だけで実装する。グラフィックはHTML5の Canvas 要素を使ってブラウザの中に直接描画する。Canvas は、プログラムで図形や画像を自由に描ける「デジタルの黒板」のようなものだ。サーバは不要で、ブラウザだけで動作する。

プログラム仕様書の構成

デモ画面
デモ画面
前項までで学んだプログラム仕様書の構成を今回も踏襲するが、シューティングゲームはオセロゲームより仕様が複雑になる。とくに重要な追加項目が「オブジェクト定義と挙動」「衝突判定(コリジョン)仕様」「シーケンス」の3つだ。

オブジェクトとは、ゲームの中に登場する部品のことだ。スペースインベーダーには、自機(プレイヤー)・エイリアン(敵)・防壁(トーチカ)・弾という4種類のオブジェクトが存在する。

それぞれのオブジェクトがどこにいて、どう動き、どんな制限があるかを仕様書に書いておくと、Codex がそれを読んで正確にプログラムを作れる。

今回のプログラム仕様書では「## 処理」の中に次の3つの節を設けた。
処理に追加した節
節の名称目的
### オブジェクト定義と挙動各オブジェクトの初期位置・動作・制限を定義する
### 衝突判定(コリジョン)仕様オブジェクト同士がぶつかったときに何が起きるかをテーブルで整理する
### シーケンス(ゲームループの1フレームの流れ)ゲームが1コマごとに行う処理の順番を定義する

オブジェクト定義

各オブジェクトの主な定義は次の通りだ。
オブジェクト定義
オブジェクト主な仕様
自機(Player)初期位置は画面下部中央。←/A・→/Dキーで左右移動。Spaceキーで弾(レーザー)発射。画面外への移動不可。弾は同時に1発のみ(連射不可)。ライフは3で、被弾すると1減少し無敵時間後に再配置される。
エイリアン(Invaders)5行×10列のグリッド状に配置。集団で左右に同期移動し、端に達したら一段下がって方向転換。残り数が減るほど移動速度が上がる。最下段のエイリアンがランダムに弾を発射する。
防壁(Bunkers)自機とエイリアンの間に複数個配置。自機の弾・敵の弾どちらが当たっても削れる耐久力を持つ。エイリアン本体が接触すると即消滅する。
仕様書でとくに重要なのが、エイリアンの「移動アルゴリズム」だ。「集団全体で同期移動する」「端に達したら一段下がって方向転換する」「残り数が減るほど速くなる」という3つの挙動を、明確な言葉で記述した。Codex はこれを読んで、ゲームの動きをそのまま実装する。

自機の弾についても「画面内に1発のみ存在できる(連射不可)」という制限を明記した。このような「やってはいけないこと」を仕様書に書いておかないと、Codex は連射できるゲームを作るかもしれない。許可すること・禁止することを両方書くのが仕様書の基本だ。

衝突判定とゲームループ

「衝突判定(あたり判定)」とは、ゲームの中でオブジェクト同士がぶつかったときに何が起きるかを定める処理だ。英語では「コリジョン検知」(Collision Detection)とも呼ぶ。今回は、すべての衝突判定を矩形(長方形)の重なりで判定する「Bounding Box」方式を採用した。

衝突の組み合わせと発生するイベントを表にまとめておくと、Codex が実装の抜け漏れなく対応できる。
衝突判定テーブル
衝突対象 A衝突対象 B発生するイベント
自機の弾エイリアンエイリアン消滅・スコア加算・自機の弾消滅
自機の弾防壁防壁の耐久力減少・自機の弾消滅
敵の弾自機自機のライフ減少・敵の弾消滅・自機リセット
敵の弾防壁防壁の耐久力減少・敵の弾消滅
自機の弾敵の弾相殺して両方消滅
自機の弾 / 敵の弾画面外オブジェクトを削除
シーケンス」とは、ゲームが1コマ(1フレーム)ごとに行う処理の順番のことだ。ゲームは通常、1秒間に数十回この処理を繰り返す。これを「ゲームループ」と呼ぶ。今回は、1フレームの流れを次のように定義した。
ゲームループのシーケンス(1フレームの流れ)
順序処理内容
入力受付プレイヤーのキー入力を検知(移動・発射)
状態更新自機位置の更新、エイリアンの移動、弾の移動
衝突判定上記テーブルの組み合わせを順次チェック
描画画面をクリアし、生存中の全オブジェクトを再描画
条件チェック勝敗が決したか判定。未決着なら①へ戻る
このように「処理の順番」を明示しておくと、Codex がゲームの動きを誤解することなく実装できる。とくに「描画は状態更新・衝突判定のあとに行う」という順序は重要で、これを守らないと画面がちらついたり、見えないオブジェクトに当たり判定が残ったりする不具合が起きる。

出力

ハイスコア表示画面
ハイスコア表示画面
画面出力は、当時を再現する意味で、単色(緑色)にした。
また、ゲーム開始前に30秒のデモ画面と10秒のハイスコア一覧画面を交互に表示するようにした。ゲーム終了後、得点を最大10件まで保存。10文字以内の英大文字でNAMEを登録できるようにした。

プログラム仕様書

以上をプログラム仕様書(プロンプト)に書くと、次のようになる。
プログラム仕様書(プロンプト)
# 目標
1970年代に一世を風靡した固定画面のシューティングゲーム「スペースインベーダー」風のゲームを作る。

# プログラム・ファイル名 SpaceInvaders.html
# プロジェクト・フォルダ作成 - プログラム・ファイル名の拡張子を除いた主ファイル名と同じ名前のサブフォルダを作成し、以降の作業はサブフォルダで行う。 - すでにサブフォルダがあれば、そのサブフォルダに移動して以降の作業を進める。
## 入力 自機の操作: ← / A キー:左移動 → / D キー:右移動 Spaceキー:弾(レーザー)の発射
## 処理 ### 勝利条件 画面上のすべてのエイリアンを全滅させる。全滅時は次ステージへ移行(敵の移動速度が上昇)。
### 敗北条件 以下のいずれかを満たした時、ゲームオーバーとなる。 1)自機の残機(ライフ)が0になる。 2)エイリアンの最下段が、侵略ライン(自機のY座標付近)に到達する。 -スコアシステム:エイリアンの種類(系統)に応じて撃破時の得点が異なる。
### オブジェクト定義と挙動 1)自機(Player) 初期位置:画面下部の中央。 制限:画面外への移動は不可。自機の弾は画面内に1発のみ存在できる(連射不可) ライフ:初期値は3。被弾すると1減少し、一定時間無敵のあと再配置。
2)エイリアン(Invaders) 配置:グリッド状(例:5行×10列)に配置されてスタート。 移動アルゴリズム: 1. 集団全体で左または右へ一定速度で同期移動する。 2. 集団の最右端または最左端のエイリアンが画面端に到達した瞬間、全体の座標を1段下げ(Y座標を加算)、移動方向を反転する。 速度変化:エイリアンの残り数が減るにつれて、移動速度(更新間隔)が段階的に速くなる。 攻撃:最下段にいるエイリアンから、ランダムな確率・間隔で下方向へ弾が発射される。
3)防壁(Bunkers / トーチカ) 配置:自機とエイリアンの間に複数個配置。 仕様:自機の弾、敵の弾どちらが当たっても削れる(耐久力を持つ)。エイリアン本体が接触した場合は即座に消滅する。
### 衝突判定(コリジョン)仕様 - すべての判定は矩形(Bounding Box)による当たり判定を行う。
| 衝突対象A | 衝突対象B | 発生するイベント | | --- | --- | --- | | 自機の弾 | エイリアン | エイリアン消滅、スコア加算、自機の弾消滅 | | 自機の弾 | 防壁 | 防壁の耐久力減少、自機の弾消滅 | | 敵の弾 | 自機 | 自機のライフ減少、敵の弾消滅、自機リセット | | 敵の弾 | 防壁 | 防壁の耐久力減少、敵の弾消滅 | | 自機の弾 | 敵の弾 | (仕様による)相殺して両方消滅、またはすり抜け | | 自機の弾 / 敵の弾 | 画面外 | 画面端(上端/下端)に出たらオブジェクトを削除 |
### シーケンス(ゲームループの1フレームの流れ) 1)入力受付:プレイヤーのキー入力を検知(移動・発射)。 2)状態更新:  自機の位置更新。  エイリアン集団の移動(タイマー依存)。  生存しているすべての弾の移動。 3)衝突判定:上記「衝突判定」を順次実行。 4)描画(レンダリング):画面をクリアし、生存している全オブジェクトを再描画。 5)条件チェック:勝敗が決したか判定。未決着なら1へ戻る。
## 通信 なし。
## 出力 - 画面は固定(スクロールなし)。 - キャラクターにはビットマップグラフィックを用いる。 - グラフィックは単色(緑色)。 - ゲーム開始前は、デモ画面を30秒間、ハイスコア一覧画面10秒間、交互に表示する。
+---------------------------------------------------+ | SCORE: 012300 LIVES: | | | | <- ステータスバー +---------------------------------------------------+ | | | M M M M M M M M M M | | A A A A A A A A A A | <- エイリアン群(5行×10列等) | B B B B B B B B B B | | | | | | --- --- --- --- | | | | | | | | | | | <- 防壁(トーチカ) | | | | | <- 自機レーザー | [A] | <- 自機(プレイヤー) +---------------------------------------------------+
## 例外・エラー処理 - 無限ループに陥ったり、システム・エラーが出たときは、画面にエラー情報を表示して終了すること。
## 記録 - ハイスコアを記録する。初期値は0。 - ゲーム終了後、ハイスコアより高い得点が出たら、あたらしいハイスコアとして登録する。 - ハイスコアは自動登録。NAMEとして、10文字までの英大文字を登録できる。 - ハイスコアは10件まで登録できる。10件を超えたら、点数の小さいものから順に削除する。
# テスト観点・合格条件 - Codexが5回プレイし、最後まで正常に動作すれば合格。テスト結果をチャットに表示する。 - すべての処理が含まれていること。 - 前提条件、制約条件が守られていること。
# 前提条件 - 仕様で分からないことがあれば、ユーザーに質問すること。 - JavaScriptを使った1本のプログラム・ファイルにすること。 - クライアントPCのブラウザ(OSやブラウザの種類は問わない)で動作すること。 - スマホでも利用できること。 - httpサーバなどやNode.jsなどサーバ技術は使わず、ブラウザの機能で完結すること。 - コーディングは「Airbnb JavaScript Style Guide」にのっとること。 - プログラムファイルにコメントとして次の情報を記載すること。 -- プログラムの名称 -- バージョン -- 目的 -- 動作環境 -- 著作権表示および使用条件 -- インストール方法 -- お問い合わせ
# 制約条件 - インターネットとのデータ送受信は行わないこと。 - 外部ライブラリを使用する場合は、下記のサイトに限定すること https://cdn.jsdelivr.net/ https://cdnjs.cloudflare.com/ https://ajax.googleapis.com/ https://code.jquery.com/ https://ajax.aspnetcdn.com/ - プログラムがMIT Licenseに違反していないこと。
# 合格判定 - テスト結果を表示し、合格かどうかをユーザーに質問する。 - 質問が正しければ、以降の処理を進める。
# 簡易取扱説明書の作成 1)HTMLファイルと同じ場所に、簡易取扱説明書のテキストファイルを作成する。ファイル名は "README.txt" にする。 2)説明書には以下の項目を含める。各々の項目は "# 項目名" と表記する。 - プログラムの名称 - バージョン - 目的 - 動作環境 - 著作権表示および使用条件 - インストール方法 - 使い方 - 変更履歴 - お問い合わせ
# 著作権表示および使用条件 このプログラムは OpenAI社の Codex によって作成し、作者が動作を確認しました。 このプログラムは外部とのデータ通信を行いません。
本アプリケーションはMIT Licenseです。 商用を含む無償利用が可能です。自由に改造できます。 再配布の際は、下記の著作権表記、およびURLと本使用条件を必ず明記してください。
Copyright by (c)studio pahoo https://www.pahoo.org/
MITライセンスについては、下記のリンク先を参考にしてください。 http://ja.wikipedia.org/wiki/MIT_License http://www.opensource.org/licenses/mit-license.php
なお,本アプリケーションの利用または改造することによって生じた得失については一切関知いたしません.また,二次利用先の組織・企業・団体の目的・内容・活動については一切関知いたしません.
# お問い合わせ ぱふぅ家のホームページ https://www.pahoo.org/ - サイト案内 - お問い合わせ
# リソース管理 1)今回の作業が、新規作成、メジャーバージョンアップ、マイナーバージョンアップ、不具合修正のいずれに当たるか、ユーザーに質問する。 2)バージョン番号を次のルールで変更し、プログラムファイル、簡易取扱説明書、このプロジェクトのプロンプト(ファイル名は PROMPT.md にする)をGitにコミットする。 -新規作成‥‥バージョン1.0.0 -メジャーバージョンアップ‥‥バージョン番号の整数部分を+1 -マイナーバージョンアップ‥‥バージョン番号の小数の1番目を+1 -不具合修正‥‥バージョン番号の小数の2番目を+1
# 配布ファイルの作成 1)プログラムファイル、簡易取扱説明書、このプロンプトを1つのZIPファイルに圧縮する。ZIPファイル名は、"プログラム主ファイル名_バージョン番号.zip" の形式にする。 2)完了後、作成したZIPファイルの保存場所を教える。

初回実装とテスト

プロンプトを入力すると、Codex はまず「SpaceInvaders」フォルダが存在するかを確認し、なければ新規作成してから作業を開始した。ゲーム本体(SpaceInvaders.html)・簡易取扱説明書(README.txt)・仕様記録(PROMPT.md)の3ファイルを一度に作成した。

実装後は、プロンプトに書いた「テスト観点・合格条件」に従って5回の自動テストを実行した。Codex はブラウザを直接操作できないため、プログラム内部に組み込んだテスト用関数を使って動作を検証した。テスト結果は次の通りだ。
初回テスト結果(v1.0.0)
確認項目結果
自機の1発制限(5回全プレイ)合格
ステージ移行(全エイリアン撃墜)合格
ゲームオーバー処理合格
JavaScript構文チェック正常
外部通信の有無なし
5回すべてのプレイで正常に動作したため、合格と判定した。ユーザーが「合格」と回答すると、Codex はバージョン番号を1.0.0に設定してGitへコミットし、配布用ZIPファイル(SpaceInvaders_1.0.0.zip)を作成した。

その後、ユーザーは「名古屋撃ち仕様」として敵弾の発射位置を4ピクセル下に変更するよう指示した。Codex はマイナーバージョンアップ(v1.1.0)として対応し、再テストにも合格した。これが最初のシリーズの完成形だ。

名古屋撃ち仕様

v1.0.0完成後、「名古屋撃ち仕様」としてマイナーバージョンアップを行った。

名古屋撃ち」とは、スペースインベーダーの上級テクニックの通称だ。敵が弾を発射する直前に自機を素早く動かし、弾の発射位置(エイリアンの真下)から外れて避けるというものだ。
実際には、「インベーダーが撃ってくる弾が自機をすり抜けて当たらない(当たり判定がない)」というバグを巧みに利用したテクニックで、名古屋のゲームセンターで広まったことから、この名が付いたとされている。

今回はこのテクニックに関係する仕様として、「敵弾の発射初期位置をエイリアンの下端より4ピクセル下に設定する」という変更を行った。
変更の指示は次のように入力した。
プロンプト:名古屋撃ち仕様
### 名古屋撃ち仕様
次のように仕様変更する
状態更新(敵弾発射):敵弾を生成する際、初期座標を エイリアンのY座標 + エイリアンの縦幅 ではなく、エイリアンのY座標 + エイリアンの縦幅 + 4ピクセル(少し下)に設定する。
Codex はプロンプトを読み、座標計算の該当箇所を修正した。修正後はバージョン番号を1.1.0に変更し、5回の回帰試験を実施した。全回で「敵弾の発生位置がエイリアン下端から正確に4ピクセル下」であることを確認し、合格と判定した。
v1.1.0 テスト結果
確認項目結果
敵弾発射位置(エイリアン下端から4px下)合格
自機弾の1発制限合格
ステージ移行合格
ゲームオーバー処理合格
ハイスコア登録合格
今回のように、既存のプログラムに対して機能を1つ変更するだけのマイナーバージョンアップは、プロンプトも短くて済み、Codex も短時間で対応できる。前項で学んだバージョン管理の仕組みが、このような細かい改訂にも有効に機能している。

参考サイト

(この項おわり)
header