AI生成コンテンツ / AI-generated content
「4.6 クイズゲームを作る」では、文章中心のデータと正誤判定を扱った。本項では、自機を上下左右に動かして空中敵と地上敵を撃ち分ける、「ゼビウス」風の縦スクロールシューティング「Xevios」を作る。空中敵にはザッパー、地上敵にはブラスターという二刀流の武器を使い分ける点が最大の特徴である。
あわせて、効果音とBGMをすべてオシレーターとノイズから作り出すWeb Audio API、背景を継ぎ目なく流し続ける無限スクロール、斜め移動の速度補正、照準地点を狙う爆撃、自機を追う誘導弾のベクトル計算、被弾時の画面シェイクや無敵時間の点滅表示、CSS変数で長さの変わるランキング演出という、これまでの記事で扱っていない要素技術を1つずつ取り上げる。
目次
- Xeviosの概要
- ゲーム・ルール
- 操作方法
- 自機ソルバルウ
- 二刀流の武器:ザッパーとブラスター
- 空中敵キャラクター
- 地上敵・地上物
- 特殊キャラクター・隠しオブジェクト
- 危険度ランキングと簡略化の判断
- ゲーム全体の流れ
- 本項で新しく取り上げる要素技術
- 新しい要素技術1:オシレーターによる効果音生成
- 新しい要素技術2:ノイズとBiquadFilterNodeによる爆発音
- 新しい要素技術3:自動再生制限とAudioContext.resume()
- 新しい要素技術4:配列とsetIntervalによる簡易BGMシーケンサー
- 新しい要素技術5:オフスクリーンCanvasによる背景の事前描画
- 新しい要素技術6:剰余演算と2枚描画による無限スクロール
- 新しい要素技術7:斜め移動の速度補正
- 新しい要素技術8:照準地点への投下型攻撃と範囲ダメージ
- 新しい要素技術9:自機を狙う誘導弾のベクトル計算
- 新しい要素技術10:画面シェイクとフラッシュによる被弾演出
- 新しい要素技術11:無敵時間の点滅表示
- 新しい要素技術12:CSS変数で作る可変長のランキング演出
- 仕様と実装を照合する
- Xeviosのプログラム仕様書
- 参考サイト
Xeviosの概要
Xeviosのゲーム画面(ステージ1)
本項で作る「Xevios」は、1983年にナムコが発売したアーケードゲーム「ゼビウス」を手本にした、縦スクロールシューティングである。自機「SOLVARO」を画面内で上下左右へ動かし、空を飛ぶ敵と地上に置かれた敵を撃ち落とす。背景は自動的に下方向へ流れ続け、プレイヤーは上空を前進しているように感じる。

ゼビウスの大きな特徴は、空中の敵と地上の敵で有効な武器が異なる点である。空中敵にはザッパー、地上敵にはブラスターを使う。どちらか一方だけを連射していても、もう一方の敵にはまったく歯が立たない。本項の Xevios もこの二刀流の仕組みをそのまま採用し、キャラクターや背景のデザイン、効果音やBGMの生成はすべてCodexに任せて作った。

キャラクターの設定は、ChatGPTにゼビウスの資料を調べてもらい、自機ソルバルウと敵キャラクターの一覧表としてまとめた。本項の後半では、この資料をもとに、Xevios で実際にどのキャラクターを採用し、どこを簡略化したかを見比べていく。
ゼビウスの大きな特徴は、空中の敵と地上の敵で有効な武器が異なる点である。空中敵にはザッパー、地上敵にはブラスターを使う。どちらか一方だけを連射していても、もう一方の敵にはまったく歯が立たない。本項の Xevios もこの二刀流の仕組みをそのまま採用し、キャラクターや背景のデザイン、効果音やBGMの生成はすべてCodexに任せて作った。
キャラクターの設定は、ChatGPTにゼビウスの資料を調べてもらい、自機ソルバルウと敵キャラクターの一覧表としてまとめた。本項の後半では、この資料をもとに、Xevios で実際にどのキャラクターを採用し、どこを簡略化したかを見比べていく。
ゲーム・ルール
| 項目 | 内容 |
|---|---|
| 移動 | 画面内を上下左右および斜め方向へ移動する。画面外へは出られない |
| 対空攻撃 | ザッパーで空中敵を撃つ。地上敵には効果がない |
| 対地攻撃 | ブラスターで照準地点を爆撃する。空中敵には効果がない |
| 残機 | 初期3機。敵・敵弾・地上物との接触で1機失う |
| 復活 | 画面下部中央付近へ復活し、3.2秒間の無敵時間を得る(点滅表示) |
| ステージ | スコアが6,000点増えるごとに1段階進み、敵の出現間隔が短くなる |
| 終了 | 残機が0になるとゲーム終了。スコアをランキングへ記録する |
| 保存 | 最高得点と上位10件のランキングをlocalStorageへ保存する |
バキュラという敵だけは例外で、ザッパーで撃っても弾き返されるだけで破壊できない。回避に専念する障害物として配置している。ソルという地上物は1回目のブラスターで地上へ姿を現し、2回目でようやく破壊できる、2段階の仕掛けになっている。
操作方法
| 操作 | キーボード | 画面ボタン |
|---|---|---|
| 上へ移動 | ↑ または W | ▲ |
| 下へ移動 | ↓ または S | ▼ |
| 左へ移動 | ← または A | ◀ |
| 右へ移動 | → または D | ▶ |
| ザッパー | Z または Space | ZAPPER |
| ブラスター | X または Enter | BLASTER |
| 一時停止・再開 | P | なし |
キーボードは矢印キーとWASDの両方を受け付け、KeyboardEvent.codeとKeyboardEvent.keyの両方で判定する。これは、キーボードの配列や入力方式(IME)によって、どちらか一方しか正しく取れない場合があるための保険である。使えないキーを押すと「矢印/WASD、Z、X、Space、Enter、Pを使用してください」と画面へ表示し、利用者が原因を理解できるようにしている。ウィンドウがフォーカスを失った場合は、押しっぱなしのキー状態をすべて解除し、フォーカスが外れている間に自機が動き続ける不具合を防ぐ。
自機ソルバルウ
| 名称 | 分類 | 移動 | 武器 | 攻撃対象 | 特徴 |
|---|---|---|---|---|---|
| ソルバルウ(SOLVALOU) | プレイヤー機/戦闘機 | 画面内を上下左右・斜めを含む8方向に移動。ステージは自動的に上方向へスクロールする | ザッパー:前方へ発射する対空兵器 ブラスター:照準位置へ投下する対地兵器 | ザッパー:空中敵 ブラスター:地上物・地上敵 | 空中と地上を別々の武器で攻撃する二重攻撃システムが最大の特徴。ブラスターは画面前方の照準地点を狙うため、移動する地上敵には進行方向を読んだ爆撃が必要 |
Xevios の自機は、この資料をほぼそのまま踏襲している。8方向へ移動できる点、ザッパーとブラスターの二刀流である点は原作と同じである。ただし、原作では画面がステージ全体を上方向へ進むのに対し、本項では自機は画面内に留まり、背景と地上物のほうが下方向へ流れる実装にした。プレイヤーから見た体感は同じだが、背景を動かすほうが実装がやさしいという理由からである。

仕様では、自機の当たり判定を見た目より少し小さくし、敵弾を避けやすくしている。Xevios.html では自機の絵は半径20ピクセル前後で描いているが、当たり判定の半径 player.r は10ピクセルに抑えている。見た目と判定を分けることで、プレイヤーが「かすった」と感じても実際は当たらない、遊びやすさを重視した調整ができる。
仕様では、自機の当たり判定を見た目より少し小さくし、敵弾を避けやすくしている。Xevios.html では自機の絵は半径20ピクセル前後で描いているが、当たり判定の半径 player.r は10ピクセルに抑えている。見た目と判定を分けることで、プレイヤーが「かすった」と感じても実際は当たらない、遊びやすさを重視した調整ができる。
二刀流の武器:ザッパーとブラスター
| 武器 | 対象 | 発射方法 | Xeviosでの実装 |
|---|---|---|---|
| ザッパー | 空中敵 | 自機前方へ直線的に連射する | 自機の左右から2本の弾を同時に発射し、上方向へ直進させる |
| ブラスター | 地上敵・地上物 | 照準地点へ投下し、着弾後に爆発する | 常時表示する照準カーソルへ向けて弾を送り、到達後に円形の爆風で範囲攻撃する |
ザッパーが「狙った方向へまっすぐ飛ばして当てる」武器であるのに対し、ブラスターは「未来の着弾地点を予測して置く」武器である。ブラスターの照準は自機の少し先の固定位置に表示され、発射すると照準地点まで弾がゆっくり移動し、到達すると爆発する。移動する地上敵(ドモグラム)を倒すには、今どこにいるかではなく、爆発が起きる瞬間にどこへ来ているかを読む必要がある。この駆け引きこそが、ゼビウス系シューティングの核心である。
空中敵キャラクター
| キャラクター | 得点 | 初出の目安 | 出現頻度 | 動き方 | 攻撃 | 破壊方法 | 危険度 |
|---|---|---|---|---|---|---|---|
| トーロイド | 30 | AREA 1 | 非常に多い | 上から編隊飛行。自機と縦軸が合うと横へ旋回して逃げる | 序盤型はなし。上位型はスパリオ | ザッパー1発 | ★ |
| タルケン | 50 | AREA 1 | 多い | 正面から接近し、攻撃後に反転して上へ退却 | 自機狙いスパリオ | ザッパー1発 | ★★ |
| ギド・スパリオ | 10 | 可変・中盤から | 普通 | 上方から超高速で直進 | 自身が高速エネルギー弾 | ザッパーで相殺 | ★★ |
| ゾシー | 70 / 100 | 可変・AREA 4前後 | 多い | 不規則な方向転換。後半型は積極的に追尾 | スパリオ | ザッパー | ★★★ |
| ジアラ | 150 | AREA 3前後 | 多い | 高速。縦軸が合うと横へ高速離脱 | 上位型はスパリオ | ザッパー | ★★★ |
| ザカート | 100 / 150 / 200 / 300 | 可変・AREA 4~6前後 | 多い | テレポート出現後、短時間移動して消滅 | タイプにより条件の異なるスパリオ | ザッパー | ★★★ |
| カピ | 300 | 可変・後半 | やや少ない | 高速接近して攻撃後、反転離脱 | 連続スパリオ | ザッパー | ★★★★ |
| テラジ | 700 | 可変・AREA 12前後 | 少ない | カピ系の最上位。非常に高速 | 高密度スパリオ | ザッパー | ★★★★★ |
| ブラグ・ザカート | 600 / 1,500 | 可変・後半 | 少ない | テレポート出現 | 自機方向を中心に多方向弾 | ザッパー | ★★★★★ |
| ガル・ザカート | 1,000 | AREA 9など | 少ない | 上方から出現。テレポートしない | 16方向弾+ブラグスパリオ4発 | ザッパー。発射前撃破推奨 | ★★★★★+ |
| ブラグ・スパリオ | 命中1回 500 | AREA 9 | 特殊 | 自機を長時間追尾 | 自身が誘導弾 | 破壊不能 | ★★★★★ |
| バキュラ | なし | AREA 3 | 特定地点 | 回転しながら上から下へ直進 | 射撃なし。接触のみ | 完全破壊不能 | ★★★ |
| アンドアジェネシス | コア 4,010~4,800 / アルゴ各1,000 | AREA 4 | AREA 4・9・14 | 巨大要塞としてスクロールに合わせて移動 | 4門のアルゴからスパリオ | ブラスターでコアまたはアルゴ | ★★★★★ |
原作の空中敵は、テレポートや長時間追尾、多方向弾など、初心者向けの解説記事で再現するには複雑すぎる挙動を数多く持つ。そこで Xevios では、序盤に登場する4種類、トーロイド、タルケン、ゾシー、バキュラだけを採用した。ジアラやザカート、アンドアジェネシスのような中盤以降の強敵は今回省略している。この判断の理由は、後述の「危険度ランキングと簡略化の判断」でまとめて説明する。
地上敵・地上物
| キャラクター | 得点 | 初出の目安 | 出現頻度 | 動き方 | 攻撃 | 破壊方法 | 危険度 |
|---|---|---|---|---|---|---|---|
| バーラ | 100 | AREA 1 | 非常に多い | 固定 | なし | ブラスター | ☆ |
| ガルバーラ | 300 | AREA 3前後 | 少ない | 固定 | なし | ブラスター | ☆ |
| ゾルバク | 200 | 序盤 | 普通 | 固定 | なし | ブラスター | ☆ |
| ログラム | 300 | AREA 1 | 非常に多い | 固定 | 自機狙いスパリオ | ブラスター | ★★ |
| ドモグラム | 800 | AREA 2 | 多い | 道路などを移動 | 移動しながらスパリオ | 進路を読んでブラスター | ★★★ |
| ボザログラム | 300~2,600 | AREA 1終盤 | 少ない | 中央+周囲4基の固定砲台 | 周囲4砲台が射撃 | ブラスター | ★★★ |
| グロブダー | 200~10,000 | AREA 1 | 多い | 型により停止・前進・後退・高速回避 | なし | ブラスター | ★~★★★★★ |
| デロータ | 1,000 | AREA 2終盤 | 普通 | 固定 | スパリオ連射 | ブラスター | ★★★★ |
| ガルデロータ | 2,000 | AREA 3終盤 | やや少ない | 固定 | 高速・高頻度の連射 | ブラスター | ★★★★★ |
地上敵も同様に、ログラムとドモグラムの2種類だけを採用した。どちらも「固定砲台」と「移動する砲台」という対照的な性質を持ち、ブラスターの2つの使い方(狙い撃ちと進路読み)を1本のゲームで両方学べる組み合わせである。加えて、地中に隠れた「ソル」も実装した。1回目のブラスターで姿を現し、2回目で破壊するという2段階の仕掛けは、原作でも人気の高い隠し要素である。
特殊キャラクター・隠しオブジェクト
| キャラクター | 得点 | 初出 | 性質 | 攻撃 | 破壊・取得 | 危険度 |
|---|---|---|---|---|---|---|
| ブラグザ | なし | AREA 4 | アンドアジェネシスのコアから脱出して上方へ飛ぶ | なし | 破壊不可 | ☆ |
| シオナイト | なし | AREA 9 | 2個が飛来し合体して飛び去る友軍的存在 | なし | 破壊不可 | ☆ |
| ソル | 出現2,000+破壊2,000 | AREA 1 | 地中に隠れた塔 | なし | ブラスターで出現→再爆撃で破壊 | ☆ |
| スペシャルフラッグ | 出現1,000+残機または10,000 | AREA 1 | 隠しボーナス | なし | ブラスターで出現後、接触取得 | ☆ |
ブラグザやシオナイト、スペシャルフラッグのような隠し要素は、今回の Xevios には実装していない。ソルの「隠れている→1回目の攻撃で姿を現す→2回目の攻撃で破壊する」という段階だけを取り入れ、それ以外の特殊要素は将来の改良課題として残した。「4.12 Codexにゲームを改良させる」で機能を追加する際の題材にするとよい。
危険度ランキングと簡略化の判断
| 順位 | 敵 | 危険度 | 理由 |
|---|---|---|---|
| 1 | ガルザカート | ★★★★★+ | 16方向弾+追尾弾4発 |
| 2 | ブラグザカート | ★★★★★ | 多方向弾。複数出現時が特に危険 |
| 3 | テラジ | ★★★★★ | 高速+高密度射撃 |
| 4 | ガルデロータ | ★★★★★ | 撃ち漏らすと後方からも激しく射撃 |
| 5 | ブラグスパリオ | ★★★★★ | 破壊不能の追尾弾 |
| 6 | カピ | ★★★★ | 高速接近+連射 |
| 7 | デロータ | ★★★★ | 複数配置時の弾幕が危険 |
| 8 | ゾシー | ★★★ | 不規則で予測しづらい |
| 9 | ジアラ | ★★★ | 高速離脱+射撃 |
| 10 | ドモグラム | ★★★ | 移動するため爆撃しにくい |
| 11 | ザカート | ★★★ | 出現位置とタイミングが読みづらい |
| 12 | バキュラ | ★★★ | 攻撃しないが破壊不能の障害物 |
| 13 | タルケン | ★★ | 比較的素直な狙い撃ち |
| 14 | ログラム | ★★ | 単体なら対処しやすい |
| 15 | トーロイド | ★ | 基本的な空中敵 |
危険度ランキングを見ると、Xevios が採用した4種類(トーロイド、タルケン、ゾシー、バキュラ)は、いずれも順位表の下半分に集まっている。原作は全体で22~26種類のキャラクターが登場するが、上位に並ぶ強敵の多くは、テレポート、多方向弾、長時間追尾といった、プレイヤーの動きを先読みする複雑な処理を必要とする。初心者向けに要素技術を1つずつ学ぶ本連載の目的からすると、まず「まっすぐ来る敵」「反転して逃げる敵」「不規則に動く敵」「破壊できない障害物」という4つの動きの型を理解するほうが優先度が高い。ザカートやアンドアジェネシスのような要塞級の敵は、この4つの型を組み合わせて拡張する形で、後日の改良課題にするのがよい。
ゲーム全体の流れ
| 状態 | 主な処理 |
|---|---|
| タイトル | MISSION STARTボタンを待つ。押されたらAudioContextを準備してゲームを初期化する |
| プレイ中 | 自機の移動、ザッパー・ブラスターの発射、敵と地上物の生成・移動・当たり判定、背景スクロールを毎フレーム更新する |
| 一時停止 | Pキーで更新を止める。BGMは新しい音を鳴らさず、画面へ「PAUSE」を表示する |
| 被弾 | 残機を1減らし、画面シェイクとフラッシュを発生させ、復活後は3.2秒間の無敵点滅にする |
| ゲーム終了 | 残機が0になった時点でBGMを止め、スコアを記録し、ランキングを表示する |
| リトライ | RETRYボタンまたはEnterキーで状態を初期化し、タイトルと同じ手順から再開する |
| システムエラー | キー入力を解除しBGMを止め、エラー専用画面へ内容を表示して安全に停止する |
プレイ中の状態は、変数stateへ 'title'、'play'、'pause'、'gameover'、'error' の文字列で保持する。「4.4 落ちものブロックゲーム」や「4.5 神経衰弱ゲーム」で使った状態遷移(有限状態機械)と同じ考え方であり、update()やrender()は、いまの状態が何かをif文で確認してから処理を行う。ゲームループ自体はrequestAnimationFrame()で毎フレーム呼び出し、前回描画からの経過時間(デルタタイム)をもとに移動量を計算する点も、これまでのゲームと共通している。

通常のイベント処理はtry-catchで囲み、さらにwindowのerrorとunhandledrejectionを受け取る。想定外の例外が起きたらsafeStop()がキー入力をすべて解除し、BGMを止め、エラー専用画面へ内容を表示する。処理を続けてスコアやランキングを壊すより、原因を示して再読み込みを求めるほうが安全である。
通常のイベント処理はtry-catchで囲み、さらにwindowのerrorとunhandledrejectionを受け取る。想定外の例外が起きたらsafeStop()がキー入力をすべて解除し、BGMを止め、エラー専用画面へ内容を表示する。処理を続けてスコアやランキングを壊すより、原因を示して再読み込みを求めるほうが安全である。
本項で新しく取り上げる要素技術
Canvas 2D API、requestAnimationFrame、デルタタイム、状態遷移(有限状態機械)、AABB・円の当たり判定、フィッシャー–イェーツのシャッフル、localStorage、JSON、キーボード入力とPointer Events、アスペクト比を保った画面拡大縮小、未処理エラーの捕捉は、これまでの記事ですでに取り上げた。本項では、これらを土台にしつつ、次の12個の要素技術を新しく説明する。
| 要素技術 | 用途 |
|---|---|
| オシレーターによる効果音生成 | Web Audio APIのOscillatorNodeとGainNodeで、ザッパーやブラスターの発射音を作る |
| ノイズとBiquadFilterNode | ランダムな波形とローパスフィルターで爆発音を作る |
| 自動再生制限とAudioContext.resume() | ブラウザの自動再生制限を避け、開始ボタンを押した後に音声を鳴らす |
| 配列とsetIntervalによる簡易BGMシーケンサー | メロディーとベースの音階配列を一定間隔で繰り返し再生する |
| オフスクリーンCanvasによる背景の事前描画 | 地形をあらかじめ1回だけ描き、毎フレームの負荷を減らす |
| 剰余演算と2枚描画による無限スクロール | 1枚のキャッシュ画像を2回描くことで継ぎ目のない縦スクロールを作る |
| 斜め移動の速度補正 | 斜め移動が縦横移動より速くなりすぎないよう調整する |
| 照準地点への投下型攻撃と範囲ダメージ | ブラスターが未来の着弾地点を狙い、爆発範囲で地上敵にダメージを与える |
| 自機を狙う誘導弾のベクトル計算 | 敵弾が発射時点の自機位置へ向かうよう方向を正規化する |
| 画面シェイクとフラッシュ演出 | 被弾やヒットの瞬間に画面を揺らし、白く光らせる |
| 無敵時間の点滅表示 | 復活直後の無敵時間を点滅で分かりやすく示す |
| CSS変数で作る可変長のランキング演出 | 件数に応じて距離と時間が変わるスクロールアニメーションを1つのCSSで実現する |
新しい要素技術1:オシレーターによる効果音生成
Xevios は効果音もBGMも、外部の音声ファイルを一切使わない。すべてWeb Audio APIのOscillatorNodeという部品が、その場で波形を作って鳴らしている。オシレーターは、決まった周波数の音(サイン波、矩形波、のこぎり波、三角波など)を発生させる部品であり、シンセサイザーの音源と同じ仕組みである。

Xevios.html のtone()関数は、周波数、長さ、波形の種類、音量、変化後の周波数を受け取り、1つの短い音を鳴らす。

1つのtone()を、渡す値だけ変えて何度も使い回している点も重要である。ザッパー発射音は高い矩形波(1150Hzから520Hzへ下降)、ブラスター発射音は低いのこぎり波(190Hzから55Hzへ下降)というように、同じ関数に別の数値を渡すだけで、まったく違う印象の音になる。関数を1つ作り込めば、あとは数値の工夫で音のバリエーションを増やせるのが、オシレーターによる効果音生成の利点である。
Xevios.html のtone()関数は、周波数、長さ、波形の種類、音量、変化後の周波数を受け取り、1つの短い音を鳴らす。
function tone(freq, duration, type = 'square', volume = .08, endFreq = freq) {OscillatorNodeが音の高さと波形を、GainNodeが音量を担当し、両者をconnect()でつなげて初めて音が出る。音を電気配線にたとえると、オシレーターが発電機、ゲインがボリュームつまみ、destinationがスピーカーである。周波数と音量の両方にexponentialRampToValueAtTime()を使い、時間とともに指数的に変化させることで、「ピュン」と上下する効果音や、自然に減衰するフェードアウトを作っている。
const a = audioReady();
const now = a.currentTime;
const o = a.createOscillator();
const g = a.createGain();
o.type = type;
o.frequency.setValueAtTime(freq, now);
o.frequency.exponentialRampToValueAtTime(Math.max(20, endFreq), now + duration);
g.gain.setValueAtTime(volume, now);
g.gain.exponentialRampToValueAtTime(.0001, now + duration);
o.connect(g).connect(a.destination);
o.start(now);
o.stop(now + duration);
}
1つのtone()を、渡す値だけ変えて何度も使い回している点も重要である。ザッパー発射音は高い矩形波(1150Hzから520Hzへ下降)、ブラスター発射音は低いのこぎり波(190Hzから55Hzへ下降)というように、同じ関数に別の数値を渡すだけで、まったく違う印象の音になる。関数を1つ作り込めば、あとは数値の工夫で音のバリエーションを増やせるのが、オシレーターによる効果音生成の利点である。
新しい要素技術2:ノイズとBiquadFilterNodeによる爆発音
オシレーターは決まった高さの音しか出せないため、「ザザッ」という爆発音には向かない。爆発音のような雑音には、ホワイトノイズ、つまりでたらめな値を連続して並べた波形が使われる。Xevios.html のnoise()関数は、AudioBufferへ乱数を書き込むことでノイズを自作している。


生のホワイトノイズは耳障りな高音を含むため、BiquadFilterNodeのlowpass(ローパスフィルター)を通し、1100Hzより高い成分を弱めている。フィルターを通すことで、耳当たりの柔らかい「爆発らしい」低い音に変わる。敵機の爆発音はnoise()だけだが、自機の爆発音はnoise()と低いtone()を重ねて鳴らし、より重く長い音にしている。1つの部品を単体で使うだけでなく、組み合わせて音の個性を変えられる点も、Web Audio APIの特徴である。
`(Math.random() * 2 - 1)` は-1~1のでたらめな値であり、これをサンプル数ぶん並べるとノイズになる。さらに `(1 - i / n)` を掛けることで、先頭は大きく、末尾に向かって小さくなる減衰する波形を作る。爆発音が「ドン」と鳴って徐々に消えていくのは、この一手間による。
function noise(duration = .3, volume = .16) {
const a = audioReady();
const now = a.currentTime;
const n = Math.ceil(a.sampleRate * duration);
const buf = a.createBuffer(1, n, a.sampleRate);
const data = buf.getChannelData(0);
for (let i = 0; i < n; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / n);
const src = a.createBufferSource();
const filter = a.createBiquadFilter();
const g = a.createGain();
src.buffer = buf;
filter.type = 'lowpass';
filter.frequency.value = 1100;
g.gain.setValueAtTime(volume, now);
g.gain.exponentialRampToValueAtTime(.0001, now + duration);
src.connect(filter).connect(g).connect(a.destination);
src.start(now);
}
生のホワイトノイズは耳障りな高音を含むため、BiquadFilterNodeのlowpass(ローパスフィルター)を通し、1100Hzより高い成分を弱めている。フィルターを通すことで、耳当たりの柔らかい「爆発らしい」低い音に変わる。敵機の爆発音はnoise()だけだが、自機の爆発音はnoise()と低いtone()を重ねて鳴らし、より重く長い音にしている。1つの部品を単体で使うだけでなく、組み合わせて音の個性を変えられる点も、Web Audio APIの特徴である。
新しい要素技術3:自動再生制限とAudioContext.resume()
多くのブラウザは、利用者の操作なしにページが勝手に音を鳴らすことを制限している。これを自動再生制限(オートプレイポリシー)と呼ぶ。Xevios は音声処理の入口をaudioReady()という1つの関数へ集め、この制限に対応している。

function audioReady() {
if (!audio) audio = new (window.AudioContext |window.webkitAudioContext)();
if (audio.state === 'suspended') audio.resume();
return audio;
} 最初の呼び出しでAudioContextを1回だけ作り、以後はその1個を使い回す。ブラウザによっては作成直後の状態がsuspended(一時停止)になっているため、audio.resume()を呼んで再生できる状態に戻す。この処理を、タイトル画面の「MISSION START」ボタンのクリックというユーザー操作の直後に呼ぶことで、ブラウザの自動再生制限に触れずに音声を開始する。
仮に、ページを開いた瞬間にBGMを鳴らそうとすると、多くのブラウザでは無音のまま失敗する。start()関数はaudioReady()の呼び出しをボタン処理の先頭に置き、続けてBGMを開始する。「ユーザーが押した直後に呼ぶ」という順序を守ることが、自動再生制限を避ける最も確実な方法である。新しい要素技術4:配列とsetIntervalによる簡易BGMシーケンサー
Xevios は、レトロゲーム風のメロディーとベースを、外部の音楽ファイルなしでループ再生する。仕組みは単純で、音階を並べた配列を、setInterval()で一定間隔ごとに1つずつtone()へ渡していくだけである。
`lead[bgmStep % lead.length]` の剰余演算(%)は、bgmStepが配列の長さを超えたら先頭へ戻す働きをする。16個のメロディーと8個のベースを、それぞれ違う周期でループさせることで、単純な配列2本だけでもレトロゲームらしい繰り返しの旋律になる。
function startBgm() {
stopBgm();
bgmStep = 0;
const lead = [330, 392, 440, 392, 330, 294, 330, 247, 294, 330, 392, 330, 294, 247, 220, 294];
const bass = [110, 110, 147, 147, 98, 98, 123, 123];
bgmTimer = setInterval(() => {
if (state === 'play') {
tone(lead[bgmStep % lead.length], .115, 'square', .018);
if (bgmStep % 2 === 0) tone(bass[(bgmStep >> 1) % bass.length], .2, 'triangle', .025);
bgmStep++;
}
}, 135);
}
function stopBgm() {
if (bgmTimer) { clearInterval(bgmTimer); bgmTimer = null; }
}
`if (state === 'play')` という条件があるため、一時停止中もタイマー自体は動き続けるが、新しい音は鳴らない。もしここでsetIntervalそのものを止めてしまうと、再開時に音のタイミングがずれてしまう。また、stopBgm()はclearInterval()でタイマーを確実に止めてからnullを代入する。リトライのたびにstartBgm()の先頭でstopBgm()を呼んでいるのは、前回のタイマーを止め忘れて2つのBGMが重なって鳴る事故を防ぐためである。新しい要素技術5:オフスクリーンCanvasによる背景の事前描画
Xevios の背景(草地・水路・道路)は、毎フレーム描き直しているわけではない。ゲーム開始時に一度だけ描いた画像を、あとはひたすら使い回している。この「画面には表示しないが、絵を描いておくためのCanvas」をオフスクリーンCanvasと呼ぶ。
document.createElement('canvas')で作ったcanvas要素は、HTMLへ追加しなければ画面には表示されない。しかし、getContext('2d')で描画命令を送ることはでき、描いた内容はメモリ上の画像として保持される。buildTerrain()はゲーム開始時に1回だけ呼ばれ、768ピクセル分の地形をこのオフスクリーンCanvasへ描き込む。
const terrain = document.createElement('canvas');
const tctx = terrain.getContext('2d', { alpha: false });
terrain.width = W;
terrain.height = TERRAIN_H;
function buildTerrain() {
// 草地、水路、道路をterrainへ一度だけ描画する
}
buildTerrain();
毎フレーム、草や道路の形を計算しながら描き直すと、敵やスコア表示の描画と合わせて処理が重くなる。あらかじめ描いた地形をdrawImage()で貼り付けるだけなら、複雑な形を毎回計算する必要がない。仕様書にある「背景地形を毎フレーム生成しないことで描画負荷を軽減する」という要求は、このオフスクリーンCanvasによって実現している。新しい要素技術6:剰余演算と2枚描画による無限スクロール
背景を止まることなく流し続けるには、地形の絵を継ぎ目なく繰り返す工夫が要る。Xevios は、事前に作った768ピクセルの地形画像1枚だけを使い、剰余演算(%)と2回のdrawImage()でこれを実現している。
scrollは毎フレーム増え続けるが、`% TERRAIN_H` によって常に0以上TERRAIN_H未満の範囲へ収まる。時計の針が12を超えると0へ戻るのと同じ考え方である。background()は、この位置へ地形画像を描き、もう1枚、その真上(y - TERRAIN_H)にも同じ画像を描く。
scroll = (scroll + 82 * dt) % TERRAIN_H;
function background() {
const y = scroll;
ctx.drawImage(terrain, 0, y - TERRAIN_H);
ctx.drawImage(terrain, 0, y);
}1枚だけでは、scrollが進むにつれて画像の下に何も描かれていない空白ができてしまう。そこで、同じ画像をもう1枚、ちょうど1枚ぶん上の位置へ重ねて描くことで、画面のどこを切り取っても必ずどちらかの画像で埋まるようにしている。動画を1本のテープではなく、同じテープを2巻用意して交互につなぐようなイメージである。1枚の地形画像だけで、継ぎ目のない縦スクロールが実現できる。新しい要素技術7:斜め移動の速度補正
上下左右のキーを組み合わせて斜めに動くとき、何も補正しないと、斜め移動のほうが縦横移動より速くなってしまう。横方向にも縦方向にも同時に最大速度で動くため、実際に進む距離は約1.41倍(√2倍)になるからである。Xevios は、この差を0.707(1÷√2)という係数で打ち消している。
dxとdyは、それぞれ「右か左か」「下か上か」を-1・0・1で表したものである。両方とも0でない、つまり斜め方向のキーが同時に押されているときだけ、両方へ0.707を掛ける。0.707は、1辺の長さが1の直角二等辺三角形の斜辺が√2になることの逆数であり、斜め方向のベクトルの長さを1へ戻す働きをする。仕様書の「斜め移動時も縦横移動時と同程度の移動速度になるよう補正する」は、この1行で実現している。
let dx = (right ? 1 : 0) - (left ? 1 : 0);
let dy = (down ? 1 : 0) - (up ? 1 : 0);
if (dx && dy) {
dx *= .707;
dy *= .707;
}
player.x = clamp(player.x + dx * player.speed * dt, 20, W - 20);
player.y = clamp(player.y + dy * player.speed * dt, 75, H - 30);
clamp()は、値を指定した範囲へ収める自作関数である。dx・dyを補正したあとの移動量をclamp()で20~W-20、75~H-30へ収めることで、自機が画面の外へ出ないようにしている。速度の補正と、画面外へ出さない制約は、別々の処理として素直に積み重ねればよい。新しい要素技術8:照準地点への投下型攻撃と範囲ダメージ
ザッパーが「自機の位置から前方へまっすぐ飛ぶ」のに対し、ブラスターは画面上の固定した1点(照準地点)を狙う。fireBlaster()は、発射時点の自機のx座標と、自機より少し上のy座標を、そのまま着弾目標(tx, ty)として記録する。
弾は'travel'(移動中)という状態で生まれ、毎フレーム、現在位置を目標(tx, ty)へ少しずつ近づける。0.48秒かけて到達すると'boom'(爆発)に切り替わり、そこで初めてMath.hypot()を使い、爆心から半径43ピクセル以内にいる地上物すべてへダメージを与える。
function fireBlaster() {
if (player.bcd <= 0) {
const tx = player.x;
const ty = clamp(player.y - 150, 90, H - 100);
blasts.push({ x: player.x, y: player.y - 8, tx, ty, t: 0, phase: 'travel' });
player.bcd = .7;
sfx.blaster();
}
}
自機やザッパーの当たり判定が「1点対1点」であるのに対し、ブラスターの爆発は「1点対、範囲内のすべて」という範囲攻撃(AoE:Area of Effect)である。ドモグラムのように動き回る地上敵を倒すには、いま見えている位置ではなく、爆発が起きる0.48秒後にどこへ移動しているかを予測して照準を合わせる必要がある。この「未来を狙う」感覚こそが、ゼビウス系シューティングらしい駆け引きを生んでいる。新しい要素技術9:自機を狙う誘導弾のベクトル計算
タルケンやゾシー、ログラム、ドモグラムが撃つ弾は、発射された瞬間の自機の位置へ向かって直進する。「向かって直進する」ためには、敵から自機への方向ベクトルを求め、それを一定の速さに揃える必要がある。
function enemyBullet(x, y, aim = true) {
let dx = 0;
let dy = 1;
if (aim) {
dx = player.x - x;
dy = player.y - y;
const d = Math.hypot(dx, dy) |1;
dx /= d;
dy /= d;
}
bullets.push({ x, y, vx: dx * 105, vy: dy * 105, r: 3.5 });
} `player.x - x` と `player.y - y` で、敵から自機へ向かう矢印(ベクトル)の成分が求まる。このままでは矢印の長さが敵と自機の距離によってバラバラなので、Math.hypot(dx, dy)で矢印の長さ(距離)を求め、dx・dyをその長さで割る。これを正規化(ノーマライズ)と呼び、結果は必ず長さ1の矢印になる。長さ1にそろえておけば、あとは好きな速さ(このプログラムでは105)を掛けるだけで、どの角度でも同じ速さの弾が作れる。
`Math.hypot(dx, dy) |1` の `| 1` は、万が一dxとdyが両方0(敵と自機がまったく同じ座標)だったときに、0で割ってしまう事故を防ぐ保険である。割り算の前には、割る数が0にならないかを確認するという習慣は、ベクトル計算に限らず広く役立つ。 新しい要素技術10:画面シェイクとフラッシュによる被弾演出
自機や敵が破壊された瞬間、Xevios の画面はわずかに揺れ、一瞬白く光る。この演出は、shakeとflashという2つの数値を、毎フレーム少しずつ減らしていくだけで作られている。
被弾時にはshake=12とflash=.18を、雑魚敵の撃破時にはshake=4だけを、というように、出来事の大きさに応じて数値の初期値を変えている。値は毎フレーム一定量ずつ減っていくので、特別なアニメーション処理を書かなくても、時間とともに自然に収まる。shakeが0より大きい間だけ、描画前にctx.translate()でキャンバス全体の原点をランダムにずらすことで、画面全体を描き直さずに「揺れ」を表現している。
shake = Math.max(0, shake - 45 * dt);
flash = Math.max(0, flash - dt);
// 描画の直前
if (shake) ctx.translate(rand(-shake, shake), rand(-shake, shake));
// 描画の直後
if (flash) {
ctx.fillStyle = '#fff8';
ctx.fillRect(0, 0, W, H);
}
flashは、半透明の白い四角形(`#fff8` はアルファ値付きの白)を画面全体に重ねるだけの単純な処理だが、値が大きいフレームほど不透明に近くなるよう色自体を変えれば、より滑らかな光り方にもできる。専用の画像やアニメーションのライブラリを使わなくても、数値を1つ増減させるだけの演出は効果が大きく、コストも小さい。新しい要素技術11:無敵時間の点滅表示
残機を失って復活した直後は、3.2秒間だけ敵や敵弾に触れてもミスにならない無敵時間を設けている。無敵かどうかが画面から分からないと不公平に感じるため、自機を点滅させて知らせている。
player.invは復活時に3.2を代入され、毎フレームdtぶん減っていく。`Math.floor(player.inv * 10) % 2` は、invを10倍して整数に切り捨てた値が偶数か奇数かを調べており、約0.1秒ごとに0と1を交互に繰り返す。1のときだけ描画を打ち切って`return`するため、約0.1秒おきに「描く」「描かない」を切り替える点滅になる。
function drawPlayer() {
ctx.save();
ctx.translate(player.x, player.y);
if (player.inv > 0 && Math.floor(player.inv * 10) % 2) return ctx.restore();
// ここから自機本体を描画する
ctx.restore();
}
ここで重要なのは、点滅は見た目だけの演出であり、無敵かどうかの判定自体は別の場所(`player.inv` が0以下かどうか)で行っている点である。update()の中で敵弾との当たり判定を調べる処理は「player.invが0以下のときだけミスにする」という条件を持ち、drawPlayer()の点滅とは独立している。見た目の表現と、実際の判定ロジックを別々の変数・条件に分けておくと、片方の見た目を後から変更しても、当たり判定の挙動には影響しない。新しい要素技術12:CSS変数で作る可変長のランキング演出
ゲーム終了画面のハイスコアランキングは、記録が1件でも10件でも、下から上へ一定の速さで流れ続ける。記録の件数によって表の高さが変わるのに、アニメーションの速さだけは変えたくないという、少しわがままな要求を、CSSのカスタムプロパティ(CSS変数)で解決している。
CSSの@keyframesは、「0%地点でどこにいて、100%地点でどこにいるか」というアニメーションの形を1回だけ定義している。実際にどれだけの距離を、何秒かけて動かすかは、`var(--rank-shift, -30px)` のようにvar()の中でCSS変数を参照し、値が指定されていなければ後ろのフォールバック値を使う。
/* CSS:距離と時間は変数まかせにしておく */
.rank-scroll {
animation: rankRoll var(--rank-time, 12s) linear infinite;
}
@keyframes rankRoll {
0% { transform: translateY(132px); }
100% { transform: translateY(var(--rank-shift, -30px)); }
}
// JavaScript:件数に応じて変数の値を計算する
function rankingHTML() {
const rows = loadScores();
const shift = -(rows.length * 25 + 18);
const time = Math.max(7, rows.length * 1.8 + 4);
return `<div class="rank-scroll" style="--rank-shift:${shift}px;--rank-time:${time}s">...</div>`;
}
JavaScript側のrankingHTML()は、記録の件数(rows.length)から移動距離(shift)と再生時間(time)を計算し、`style="--rank-shift:...;--rank-time:..."` という形でHTML要素へ直接書き込む。要素ごとに異なる値を持つCSS変数をインラインスタイルで上書きすることで、@keyframesという1つの定義を書き換えずに、記録が1件のときはゆっくり短く、10件のときは少し長い時間をかけて、同じ体感速度でスクロールさせられる。CSSに「動き方の形」を、JavaScriptに「今回の具体的な数値」を担当させる、役割分担の一例である。仕様と実装を照合する
添付された Xevios.html は、8方向移動、ザッパーとブラスターの二刀流、残機3・復活時3.2秒の無敵、ステージ6,000点ごとの進行、スコア・最高得点・ステージ・残機の表示、上位10件のランキング、キーボードとタッチ操作の両対応、システムエラー時の安全停止という中心要件を満たしている。外部ライブラリや外部の画像・音声ファイルを使わず、インターネット通信も行っていない点も仕様どおりである。
一方、仕様書の「画面全体は自動的に縦スクロールする」という表現には、誰が動いているのかという解釈の余地がある。原作のゼビウスは自機を含む世界全体がスクロールする表現だが、Xevios.html の実装は、自機を画面内の固定した範囲にとどめ、背景・地上敵・地上物だけを下方向へ動かすことで、見た目には同じ「上空を前進している」体感を再現している。プレイヤーの操作感覚は仕様の意図どおりだが、内部の実装方針としては別の選択肢もあったことは、仕様書に一言添えておくとよい。
また、原作ゼビウスには22~26種類のキャラクターが登場するが、Xevios.html はそのうち、空中敵4種(トーロイド、タルケン、ゾシー、バキュラ)、地上敵・地上物3種(ログラム、ドモグラム、ソル)だけを実装している。これは手抜きではなく、「危険度ランキングと簡略化の判断」で述べたとおり、複雑な挙動を持つ強敵を除き、動きの型を4種類に絞って理解しやすくするという設計判断である。将来キャラクターを追加する場合は、危険度ランキングの表を参考に、どの動きの型から手を付けるかを検討するとよい。Xeviosのプログラム仕様書
以下が、Xevios.html を作るために Codex へ渡したプロンプトである。ゲーム・ルールの節(自機の移動、残機、武器、空中敵、地上敵、難易度、サウンドなど)を細かく書き込むほど、Codexが作る挙動と、頭の中で思い描いていた挙動のずれが小さくなる。プログラム仕様書(プロンプト)# 縦スクロールシューティングゲーム「Xevios」
# 目標 自機を上下左右に動かし、駆虫や地上の敵キャラを撃ち落とす。画面全体は自動的に縦スクロールする「ゼビウス」風ゲーム。
# プロジェクト・フォルダ作成 - プログラム・ファイル名の拡張子を除いた主ファイル名と同じ名前のサブフォルダを作成し、以降の作業はサブフォルダで行う。 - すでにサブフォルダがあれば、そのサブフォルダに移動して以降の作業を進める。
# ゲーム・ルール
## 1. 基本情報 - ゲーム名:Xevios - ジャンル:縦スクロールシューティング - メインプログラム:Xevios.html - 実行方法:Webブラウザで Xevios.html を開く - 外部ライブラリ:使用しない - 外部画像・音声ファイル:使用しない - 描画方式:HTML Canvas - サウンド方式:Web Audio APIによるリアルタイム生成
## 2. ゲーム概要 自機「SOLVARO」を画面内で上下左右に動かし、空中敵と地上敵を撃破する。
背景は自動的に下方向へ流れ、上空を前進しているように見える縦スクロール方式とする。空中敵は独自に移動し、地上敵は背景スクロールに合わせて画面下へ移動する。
空中敵と地上敵では有効な武器が異なるため、ザッパーとブラスターを使い分ける。
## 3. 自機「SOLVARO」
### 移動 - 画面内を上下左右および斜め方向へ移動できる。 - 斜め移動時も縦横移動時と同程度の移動速度になるよう補正する。 - 画面外へは移動できない。 - 自機の見た目より当たり判定を小さくし、敵弾を避けやすくする。
### 残機と復活 - 初期残機は3機。 - 敵、敵弾、地上物との接触で1機失う。 - 残機がある場合は画面下部中央付近へ復活する。 - 復活後は3.2秒間の無敵時間を設ける。 - 無敵中は自機を点滅表示する。 - 残機が0になるとゲーム終了となる。
## 4. 操作方法
| 操作 | キーボード | 画面ボタン | |---|---|---| | 上へ移動 | ↑ または W | ▲ | | 下へ移動 | ↓ または S | ▼ | | 左へ移動 | ← または A | ◀ | | 右へ移動 | → または D | ▶ | | ザッパー | Z または Space | ZAPPER | | ブラスター | X または Enter | BLASTER | | 一時停止・再開 | P | なし |
- カーソルキーは KeyboardEvent.code と KeyboardEvent.key の両方で認識する。 - ゲーム開始およびリトライ時は、ゲーム画面へキーボードフォーカスを移す。 - カーソルキー操作によるWebページ自体のスクロールを抑止する。 - ウィンドウがフォーカスを失った場合は、押下中のキー状態を解除する。 - タッチ端末では画面下部の方向ボタンと攻撃ボタンを使用できる。
## 5. 武器
### ザッパー - 空中敵用の前方射撃。 - 自機前方から2本の弾を同時に発射する。 - 空中敵に命中するとダメージを与える。 - 地上敵には効果がない。 - 破壊不能敵「バキュラ」に命中すると弾き返され、反射エフェクトと反射音が発生する。
### ブラスター - 地上敵用の爆撃。 - 自機前方に照準カーソルを常時表示する。 - 発射すると照準地点へ弾が移動し、到達後に円形の爆発を発生させる。 - 爆発範囲内の地上敵へダメージを与える。 - 空中敵には効果がない。
## 6. 空中敵
### トーロイド - 序盤から多く出現する基本敵。 - 自機と横位置が近づくと左右へ旋回して離脱する。 - 基本的に射撃しない。 - ザッパー1発で破壊できる。 - 得点:30点。
### タルケン - 正面から接近する。 - 一定位置まで降下すると反転し、画面上方向へ退却する。 - 自機を狙って敵弾を発射する。 - ザッパー1発で破壊できる。 - 得点:50点。
### ゾシー - 左右へ不規則に移動しながら降下する。 - 自機を狙って敵弾を発射する。 - ザッパー1発で破壊できる。 - 得点:100点。
### バキュラ - ステージ2以降に低確率で出現する。 - 回転しながら上から下へ直進する。 - 射撃は行わないが、接触すると自機が失われる。 - 完全破壊不能。 - ザッパーを弾き返す。 - 得点は発生しない。
## 7. 地上敵・地上物
### ログラム - 地上に固定された砲台。 - 自機を狙って敵弾を発射する。 - ブラスター1発で破壊できる。 - 得点:300点。
### ドモグラム - 地上を左右に移動する。 - 移動しながら自機を狙って敵弾を発射する。 - ブラスター1発で破壊できる。 - 得点:800点。
### ソル - 地中に隠された地上物。 - 初期状態では地面上に薄い目印のみを表示する。 - 1回目のブラスター命中で地上へ出現し、2,000点を加算する。 - 2回目のブラスター命中で破壊し、さらに2,000点を加算する。
## 8. 敵弾と衝突判定 - 敵弾は発射時点の自機位置を狙って直進する。 - 敵弾は画面外へ出ると削除する。 - 敵弾と自機の当たり判定は、表示サイズより少し小さくする。 - 空中敵との接触および地上物との接触でも自機を失う。
## 9. 難易度 - 序盤は敵の出現間隔を長めにする。 - スコアが6,000点増えるごとにステージが1段階進む。 - ステージ進行に応じて空中敵の出現間隔を徐々に短縮する。 - 最短出現間隔には下限を設け、過密状態を防止する。 - 敵弾速度は自機が回避しやすい速度に調整する。 - 地上敵の射撃間隔は空中敵より長めにする。 - バキュラの出現率は低く設定する。 - 自機の移動速度は敵弾を避けやすいよう若干速めに設定する。
## 10. スコアとステージ表示 画面上部に次の情報を表示する。
- 現在のスコア - 最高得点 - 現在のステージ - 残機
最高得点はブラウザの localStorage に保存し、ブラウザを閉じた後も保持する。
## 11. ハイスコアランキング - ゲーム終了時に今回のスコアを自動的に記録する。 - 記録内容は順位、スコア、記録日時とする。 - スコアの高い順に並べる。 - 上位10件を localStorage に保存する。 - リトライ待機画面にランキングを表示する。 - ランキングは表示領域の下から上へ繰り返しスクロールする。 - 記録が1件だけの場合もスクロール表示する。
## 12. サウンド 音声は外部ファイルを使用せず、Web Audio APIのオシレーターとノイズで生成する。
### 効果音 - ザッパー発射音:短い高音。 - ブラスター発射音:低く下降する投下音。 - 敵機爆発音:ノイズを含む短い爆発音。 - ザッパー反射音:金属的に上昇する音。 - 自機爆発音:敵機より低く重い爆発音。 - ゲーム終了音:音程が段階的に下降する短いフレーズ。
### BGM - レトロゲーム調のメロディーとベースをループ再生する。 - ゲーム開始時に再生を開始する。 - 一時停止中は新しい音符を鳴らさない。 - ゲーム終了時に停止する。 - リトライ時は以前のBGMタイマーを解除してから再開し、重複再生を防ぐ。
ブラウザの自動再生制限に対応するため、音声処理はユーザーがゲーム開始ボタンを押した後に開始する。
## 13. 画面構成
### タイトル画面 - ゲームタイトル「Xevios」 - 基本操作説明 - MISSION START ボタン
### ゲーム画面 - 縦横比は2:3。 - 内部Canvas解像度は480×720ピクセル。 - ウィンドウサイズに合わせて縦横比を維持したまま拡大・縮小する。 - 画面上部にスコア、ハイスコア、ステージ、残機を表示する。 - 画面下部にタッチ操作用ボタンを表示する。
### ゲーム終了画面 - MISSION OVER を表示する。 - 今回のスコアを表示する。 - ハイスコアランキングをスクロール表示する。 - RETRY ボタンで新しいゲームを開始できる。 - Enter キーでもリトライできる。
## 14. 背景スクロールと描画最適化 - 草地、水路、道路状の地形を自動的に縦スクロールする。 - 768ピクセル高の地形をオフスクリーンCanvasへ起動時に一度だけ描画する。 - ゲーム中はキャッシュした地形画像2枚を連結して循環表示する。 - 背景地形を毎フレーム生成しないことで描画負荷を軽減する。 - スクロール位置はサブピクセル精度で計算し、滑らかに描画する。 - フレーム更新が一時的に遅れた場合は、経過時間に上限を設けて大きな位置飛びを防ぐ。 - Canvasは不透明描画として生成し、背景合成処理を軽減する。
## 15. データ保存 ブラウザの localStorage に次の情報を保存する。
| キー | 内容 | |---|---| | xeviosHigh | 最高得点 | | xeviosScores | 上位10件のスコアと記録日時 |
保存データは同じブラウザおよび同じページの保存領域で利用する。
## 16. 対応環境 - HTML Canvas、Web Audio API、localStorage を利用できるモダンブラウザ。 - PCのキーボード操作に対応する。 - タッチまたはポインター操作が可能な端末に対応する。 - インターネット接続は不要。
# エラー処理 - JavaScript実行中にシステムエラーが発生した場合は、処理を安全に停止する。 - エラー内容を画面に表示する - 無効な入力は受け付けず、理由をメッセージで表示する
# テスト観点・合格条件 - 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にコミットする。 -新規作成(評価用)‥‥バージョン0.1.0 -新規作成(配布用)‥‥バージョン1.0.0 -メジャーバージョンアップ‥‥バージョン番号の整数部分を+1 -マイナーバージョンアップ‥‥バージョン番号の小数の1番目を+1 -不具合修正‥‥バージョン番号の小数の2番目を+1
# 配布ファイルの作成 1)プログラムファイル、簡易取扱説明書、このプロンプトを1つのZIPファイルに圧縮する。ZIPファイル名は、"プログラム主ファイル名_バージョン番号.ZIP" の形式にする。 2)完了後、作成したZIPファイルの保存場所を教える。実際の仕様書には、自機・武器・敵ごとの挙動、難易度の上げ方、サウンドの種類、データ保存の項目まで、数値を含めて細かく指定した。「補正する」「軽減する」のような曖昧な言葉だけで終わらせず、「6,000点ごと」「3.2秒間」「768ピクセル」のように具体的な数値を仕様書へ書くことで、Codexが作る挙動のばらつきを減らしている。参考サイト
- Web Audio API:MDN Web Docs
- OscillatorNode:MDN Web Docs
- GainNode:MDN Web Docs
- BiquadFilterNode:MDN Web Docs
- BaseAudioContext.createBuffer():MDN Web Docs
- AudioContext.resume():MDN Web Docs
- setInterval():MDN Web Docs
- CanvasRenderingContext2D.drawImage():MDN Web Docs
- requestAnimationFrame():MDN Web Docs
- Math.hypot():MDN Web Docs
- CSSカスタムプロパティの使用:MDN Web Docs
- @keyframes:MDN Web Docs
- ゼビウス:Wikipedia
(この項おわり)
