【ゲーム】JavaScript:63 絵文字よけゲーム

 「🏃‍♂️ 絵文字よけゲーム 🏃‍♂️」は、プレイヤー(🚗)を左右キーで操作し、上から降ってくる絵文字を一定時間よけ続けるアクションゲームです。制限時間(デフォルト25秒)を越えるとクリア、障害物にぶつかるとゲームオーバーになります。

遊び方・操作方法

  • スタートボタンを押すとゲーム開始。
  • ←キー/→キーで車(🚗)を左右に移動。
  • 障害物として降ってくる絵文字(🍎,🍋,…)に当たらないように操作。
  • 制限時間を過ぎるか、障害物に衝突すると終了画面に移行します。

ルール

  • 制限時間:25秒
  • 障害物:ランダム絵文字、時間経過で出現頻度と落下速度が上昇
  • クリア条件:25秒間一度も衝突せずにプレイ
  • ゲームオーバー:車と障害物が重なった瞬間
  • 得点要素:タイム到達でクリア演出のみ(タイムトライアルとして楽しむ)

🎮ゲームプレイ

以下のリンク先から実際にプレイできます。

63 絵文字よけゲーム

素材のダウンロード

以下のリンクから使用する素材をダウンロードできます。

emoji_dodge_title.pngemoji_dodge_bg.png

ゲーム画面イメージ

プログラム全文(emoji_dodge.html)

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>🏃‍♂️ 絵文字よけゲーム 🏃‍♂️</title>
  <style>
    html, body {
      margin: 0; padding: 0;
      width: 100vw; height: 100vh;
      font-family: 'Yu Gothic', 'Meiryo', sans-serif;
      background: url('emoji_dodge_bg.png') no-repeat center center fixed;
      background-size: cover;
    }
    body {
      width: 100vw;
      height: 100vh;
      overflow: auto;
    }
    .container {
      width: 800px;
      margin: 40px auto;
      background: rgba(255,255,255,0.96);
      border-radius: 22px;
      box-shadow: 0 4px 24px rgba(0,0,0,0.13);
      padding-bottom: 32px;
      min-height: 540px;
      position: relative;
    }
    .title-img {
      display: block;
      margin: 20px auto 18px auto;
      width: 340px;
      max-width: 80%;
      height: auto;
    }
    h1 {
      text-align: center;
      font-size: 2.1em;
      margin: 0.6em 0 0.15em 0;
      color: #1976d2;
      text-shadow: 1px 1px 7px #fff;
      letter-spacing: 0.04em;
    }
    .rule-section {
      background: rgba(235,245,255,0.98);
      border-radius: 14px;
      margin: 28px 32px 14px 32px;
      padding: 16px 24px;
      box-shadow: 0 2px 8px #4d90fe22;
    }
    .rule-title {
      text-align: center;
      font-weight: bold;
      font-size: 1.3em;
      margin-bottom: 10px;
      color: #1976d2;
    }
    .rule-text {
      text-align: left;
      font-size: 1.09em;
      line-height: 1.65;
      color: #174078;
      letter-spacing: 0.01em;
    }
    .btn {
      display: block;
      margin: 28px auto 0 auto;
      padding: 14px 50px;
      font-size: 1.2em;
      border: none;
      border-radius: 28px;
      background: linear-gradient(90deg, #87c9fa, #1565c0);
      color: #fff;
      font-weight: bold;
      box-shadow: 0 2px 8px #1976d266;
      cursor: pointer;
      transition: background 0.2s;
    }
    .btn:hover { background: linear-gradient(90deg, #b7e4fe, #1976d2); }
    .game-area {
      width: 740px;
      margin: 36px auto 0 auto;
      background: rgba(240,248,255,0.96);
      border-radius: 16px;
      box-shadow: 0 1px 10px #1565c055;
      display: flex;
      flex-direction: column;
      align-items: center;
      min-height: 330px;
      padding: 16px 0 20px 0;
      user-select: none;
    }
    .info {
      text-align: center;
      color: #1976d2;
      font-size: 1.18em;
      margin: 5px 0 0 0;
    }
    .game-canvas {
      margin: 20px auto 0 auto;
      background: #fff;
      border-radius: 18px;
      box-shadow: 0 0 12px #1976d245;
      border: 2px solid #1565c0;
      display: block;
    }
    .message-box {
      text-align: center;
      background: rgba(235,245,255,0.98);
      font-size: 1.7em;
      color: #1976d2;
      font-weight: bold;
      border-radius: 16px;
      box-shadow: 0 4px 24px #1976d233;
      width: 80%;
      max-width: 480px;
      margin: 34px auto 0 auto;
      padding: 32px 18px;
      position: relative;
      z-index: 2;
    }
    .center-btn { margin: 22px auto 0 auto; }
    .timer-bar {
      text-align: center;
      font-size: 1.14em;
      margin-top: 6px;
      color: #e53935;
      font-weight: bold;
      letter-spacing: 0.08em;
    }
  </style>
</head>
<body>
  <div class="container" id="main-container"></div>
  <script>
    // ============================
    // 🏃‍♂️ 絵文字よけゲーム
    // ============================

    // ゲームパラメータ
    const CANVAS_W = 700;
    const CANVAS_H = 330;
    const PLAYER_W = 44;
    const PLAYER_H = 44;
    const PLAYER_Y = CANVAS_H - PLAYER_H - 4;
    const OBSTACLE_W = 38;
    const OBSTACLE_H = 38;
    const OBSTACLE_EMOJIS = ["🍎","🍋","🍌","🍇","🍊","🍓","🥑","🍒","🥕","🌽","🥐","🍔","🍕"];
    const PLAYER_EMOJI = "🚗";
    const GAME_TIME = 25; // 秒避けきればクリア

    let obstacles = [];
    let playerX = 0;
    let playerV = 0;
    let startTime = 0;
    let timerId = null;
    let animId = null;
    let gameActive = false;
    let clearFlag = false;

    // タイトル画面
    function showTitleScreen() {
      clearInterval(timerId);
      cancelAnimationFrame(animId);
      document.getElementById('main-container').innerHTML = `
        <h1>🏃‍♂️ 絵文字よけゲーム 🏃‍♂️</h1>
        <img src="emoji_dodge_title.png" class="title-img" alt="絵文字よけゲーム タイトル">
        <div class="rule-section">
          <div class="rule-title">🏃‍♂️ 遊び方・ルール 🏃‍♂️</div>
          <div class="rule-text">
            ・左右キー(←→)で車(🚗)を操作します。<br>
            ・上から落ちてくる絵文字をよけ続けましょう。<br>
            ・${GAME_TIME}秒よけきるとクリア!<br>
            ・ぶつかるとゲームオーバーです。
          </div>
        </div>
        <button class="btn" id="start-btn">スタート</button>
      `;
      document.getElementById('start-btn').onclick = showGameScreen;
    }

    // ゲーム初期化&開始
    function showGameScreen() {
      obstacles = [];
      playerX = (CANVAS_W - PLAYER_W) / 2;
      playerV = 0;
      clearFlag = false;
      startTime = Date.now();
      gameActive = true;

      // 描画
      document.getElementById('main-container').innerHTML = `
        <h1>🏃‍♂️ 絵文字よけゲーム 🏃‍♂️</h1>
        <div class="game-area">
          <div class="info">←→キーで操作・絵文字をよけよう!</div>
          <div class="timer-bar" id="timer-bar">残り: ${GAME_TIME} 秒</div>
          <canvas class="game-canvas" id="game-canvas" width="${CANVAS_W}" height="${CANVAS_H}"></canvas>
        </div>
      `;
      // キーリスナー
      window.onkeydown = (e) => {
        if (!gameActive) return;
        if (e.key === "ArrowLeft") playerV = -7;
        else if (e.key === "ArrowRight") playerV = 7;
      };
      window.onkeyup = (e) => {
        if (!gameActive) return;
        if (e.key === "ArrowLeft" || e.key === "ArrowRight") playerV = 0;
      };
      // ループ開始
      animId = requestAnimationFrame(gameLoop);
      timerId = setInterval(()=>{
        let t = Math.max(0, GAME_TIME - ((Date.now() - startTime) / 1000));
        document.getElementById("timer-bar").textContent = `残り: ${t.toFixed(1)} 秒`;
      }, 100);
    }

    // メインゲームループ
    function gameLoop() {
      const ctx = document.getElementById("game-canvas").getContext("2d");
      ctx.clearRect(0, 0, CANVAS_W, CANVAS_H);

      // 障害物生成
      if (Math.random() < 0.028 + Math.min(0.07, (Date.now() - startTime)/30000)) {
        // 落ちてくるx座標ランダム
        const x = Math.floor(Math.random() * (CANVAS_W - OBSTACLE_W));
        const emoji = OBSTACLE_EMOJIS[Math.floor(Math.random() * OBSTACLE_EMOJIS.length)];
        obstacles.push({x, y: -OBSTACLE_H, emoji});
      }
      // 障害物移動
      for (let obs of obstacles) {
        obs.y += 4 + Math.min(6, (Date.now()-startTime)/8000); // 時間で加速
      }
      // 障害物と当たり判定
      for (let obs of obstacles) {
        if (
          obs.x < playerX + PLAYER_W &&
          obs.x + OBSTACLE_W > playerX &&
          obs.y < PLAYER_Y + PLAYER_H &&
          obs.y + OBSTACLE_H > PLAYER_Y
        ) {
          // 当たり!ゲームオーバー
          gameOver(false);
          return;
        }
      }
      // 画面外を削除
      obstacles = obstacles.filter(obs => obs.y < CANVAS_H);

      // プレイヤー移動
      playerX += playerV;
      if (playerX < 0) playerX = 0;
      if (playerX > CANVAS_W - PLAYER_W) playerX = CANVAS_W - PLAYER_W;

      // プレイヤー描画
      ctx.font = "38px serif";
      ctx.fillText("🚗", playerX, PLAYER_Y + PLAYER_H - 2);

      // 障害物描画
      for (let obs of obstacles) {
        ctx.font = "33px serif";
        ctx.fillText(obs.emoji, obs.x, obs.y + OBSTACLE_H - 4);
      }

      // 時間切れ判定
      if (((Date.now() - startTime) / 1000) >= GAME_TIME) {
        clearFlag = true;
        gameOver(true);
        return;
      }

      // ループ
      if (gameActive) animId = requestAnimationFrame(gameLoop);
    }

    // ゲーム終了
    function gameOver(isClear) {
      gameActive = false;
      clearInterval(timerId);
      cancelAnimationFrame(animId);
      setTimeout(() => showEndScreen(isClear), 300);
    }

    // 終了画面
    function showEndScreen(isClear) {
      document.getElementById('main-container').innerHTML = `
        <h1>🏃‍♂️ 絵文字よけゲーム 🏃‍♂️</h1>
        <img src="emoji_dodge_title.png" class="title-img" alt="絵文字よけゲーム タイトル">
        <div class="message-box">
          ${
            isClear
            ? "🎉 おめでとう!<br>すべての障害物を避けきりました!"
            : "😢 ゲームオーバー!<br>またチャレンジしてね。"
          }
        </div>
        <button class="btn center-btn" id="back-title-btn">タイトル画面に戻る</button>
      `;
      document.getElementById('back-title-btn').onclick = showTitleScreen;
      window.onkeydown = null;
      window.onkeyup = null;
    }

    // 初期表示
    showTitleScreen();
  </script>
</body>
</html>

アルゴリズムの流れ

手順処理内容
1タイトル画面描画 → スタートボタン押下でゲーム画面へ
2showGameScreen() で初期化:プレイヤー位置、障害物配列、タイマー開始
3キー押下 (onkeydown)/離上 (onkeyup) で左右移動フラグを制御
4gameLoop():・障害物生成(時間経過で頻度UP)・障害物移動・衝突判定
5衝突なければプレイヤーと障害物をCanvas描画
6制限時間到達でクリア判定、衝突でゲームオーバー判定
7gameOver()showEndScreen() で結果表示

関数の詳細

関数名機能概要詳細説明
showTitleScreen()タイトル画面描画HTMLを出し「スタート」ボタン設置。リスナー解除、タイマー&アニメ停止。
showGameScreen()ゲーム初期化&画面描画変数初期化、Canvas要素&タイマー要素を出力。キーイベント登録。メインループ開始。
handleKeyDown/Up()プレイヤー移動フラグ制御←→キーの押下離上をフラグ(playerV)に反映。
gameLoop()ゲームループ本体障害物生成・移動・消去/衝突判定/プレイヤー移動&描画/障害物描画/クリア判定/次フレーム呼び出しの一連処理。
updateTimer()タイマー表示更新経過時間から残り時間を計算し、画面上のテキストを更新。
gameOver(isClear)終了処理ループ停止、タイマー解除、結果表示遅延呼び出し。
showEndScreen()終了画面描画クリア or ゲームオーバーのメッセージを出力し、タイトルへの戻るボタンを設置。キーイベント解除。

改造のポイント

  • 難易度調整
    GAME_TIME を短くすれば緊張感UP、長くすれば初心者向けに。
    ・障害物落下速度や生成頻度(Math.random() の閾値)をステージ経過で動的に増加させると、後半の難度が上がります。
  • グラフィック&演出
    ・Canvasの背景や🎵サウンドエフェクトを追加して、視覚・聴覚で盛り上げる。
    ・衝突時・クリア時にアニメーション(画面フラッシュ、パーティクル)を実装。
  • 操作の拡張
    ・スマホ向けに左右キーだけでなく、画面左半分タップで左、右半分で右移動などタッチ操作を追加。
  • 新モード
    ・一定時間内に何体避けられるか「サバイバルモード」や、制限時間内にスコアを競う「ポイント制モード」を実装すると、リプレイ性が高まります。

アドバイス
まずはコアの避け動作と衝突判定をブラッシュアップし、動作が安定してから難易度調整や演出強化を進めましょう。段階的にモード追加・ランキング機能を取り入れると、より長く遊ばれるゲームになります!