マガジン

HTMLとJavaScriptだけでゲームを作る:1ファイル構成の始め方

遊びをつくる

執筆にClaudeを使用しています。

週末の明るい机に置かれた、一つの仕組みで遊べる小さな完成済みゲーム。
記事のテーマをもとに制作したAI生成の写真風イメージ。

ゲームを作ってみたいと思ったとき、最初の壁になりやすいのが「環境構築」です。サーバーを立てたり、フレームワークをインストールしたりする前に、実はHTMLファイル1つで動くゲームを書くことができます。 1ファイル構成とは、HTML・CSS・JavaScriptをひとつの .html ファイルにまとめて書く方法です。ブラウザで直接開けば動き、ファイルをコピーするだけで共有できます。練習用の選択肢として手軽ですが、ロジックの規模が大きくなっても同じ構造で対応できる範囲を知っておくことが大切です。 この記事では、1ファイル構成がどんな用途に向くかを整理したうえで、実際に動くゲームの骨格を書く手順と、よく詰まるポイントを具体的に解説します。

この記事の目次
  1. 1ファイル構成が向く場面と向かない場面
  2. ファイルの基本構造を把握する
  3. Canvas を使ったゲームループの書き方
  4. よく詰まるポイントと対処
  5. 動作確認から公開まで
  6. 次の行動

1ファイル構成が向く場面と向かない場面

1ファイル構成の強みは「開くだけで動く」という即時性にあります。開発中の確認にNode.jsもローカルサーバーも要りません。完成後もファイルひとつを渡すか、そのままアップロードするだけです。

向いている用途の目安は次のとおりです。

  • プロトタイプや習作:ルールを素早く試したいとき
  • ミニゲーム:パズル・クリッカー・アクションなど、1〜2画面で完結するもの
  • 学習目的:HTML・CSSとJavaScriptの関係を手を動かして理解したいとき

逆に、次のような状況では早めに複数ファイル構成へ切り替えるほうが管理しやすくなります。

  • JavaScriptが500〜1000行を超え始めたとき
  • 音声・画像などのアセットが増えてファイルサイズが大きくなるとき
  • 複数人で同じコードを編集するとき

「1ファイルだから限界がある」ではなく、「今の規模に対して適切か」で判断するのが実際的です。

ファイルの基本構造を把握する

1ファイルのHTML内には、大きく3つの領域を置きます。

html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ゲームのタイトル</title>
  <style>
    /* CSSをここに書く */
  </style>
</head>
<body>
  <!-- HTMLの要素をここに書く -->
  <script>
    // JavaScriptをここに書く
  </script>
</body>

<style> タグはHTMLの見た目を制御し、<script> タグはゲームのロジックを担います。<script> を </body> の直前に置くのは、HTMLの要素が読み込まれてからJavaScriptが実行されるようにするためです。これを逆にすると「要素が見つからない」エラーが起きやすくなります。

Canvas を使ったゲームループの書き方

ブロック崩しやシューティングのように「毎フレーム画面を更新する」ゲームには、<canvas> 要素とゲームループが必要です。ここでは最小限の骨格を順に説明します。

1. canvas要素を置く

html
<body>
  <canvas id="gameCanvas" width="480" height="320"></canvas>
  <script>
    // 以下にJSを書く
  </script>
</body>

width と height はCSSではなくHTML属性で指定します。CSSで大きさを変えると描画がぼやけることがあるため、実際の描画解像度はHTML属性側で決めるのが基本です。

2. 描画コンテキストを取得する

js
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

ctx が実際に絵を描くための道具です。ctx.fillRect(x, y, w, h) で矩形を、ctx.drawImage(image, x, y) で画像を描けます。

3. ゲームループを作る

js
function update() {
  // ゲームの状態を更新する
}

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height); // 毎フレーム消去
  // 描画処理を書く
}

function loop() {
  update();
  draw();
  requestAnimationFrame(loop);
}

requestAnimationFrame(loop);

requestAnimationFrame はブラウザの描画タイミングに合わせてループを呼び出す関数で、setInterval より滑らかに動きます。clearRect で前のフレームを消してから描き直すことで、残像なしのアニメーションになります。

4. キー入力を受け取る

js
const keys = {};

document.addEventListener('keydown', e => { keys[e.key] = true; });
document.addEventListener('keyup',  e => { keys[e.key] = false; });

keys オブジェクトを参照することで、update() の中で「ArrowLeftが押されているか」を毎フレーム確認できます。keydown イベントだけを使うと押しっぱなしに対応できないため、押した・離したの両方を記録するのがポイントです。

よく詰まるポイントと対処

画像が表示されない:ローカルファイルから <img> や drawImage で別ファイルの画像を読み込もうとすると、ブラウザのセキュリティ制限(CORS)でブロックされる場合があります。1ファイル構成で画像を使いたいときは、画像をBase64エンコードしてHTMLに埋め込む方法が確実です。

スマートフォンで動かない:タッチ操作に対応するには touchstart / touchend / touchmove イベントを別途記述する必要があります。キーボード操作だけでは、スマートフォンからプレイできません。

ファイルが重くなってきた:音声ファイルやスプライトシートをBase64で埋め込むとファイルサイズが急増します。数十KBを超えるアセットが複数必要になった段階で、ZIPにまとめて外部ファイルとして配置する構成に切り替えることを検討してください。

コードが読みにくくなってきた:1ファイルの中でもコードは整理できます。update ・draw ・init など役割ごとに関数を分け、変数を関数の外にまとめて宣言することで、スクロールせずに全体を把握しやすくなります。

動作確認から公開まで

ファイルをブラウザで開いて動作確認できたら、そのまま公開できます。アソバースではHTMLファイルの直接投稿に対応しているため、完成したファイルをアップロードするだけでブラウザプレイ可能なページが生成されます。複数のアセットを使っている場合はZIPにまとめて投稿することもできます。

公開前に確認しておきたい点は次のとおりです。

  • 別ウィンドウやシークレットモードで開いても正常に動くか(キャッシュの影響を除外するため)
  • スマートフォン表示でレイアウトが崩れていないか( viewport メタタグと canvas サイズの関係を確認)
  • ゲームオーバーや終了後に再スタートできるか(ループが止まったまま操作不能にならないか)

これらを確認してから投稿すると、初めてプレイした人がつまずきにくくなります。

次の行動

まずテキストエディタで空の .html ファイルを作り、この記事の骨格コードをそのまま貼り付けて動かしてみてください。ゲームループが動いていることを確認できたら、矩形を動かすだけのシンプルなプロトタイプを作ることが次のステップです。ある程度動くものができたら、アソバースへの投稿を試してみると、プレイ可能なURLが手に入り、フィードバックを得やすくなります。