
【ゲーム】JavaScript:14 ペアマッチゲーム
「🎯 ペアマッチゲーム 🎯」は、裏返しのカードから同じ絵文字をペアでめくり、できるだけ早くすべてのペアを揃えてクリアを目指す、一人用の神経衰弱系ゲームです。
遊び方と操作方法
- 自動でゲーム開始
ページ読み込み時または「🔄再挑戦」ボタン押下でゲームが初期化され、8種類×2枚ずつ、合計16枚のカードがシャッフルされて4×4のグリッドに裏向きで配置されます。 - カードをめくる
カードをクリックすると表向きになり、絵文字が表示されます。 - ペア判定
・1枚目をめくった後、もう1枚をめくります。
・同じ絵文字ならペア成功→カードに「matched」クラスが付き、緑色に固定。
・違う絵文字なら1秒後に自動で裏返し。 - タイマーとスコア
・クリックと同時に経過タイマーがスタート(秒表示)。
・すべてのペアを揃えたタイミングでタイマーを停止し、経過秒数をスコアとして記録します。 - スコアボード
クリアタイム(秒)がlocalStorage
に保存され、昇順でハイスコア一覧として表示されます。
ルール
- 合計16枚(8ペア)のカードをめくってペアを探します。
- 連続で正解(ペア成功)してもボーナスはありません。
- ペアをすべて揃えた瞬間にゲームクリア。
- タイマーが動き続けるため、いかに早くすべてのペアを揃えるかが勝負です。
🎮ゲームプレイ
以下のリンク先から実際にプレイできます。
14 ペアマッチゲーム
素材のダウンロード
以下のリンクから使用する素材をダウンロードできます。
pairs_match_bg.png |
---|
![]() |
ゲーム画面イメージ

プログラム全文(pairs_match.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>
/* 背景画像を往復スクロールする設定 */
body {
font-family: Arial, sans-serif;
text-align: center;
margin: 0;
padding: 0;
background: url('pairs_match_bg.png') repeat-x 0 0 fixed;
background-size: cover;
animation: scrollBG 30s linear infinite;
color: #fff;
}
@keyframes scrollBG {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* タイトル */
h1 {
margin-top: 20px;
font-size: 32px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
}
/* タイマー表示 */
#timer {
margin-top: 15px;
font-size: 24px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
}
/* メッセージエリア */
.message-area {
margin-top: 20px;
font-size: 28px;
color: #ffeb3b;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
}
/* ゲームボード */
.game-board {
display: grid;
grid-template-columns: repeat(4, 150px);
gap: 10px;
justify-content: center;
margin: 20px auto;
}
/* カード */
.card {
width: 150px;
height: 150px;
background-color: #007acc;
color: white;
font-size: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
cursor: pointer;
user-select: none;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: scale(1.1);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
}
.flipped {
background-color: #fff;
color: #000;
cursor: default;
}
.matched {
background-color: #32cd32;
color: white;
cursor: default;
}
/* 再挑戦ボタン */
.retry-btn {
margin-top: 20px;
padding: 12px 24px;
background: linear-gradient(45deg, #4caf50, #2e7d32);
color: #fff;
border: none;
border-radius: 8px;
font-size: 20px;
font-weight: bold;
cursor: pointer;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
transition: transform 0.2s, box-shadow 0.2s;
}
.retry-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.4);
}
/* スコアボード */
.scoreboard {
margin-top: 30px;
}
.scoreboard h2 {
font-size: 24px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7);
}
.scoreboard ul {
list-style: none;
padding: 0;
color: #fff;
font-size: 18px;
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.7);
}
</style>
</head>
<body>
<h1>🎯ペアマッチゲーム🎯</h1>
<div id="timer">⏱経過時間: 0秒</div>
<div id="messageArea" class="message-area"></div>
<div class="game-board" id="gameBoard"></div>
<button class="retry-btn" id="retryButton">🔄再挑戦</button>
<div class="scoreboard">
<h2>📊スコアボード📊</h2>
<ul id="scoreList"></ul>
</div>
<script>
// --- 要素取得 ---
const gameBoard = document.getElementById('gameBoard');
const retryButton = document.getElementById('retryButton');
const timerDisplay = document.getElementById('timer');
const scoreList = document.getElementById('scoreList');
const messageArea = document.getElementById('messageArea');
// --- カードの種類(絵文字) ---
const cardValues = ['🍎', '🍌', '🍇', '🍒', '🍓', '🍍', '🥝', '🍋'];
// --- ゲームの状態管理変数 ---
let firstCard = null; // 1枚目にめくったカード
let secondCard = null; // 2枚目にめくったカード
let preventClick = false; // クリックを一時停止するフラグ
let timer; // setInterval の ID
let elapsedTime = 0; // 経過秒数
let matchedPairs = 0; // 揃ったペアの数
/**
* タイマーをスタートする
*/
function startTimer() {
elapsedTime = 0;
timerDisplay.textContent = `⏱経過時間: ${elapsedTime}秒`;
timer = setInterval(() => {
elapsedTime++;
timerDisplay.textContent = `⏱経過時間: ${elapsedTime}秒`;
}, 1000);
}
/**
* タイマーをストップする
*/
function stopTimer() {
clearInterval(timer);
}
/**
* スコアをローカルに保存する
*/
function saveScore() {
const scores = JSON.parse(localStorage.getItem('pairMatchScores')) || [];
scores.push(elapsedTime);
localStorage.setItem('pairMatchScores', JSON.stringify(scores));
updateScoreboard();
}
/**
* スコアボードを更新して表示する
*/
function updateScoreboard() {
const scores = JSON.parse(localStorage.getItem('pairMatchScores')) || [];
scoreList.innerHTML = scores
.sort((a, b) => a - b)
.map(score => `<li>${score}秒</li>`)
.join('');
}
/**
* ゲームを初期化・開始する
*/
function initializeGame() {
// メッセージをクリア
messageArea.textContent = '';
// ボードをクリア
gameBoard.innerHTML = '';
matchedPairs = 0;
// タイマー再スタート
stopTimer();
startTimer();
// カードをペアで用意してシャッフル
let cards = [...cardValues, ...cardValues];
cards.sort(() => 0.5 - Math.random());
// カード要素を生成
cards.forEach(value => {
const card = document.createElement('div');
card.classList.add('card');
card.dataset.value = value;
card.textContent = ''; // めくる前は空
gameBoard.appendChild(card);
// クリック時の処理
card.addEventListener('click', () => {
// 既に2枚めくっている or 同じカード or マッチ済みは無視
if (preventClick || card.classList.contains('flipped') || card.classList.contains('matched')) return;
// カードをめくる
card.textContent = card.dataset.value;
card.classList.add('flipped');
if (!firstCard) {
// 最初のカードを保持
firstCard = card;
} else {
// 2枚目のカードを保持
secondCard = card;
preventClick = true;
// ペア判定
if (firstCard.dataset.value === secondCard.dataset.value) {
// マッチ成功
firstCard.classList.add('matched');
secondCard.classList.add('matched');
matchedPairs++;
// 全ペアクリア時の処理
if (matchedPairs === cardValues.length) {
stopTimer();
saveScore();
messageArea.textContent = `🎉ゲームクリア!経過時間: ${elapsedTime}秒🎉`;
}
resetSelection();
} else {
// 不一致なら1秒後に裏返す
setTimeout(() => {
firstCard.textContent = '';
secondCard.textContent = '';
firstCard.classList.remove('flipped');
secondCard.classList.remove('flipped');
resetSelection();
}, 1000);
}
}
});
});
}
/**
* カード選択状態をリセット
*/
function resetSelection() {
firstCard = null;
secondCard = null;
preventClick = false;
}
// 「再挑戦」ボタンでゲーム再スタート
retryButton.addEventListener('click', initializeGame);
// 初回ロード時にゲームを開始&スコアボードを更新
initializeGame();
updateScoreboard();
</script>
</body>
</html>
アルゴリズムの流れ
ステップ | 関数/処理 | 詳細 |
---|---|---|
ゲーム開始・初期化 | initializeGame() | ペア用カード16枚生成→シャッフル→盤面HTML再構築→タイマー初期化→変数リセット |
タイマー開始 | startTimer() | setInterval で1秒ごとに elapsedTime++ →表示更新 |
カードクリック | クリックイベントハンドラ | 未めくりかつ2枚未選択なら絵文字表示&flipped クラス追加 |
ペア判定 | if (firstCard && secondCard) | 2枚めくり後に値を比較→一致なら matched クラス → ペアカウント++ → 全ペアならクリア処理 |
裏返し | setTimeout(...,1000) | 不一致時、1秒待ってから textContent='' +flipped 削除 |
ペア成功時 | matchedPairs++ | カウントし、必要数到達で stopTimer() & saveScore() →結果メッセージ |
スコア保存・表示 | saveScore() , updateScoreboard() | localStorage へ経過秒を蓄積→降順ソートしてリストに描画 |
再挑戦 | ボタン押下で initializeGame() | 再初期化・再挑戦 |
関数の詳細
関数名 | 役割 |
---|---|
initializeGame() | ゲームを初期化しカード配置・タイマー再スタート・変数のクリアを行う |
startTimer() | elapsedTime を秒単位でカウントし、画面上に経過時間を表示 |
stopTimer() | clearInterval でタイマーを停止 |
saveScore() | localStorage にクリアタイムを配列保存し、スコアボード更新を呼び出す |
updateScoreboard() | 保存されたスコアを取得し昇順ソート→HTMLリストに描画 |
カードクリックハンドラ | カードをめくって flipped クラスを追加→2枚めくり後にペア判定→一致時/不一致時の処理 |
resetSelection() | firstCard , secondCard , preventClick をリセット |
このゲームの改造ポイント
- 難易度調整:カードの種類数(ペア数)やグリッドサイズ(4×4→6×6等)を変更し、難易度を上げる。
- 制限時間バリエーション:制限時間を設定し、クリア条件を「時間内に全ペア」または「何ペア揃えられるか」に変更。
- サウンドエフェクト:めくる音、ペア成功音、失敗音などの効果音を追加して臨場感アップ。
- テーマ切替:絵文字以外にキャラクターや写真をカードに使用するカスタムデッキ機能を実装。
- マルチプレイヤーモード:複数人が交互にめくる対戦モードを追加し、得点制バトルに拡張。
- アニメーション強化:カードめくり時に3D回転エフェクト、ペア成功時のアニメーションを追加。
- デイリーチャレンジ:毎日異なるシャッフルシードを生成し、共通タイムでランキングを競う機能。
この基本構造をベースに、ぜひオリジナル要素や演出を追加して、魅力的な「ペアマッチ」ゲームに仕上げてみてください!