「JavaScriptでゲームを作ってみたいけど、何から始めたらいいのかわからない…」
そんな方におすすめなのが、今回紹介する神経衰弱ゲームの作り方です。
トランプを使った定番の記憶力ゲームを、HTML・CSS・JavaScriptの3つだけで実装できます。
カードをクリックするとひっくり返り、同じ数字を当てると固定される――
シンプルながらも、しっかり“ゲームとして遊べる”本格仕様です。
しかもこのゲームは、ひとつのHTMLファイルで完結。
コピペしてブラウザで開くだけで、すぐに動作を確認できます。
「JavaScriptでどんなふうに動きをつけるの?」「カードのランダム配置ってどうするの?」
といった疑問も、この記事を読めばすべて解決します。
後半では、カードをめくるアニメーションの作り方や
スコアをブラウザに保存する方法(localStorage)も詳しく解説します。
初心者でもゼロから完成できるステップ形式で進めていくので、
ぜひ一緒に作りながら学んでみてください。
完成イメージと仕様を確認しよう
まずは、今回作る神経衰弱ゲームの完成イメージを見てみましょう。
この記事で紹介するコードをコピペすれば、以下のような本格的なトランプゲームが動きます。
完成版を体験してみよう
ゲームの仕様まとめ
この神経衰弱ゲームでは、以下のような機能を実装しています。
カード構成
- 使用するカード:1〜10までの数字×2枚(合計20枚)
- 配置:5列×4行のグリッドレイアウト
- 裏面:クラシックなトランプ柄画像
ランダム配置
- ゲーム開始時に、カードの並びが毎回ランダムに変化します。
→ 何度遊んでも新鮮な感覚でプレイできます。
ゲームの流れ
- 「ゲームスタート」を押すと、カードがシャッフルされて並びます。
- 2枚のカードをめくり、同じ数字ならそのまま固定。
- 異なる数字なら、約1秒後に自動で裏返ります。
- すべてのペアを見つけるとゲームクリア!
ゲームの全体像を理解してからコードを読み進めると、
「どの部分で何をしているか」がぐっと分かりやすくなりますよ。
完成コードをコピペして動かそう
プログラミングの学習で大切なのは、
「動くものを体験すること」です。
まずは、完成済みの神経衰弱ゲームをそのままコピーして動かしてみましょう。
この1ステップで、HTML・CSS・JavaScriptがどんなふうに連携しているのかが自然と見えてきます。
手順①:コードをコピーしよう
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>神経衰弱ゲーム (最終決定版)</title> <style> /* --- 1. 全体デザイン & リセット --- */ body { font-family: 'Arial', sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 80vh; margin: 0; background-color: #f0f4f8; padding: 10px; } .container { width: 95%; max-width: 600px; padding: 20px; border-radius: 15px; background-color: #ffffff; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15); text-align: center; } h1 { color: #333; font-size: 1.8em; margin-bottom: 20px; } /* --- 2. 情報表示エリア --- */ .info { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; padding: 10px 15px; border: 1px solid #ddd; border-radius: 8px; background-color: #e9ecef; } #scoreDisplay, #highScoreDisplay { font-size: 1.1em; font-weight: bold; color: #007bff; } #message { font-weight: bold; color: #333; } /* --- 3. カードボード(グリッド) --- */ .memory-game { display: grid; grid-template-columns: repeat(5, 1fr); grid-template-rows: repeat(4, 1fr); gap: 8px; width: 100%; aspect-ratio: 5 / 4; margin: 0 auto; } /* --- 4. 個別カードのスタイルとアニメーション --- */ .card { perspective: 1000px; cursor: pointer; position: relative; transform-style: preserve-3d; transition: transform 0.6s; aspect-ratio: 1 / 1; } /* カードがめくられた状態 (表面が見えている状態) */ .card.flipped { transform: rotateY(180deg); pointer-events: none; width: 95%; } /* カードの表と裏 */ .card-face { position: absolute; width: 95%; height: 100%; border-radius: 5px; backface-visibility: hidden; display: flex; align-items: center; justify-content: center; font-size: clamp(1.2em, 4vw, 2em); font-weight: bold; border: 1px solid darkblue; border-radius: 5px; box-shadow: rgb(0, 0, 0, 0.2) 3px 3px; } /* カードの裏面(トランプ画像) */ .card-back { /* background-image: url('card_back.png'); 画像ファイル名を指定 */ background-color: #2c3e50; /* 画像裏面の代わりに濃い色を指定 */ background-size: cover; background-position: center; color: transparent; } /* カードの表面(数字) */ .card-front { background-color: #f8f9fa; color: #333; transform: rotateY(180deg); border: 2px solid #333; } /* マッチしたカード - 薄くする処理を削除し、表面のまま固定 */ .card.matched { pointer-events: none; /* opacityやfilterを削除 */ transform: rotateY(180deg); /* 表面を維持 */ /* transitionもシンプルに */ transition: transform 0.6s; width: 95%; } /* --- 5. ボタンエリア --- */ .button-group { display: flex; justify-content: center; gap: 10px; margin-top: 15px; } #startButton, #resetButton { padding: 10px 20px; font-size: 1.1em; cursor: pointer; border: none; border-radius: 25px; } #startButton { background-color: #28a745; color: white; } #startButton:hover:not(:disabled) { background-color: #218838; } #resetButton { background-color: #dc3545; color: white; } #resetButton:hover:not(:disabled) { background-color: #c82333; } </style> </head> <body> <div class="container"> <h1>神経衰弱ゲーム</h1> <div class="info"> <div id="highScoreDisplay">ベストスコア: -</div> <div id="message">スタートボタンを押して開始</div> <div id="scoreDisplay">ターン: 0</div> </div> <div class="memory-game" id="gameBoard"> </div> <div class="button-group"> <button id="startButton">ゲームスタート</button> <button id="resetButton" disabled>リセット</button> </div> </div> <script> // --- 1. 定数とDOM要素の定義 --- const gameBoard = document.getElementById('gameBoard'); const startButton = document.getElementById('startButton'); const resetButton = document.getElementById('resetButton'); const scoreDisplay = document.getElementById('scoreDisplay'); const highScoreDisplay = document.getElementById('highScoreDisplay'); const messageDisplay = document.getElementById('message'); const HIGHSCORE_KEY = 'memoryGameHighScore'; // --- 2. ゲームの状態変数 --- let cardsFlipped = 0; let turns = 0; let firstCard = null; let secondCard = null; let boardLock = false; let isGameActive = false; // --- 3. ユーティリティ関数 --- function loadHighScore() { const savedScore = localStorage.getItem(HIGHSCORE_KEY); return savedScore ? parseInt(savedScore, 10) : Infinity; } function generateAndShuffleCards() { let values = []; for (let i = 1; i <= 10; i++) { values.push(i, i); } for (let i = values.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [values[i], values[j]] = [values[j], values[i]]; } return values; } // --- 4. ゲームボードの構築 --- function createBoard(values) { gameBoard.innerHTML = ''; values.forEach(value => { const card = document.createElement('div'); card.classList.add('card'); card.dataset.value = value; const cardFront = document.createElement('div'); cardFront.classList.add('card-face', 'card-front'); cardFront.textContent = value; const cardBack = document.createElement('div'); cardBack.classList.add('card-face', 'card-back'); card.appendChild(cardFront); card.appendChild(cardBack); card.addEventListener('click', flipCard); gameBoard.appendChild(card); }); } // --- 5. ゲームロジック本体 --- function flipCard(event) { if (!isGameActive || boardLock) return; const card = event.currentTarget; if (card.classList.contains('flipped')) return; card.classList.add('flipped'); if (!firstCard) { firstCard = card; messageDisplay.textContent = '2枚目を選んでください'; return; } secondCard = card; turns++; scoreDisplay.textContent = `ターン: ${turns}`; boardLock = true; messageDisplay.textContent = '判定中...'; checkForMatch(); } function checkForMatch() { const isMatch = firstCard.dataset.value === secondCard.dataset.value; if (isMatch) { matchCards(); } else { unflipCards(); } } function matchCards() { // マッチしたカードはflipped状態のままmatchedクラスを追加し、固定 firstCard.classList.add('matched'); secondCard.classList.add('matched'); firstCard.removeEventListener('click', flipCard); secondCard.removeEventListener('click', flipCard); resetBoard(); cardsFlipped++; messageDisplay.textContent = 'マッチ成功!'; if (cardsFlipped === 10) { gameClear(); } } function unflipCards() { // 1秒後に裏返す(非同期処理) setTimeout(() => { firstCard.classList.remove('flipped'); secondCard.classList.remove('flipped'); resetBoard(); messageDisplay.textContent = '不一致...もう一度!'; }, 1000); } function resetBoard() { [firstCard, secondCard] = [null, null]; boardLock = false; } function gameClear() { isGameActive = false; messageDisplay.textContent = `ゲームクリア!${turns}ターンで完了!`; startButton.disabled = false; resetButton.disabled = true; startButton.textContent = 'もう一度プレイ'; // ハイスコアの更新 const currentHighScore = loadHighScore(); if (turns < currentHighScore) { localStorage.setItem(HIGHSCORE_KEY, turns); highScoreDisplay.textContent = `ベストスコア: ${turns} (新記録!)`; } else { highScoreDisplay.textContent = `ベストスコア: ${currentHighScore}`; } } // --- 6. 初期化とゲーム開始/リセット --- function initializeGame() { const currentHighScore = loadHighScore(); highScoreDisplay.textContent = `ベストスコア: ${currentHighScore === Infinity ? '-' : currentHighScore}`; const initialValues = generateAndShuffleCards(); createBoard(initialValues); startButton.addEventListener('click', startGame); resetButton.addEventListener('click', resetGame); } function startGame() { isGameActive = true; turns = 0; cardsFlipped = 0; resetBoard(); scoreDisplay.textContent = `ターン: ${turns}`; startButton.disabled = true; resetButton.disabled = false; messageDisplay.textContent = 'ゲーム開始!カードをめくってください'; const shuffledValues = generateAndShuffleCards(); createBoard(shuffledValues); } function resetGame() { startGame(); messageDisplay.textContent = 'ゲームをリセットしました'; } // ページロード時にゲームを初期化 window.onload = initializeGame; </script> </body> </html> |
手順②:ファイルを保存しよう
- メモ帳やVS Codeなどのエディタを開きます。
- コピーしたコードを貼り付けて、
ファイル名をindex.htmlにして保存します。 - 保存場所は、デスクトップやわかりやすいフォルダでOKです。
拡張子が .html になっていればOKです。
もし .txt のままだとブラウザで動かないので注意してくださいね。
手順③:ブラウザで開こう
保存した index.html をダブルクリックして開いてみましょう。
Google Chrome・Edge・Safariなど、どのブラウザでも動作します。
HTMLを理解しよう
神経衰弱ゲームは、HTML・CSS・JavaScript の3つの技術でできています。
そのうち、HTMLはゲーム全体の“骨組み”を作る役割を持っています。
まずは、完成コードの中からHTML部分を抜き出して見てみましょう
各パーツの役割を見てみよう
| 要素 | 内容・役割 |
|---|---|
<div class="container"> | ゲーム全体をまとめる枠。中央寄せのレイアウトに使われます。 |
<h1> | ページタイトル。「神経衰弱ゲーム」と表示。 |
<div class="info"> | 現在のターン数とベストスコアを表示する情報エリア。 |
<div class="button-group"> | 「ゲームスタート」と「リセット」ボタンを配置する部分。 |
<div class="memory-game"> | 実際のカードを並べるボード。JavaScriptで自動的にカードが追加されます。 |
<div class="memory-game"> の中身は最初は空なんです。
JavaScriptがゲーム開始時にここへカードを自動生成してくれます。
ポイント解説
- HTMLは「見た目」ではなく「構造」を定義する
- どこにスコアが出るのか、どこにカードが並ぶのかを指定する。
- デザインや動きは別パート(CSS/JavaScript)が担当。
- クラス名とID名をわかりやすく
message、startButton、resetButtonのように、
名前だけで“何の役割か”が分かるようにするのが大切です。
- HTMLはゲームの「設計図」
- これをベースに、CSSで装飾し、JavaScriptで動かす。
- どの要素に動きをつけるかは、ここで付けた
idやclassを使って指定します。
HTMLを読めるようになると、「このボタンがどこにつながってるのか」「どの部分がスコアなのか」がすぐ分かります。
コードが長くなっても、構造を意識して書けば迷わなくなりますよ。
CSSでカードとレイアウトをデザインしよう
HTMLで骨組みができたら、次は見た目を整えましょう。
ここでは、カードの並び・デザイン・アニメーションを作るためにCSSを使います。
レイアウトの仕組みを理解しよう
カードの並び部分は、CSSの グリッドレイアウト でカードを整列させています。
| プロパティ | 内容 |
|---|---|
display: grid; | 要素を格子状に並べるモードにする。 |
grid-template-columns: repeat(5, 1fr); | 横に5枚並べる。 |
gap: 8px; | カード間のすき間を8pxに設定。 |
カードのデザインポイント
カード部分は .card クラスで定義されています。
今回のCSSではトランプの裏面に画像を使っていませんが、background-color 部分を変更することで、画像を使用することができます。
この部分を以下のように変更するだけでOKです
background-color: #2c3e50;
▼
background-image: url('card_back.png'); /* 画像ファイル名を指定 */
アニメーションのポイント
transform: rotateY(180deg); は、要素をY軸方向に回転させる指定です。
まさに“カードを裏返す”動きにピッタリなんです。
JavaScriptでゲームを動かそう
ハイスコアの読み込み
|
1 2 3 4 |
function loadHighScore() { const savedScore = localStorage.getItem(HIGHSCORE_KEY); return savedScore ? parseInt(savedScore, 10) : Infinity; } |
保存がなければ Infinity を返す設計にすることで、「初回はどんなスコアでも“最小”として更新可能」にしています。
カードの生成&シャッフル
|
1 2 3 4 5 6 7 8 |
function generateAndShuffleCards() { ・ for (let i = values.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [values[i], values[j]] = [values[j], values[i]]; } ・ } |
- 重複なしで均等にランダム化できるシャッフル。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function createBoard(values) { gameBoard.innerHTML = ''; values.forEach(value => { const card = document.createElement('div'); card.classList.add('card'); card.dataset.value = value; const cardFront = document.createElement('div'); cardFront.classList.add('card-face', 'card-front'); cardFront.textContent = value; const cardBack = document.createElement('div'); cardBack.classList.add('card-face', 'card-back'); card.appendChild(cardFront); card.appendChild(cardBack); card.addEventListener('click', flipCard); gameBoard.appendChild(card); }); } |
- 1枚のカードに表(
.card-front)と裏(.card-back)の2面を持たせています。 - 表面は
textContent = valueで数字を表示。 - クリックはカード本体
.cardにバインド(flipCard)。
一致判定と結果反映
|
1 2 3 4 5 |
function checkForMatch() { const isMatch = firstCard.dataset.value === secondCard.dataset.value; if (isMatch) matchCards(); else unflipCards(); } |
- 1枚目と2枚目の数字一致をチェック。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
function matchCards() { firstCard.classList.add('matched'); secondCard.classList.add('matched'); firstCard.removeEventListener('click', flipCard); secondCard.removeEventListener('click', flipCard); resetBoard(); cardsFlipped++; // ペア数インクリメント messageDisplay.textContent = 'マッチ成功!'; if (cardsFlipped === 10) gameClear(); // 10ペア=クリア } |
- そろったカードは
matchedを付与して固定、クリックも解除。 cardsFlippedが 10(= 10ペア)に達したらクリア。
|
1 2 3 4 5 6 7 8 |
function unflipCards() { setTimeout(() => { firstCard.classList.remove('flipped'); secondCard.classList.remove('flipped'); resetBoard(); messageDisplay.textContent = '不一致...もう一度!'; }, 1000); } |
- 不一致は1秒後に表を戻す(
setTimeoutで演出と誤操作防止)。
スコア保存
|
1 2 3 4 5 6 7 8 9 10 11 |
function gameClear() { ・ ・ const currentHighScore = loadHighScore(); if (turns < currentHighScore) { localStorage.setItem(HIGHSCORE_KEY, turns); highScoreDisplay.textContent = `ベストスコア: ${turns} (新記録!)`; } else { highScoreDisplay.textContent = `ベストスコア: ${currentHighScore}`; } } |
- ハイスコアは
localStorageに最小値として保存・表示します。
アレンジしてオリジナル神経衰弱に挑戦!
基本の神経衰弱ゲームが完成したら、
次は少し手を加えて自分だけのオリジナル版に進化させてみましょう。
ちょっとした見た目の変更や機能追加だけでも、
まったく新しいゲーム体験になります。
裏面デザインを変えてみよう
現在のコードでは、カードの裏面をシンプルな色で表現していますが、
画像を使えばトランプのような本格的な雰囲気にできます。
|
1 2 3 4 5 |
.card-back { background-image: url('card_back.png'); background-size: cover; background-position: center; } |
card_back.png を同じフォルダに置いておけば、裏面が一気に華やかになりますよ!
カード枚数を増減して難易度を変える
generateAndShuffleCards() 関数の以下の部分を変更すれば、
カードの枚数(ペア数)を調整できます。
|
1 2 3 |
for (let i = 1; i <= 8; i++) { values.push(i, i); // 1〜8までのペア } |
数字を10から8に減らして難易度を簡単にする。
逆に10から12に増やして難易度を上げる。
事が可能です。
難易度ボタンを追加して、「初級」「中級」「上級」ごとにペア数を自動切り替えるのも面白いですよ。
効果音を追加して臨場感アップ
クリックやマッチのタイミングに効果音を入れると、
一気に“遊んでいる感”が高まります。
|
1 2 3 |
const flipSound = new Audio('flip.mp3'); flipSound.play(); // カードをめくった時の音 |
効果音はゲーム体験をぐっと豊かにします。
MP3ファイルを同じフォルダに置くだけで簡単に使えますよ。
小さな工夫の積み重ねで、同じゲームがどんどん楽しく進化していきます。
ぜひ自分だけの“オリジナル神経衰弱”を作ってみてくださいね。
おすすめの学習リソース|もっとスキルを伸ばしたいあなたへ
「神経衰弱ゲーム」を完成させたことで、あなたはすでに HTML・CSS・JavaScriptの基礎 に触れました。
ここからさらにステップアップするために役立つ学習リソースを紹介します。
おすすめ書籍
『ゲームで学ぶJavaScript入門 増補改訂版 ~ブラウザゲームづくりでHTML&CSSも身につく!』
- ゲームを作りながら学べるので、退屈せずにJavaScriptの基礎が身につく
- HTML・CSS・JavaScriptの基本を一冊で網羅、Web制作の土台が自然に理解できる
- 13本のサンプルゲームを実際に動かせるから「作れる喜び」が味わえる
- 初心者・中高生にもやさしい解説で、初めてのゲームプログラミングに最適
『スラスラわかるHTML&CSSのきほん』(SBクリエイティブ)
- Web制作初心者に最適な入門書。HTMLとCSSの仕組みをやさしく解説。
- これからWebページを作ってみたい方にぴったり。
- レイアウトの基本やスタイルの調整方法など、実践的に学べます。
オンライン講座編
Udemy|世界最大級のオンライン学習プラットフォーム
世界中で利用されるオンライン学習サイト。
HTML、CSS、JavaScriptの入門から応用まで、高評価の講座が数百種類揃っています。
初心者でも動画を見ながら手を動かせるので、挫折しにくいのが魅力です。
まとめ|HTMLとJavaScriptで作る神経衰弱ゲームの魅力
ここまでの手順で、
HTML・CSS・JavaScriptだけで動く本格的な神経衰弱ゲームを完成させることができました
たった1つのHTMLファイルで、
- カードのシャッフル
- フリップアニメーション
- 一致判定
- リセットボタン
- ハイスコア保存
といった複数の仕組みを実装できたのは大きな成果です。
今回のように、1つの完成品を作りながら覚えると、
“コードを書く意味”が自然と理解できます。
ゲームは学習のモチベーションを高める最高の題材です。
小さな成功を積み重ねていくことが、スキルアップの一番の近道です。
ぜひ、次はあなたのアイデアで“世界に一つだけのWebゲーム”を作ってみてください!
関連記事
【実際に遊べる神経衰弱ゲームはこちら。ベストスコア更新を目指して挑戦してみよう!】▼
【作り方記事】記憶力テストゲームの作り方|HTMLとJavaScriptで簡単作成!遊べる脳トレアプリ▼
【作り方記事】タイピング練習ゲームの作り方|HTMLとJavaScriptで初心者でもできる!▼




