
【ゲーム】JavaScript:27 ワードスクランブルゲーム
「ワードスクランブルゲーム」は、あらかじめ用意された単語をランダムに文字単位でシャッフルし、カード風のUIアニメーションで見せた後にプレイヤーが正しい単語を入力して当てる記憶+語彙クイズです。全単語数分のラウンドに挑戦し、何問正解できるかを競います。
遊び方と操作方法
- タイトル画面の「スタート 🚀」ボタンをクリック
- カードがランダムに「めくり・戻し」アニメーションでシャッフルされる様子を見る
- 表示されたシャッフル後の文字配列をもとに、入力欄に正しい単語を入力
- 「判定 ✔️」ボタン、または Enter キーで回答を確定
- 正解/不正解のフィードバックが表示されたあと、次のラウンドへ自動で移行
- 全ラウンド終了後、最終的な正解数が結果画面に表示
ルール
- 使用単語は
WORDS
配列に定義された全5語 - 毎ラウンド、未出題の単語から順に出題
- 各単語は文字単位でシャッフルし、同じ長さのカードを並べて表示
- プレイヤーはシャッフル後の文字順を見て正解単語を入力
- 正解数をスコアとしてカウント
- 最終ラウンド終了後に「正解数 / 総ラウンド数」を表示
🎮ゲームプレイ
以下のリンク先から実際にプレイできます。
27 ワードスクランブルゲーム
素材のダウンロード
以下のリンクから使用する素材をダウンロードできます。
word_scramble_title.png | word_scramble_bg.png |
---|---|
![]() | ![]() |
ゲーム画面イメージ

プログラム全文(word_scramble.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>🃏 ワードスクランブルゲーム 🃏</title>
<style>
/* ===== 全体背景設定 ===== */
body {
margin: 0;
padding: 0;
font-family: 'Arial', sans-serif;
/* 背景画像を固定で全面に表示 */
background: url('word_scramble_bg.png') no-repeat center center fixed;
background-size: cover;
}
/* ===== 中央オーバーレイ共通スタイル ===== */
.overlay {
position: absolute;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(255, 255, 255, 0.9);
padding: 20px;
border-radius: 10px;
text-align: center;
width: 90%; max-width: 500px;
}
/* ===== 非表示クラス ===== */
.hidden { display: none; }
/* ===== タイトル画像 ===== */
.title-image {
display: block;
margin: 0 auto 10px;
max-width: 80%; height: auto;
}
/* ===== 汎用ボタン ===== */
.btn {
display: inline-block;
padding: 12px 24px;
font-size: 18px;
font-weight: bold;
margin: 15px 5px 0;
cursor: pointer;
border: 2px solid #007BFF;
border-radius: 8px;
background-color: #007BFF;
color: #fff;
box-shadow: 0 4px 6px rgba(0,0,0,0.2);
transition: background-color 0.2s, transform 0.1s;
}
.btn:hover { background-color: #0056b3; transform: translateY(-2px); }
.btn:active { transform: translateY(0); box-shadow: 0 2px 4px rgba(0,0,0,0.2); }
/* ===== カード表示(トランプ風) ===== */
#cards-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
margin: 20px 0;
}
.card {
width: 60px;
height: 80px;
perspective: 600px;
}
.card-inner {
position: relative;
width: 100%; height: 100%;
transform-style: preserve-3d;
transition: transform 0.6s;
}
/* 裏面:カード裏地 */
.card-back, .card-front {
position: absolute;
width: 100%; height: 100%;
backface-visibility: hidden;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
font-weight: bold;
}
.card-back {
background-color: #444;
}
.card-front {
background-color: #fff;
color: #000;
transform: rotateY(180deg);
border: 2px solid #000;
}
/* flipped クラスで表面表示 */
.card.flipped .card-inner {
transform: rotateY(180deg);
}
/* ===== 入力フィールド ===== */
#guess-input {
font-size: 18px;
padding: 8px;
width: 200px;
text-align: center;
border: 2px solid #343a40;
border-radius: 5px;
}
/* ===== メッセージ ===== */
.message {
margin-top: 15px;
padding: 10px;
background-color: rgba(0,0,0,0.7);
color: #fff;
border-radius: 5px;
min-height: 1.5em;
}
/* ===== 正答数表示 ===== */
.score-display {
margin-top: 10px;
font-size: 18px;
}
</style>
</head>
<body>
<!-- タイトル画面 -->
<div id="title-screen" class="overlay">
<img src="word_scramble_title.png" alt="ワードスクランブルタイトル" class="title-image">
<h1>🃏 ワードスクランブルゲーム 🃏</h1>
<p>コンピューターが単語を選び、文字をランダムにシャッフルします。</p>
<p>カードのような札が回転してシャッフルされる様子を見て、</p>
<p>正しい単語を入力して当ててください。</p>
<button id="start-btn" class="btn">スタート 🚀</button>
</div>
<!-- ゲーム画面 -->
<div id="game-screen" class="overlay hidden">
<!-- カード群表示 -->
<div id="cards-container"></div>
<!-- ユーザー入力欄と判定ボタン -->
<input type="text" id="guess-input" placeholder="単語を入力">
<button id="submit-btn" class="btn">判定 ✔️</button>
<!-- フィードバックメッセージ -->
<div id="message" class="message">準備中…</div>
<!-- 現在のスコア表示 -->
<div class="score-display">正解数: <span id="score">0</span></div>
</div>
<!-- 終了画面 -->
<div id="end-screen" class="overlay hidden">
<h2>ゲーム終了 🎉</h2>
<p id="final-message">正解数: <span id="final-score">0</span> / <span id="total-rounds">0</span></p>
<button id="restart-btn" class="btn">タイトルに戻る 🔄</button>
</div>
<script>
// ================================================
// 単語リスト設定
// ================================================
const WORDS = [
'APPLE', 'BANANA', 'ORANGE', 'GRAPE', 'MANGO'
];
const TOTAL_ROUNDS = WORDS.length; // ラウンド数
// ================================================
// ゲーム状態変数
// ================================================
let currentRound = 0;
let score = 0;
let currentWord = '';
let shuffled = [];
// ================================================
// DOM要素取得
// ================================================
const titleScreen = document.getElementById('title-screen');
const gameScreen = document.getElementById('game-screen');
const endScreen = document.getElementById('end-screen');
const startBtn = document.getElementById('start-btn');
const submitBtn = document.getElementById('submit-btn');
const restartBtn = document.getElementById('restart-btn');
const cardsContainer = document.getElementById('cards-container');
const guessInput = document.getElementById('guess-input');
const messageDiv = document.getElementById('message');
const scoreSpan = document.getElementById('score');
const finalScore = document.getElementById('final-score');
const totalRounds = document.getElementById('total-rounds');
/**
* タイトル画面表示
*/
function showTitle() {
titleScreen.classList.remove('hidden');
gameScreen.classList.add('hidden');
endScreen.classList.add('hidden');
}
/**
* ゲーム開始処理
*/
function startGame() {
// 状態リセット
currentRound = 0;
score = 0;
scoreSpan.textContent = score;
totalRounds.textContent = TOTAL_ROUNDS;
titleScreen.classList.add('hidden');
endScreen.classList.add('hidden');
gameScreen.classList.remove('hidden');
nextRound(); // ラウンド開始
}
/**
* 次のラウンド準備
*/
function nextRound() {
if (currentRound >= TOTAL_ROUNDS) {
endGame();
return;
}
// 単語決定とシャッフル
currentWord = WORDS[currentRound];
shuffled = shuffleArray(currentWord.split(''));
// カード要素生成
renderCards();
// アニメーション実行
animateShuffle(() => {
// 終了後入力受付
messageDiv.textContent = '単語を入力して「判定」を押してください';
guessInput.value = '';
guessInput.focus();
});
}
/**
* 配列をシャッフル
*/
function shuffleArray(arr) {
const a = arr.slice();
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a;
}
/**
* カード要素をDOMに描画
*/
function renderCards() {
cardsContainer.innerHTML = '';
shuffled.forEach(letter => {
// card要素構築
const card = document.createElement('div'); card.className = 'card';
const inner = document.createElement('div'); inner.className = 'card-inner';
const back = document.createElement('div'); back.className = 'card-back';
const front = document.createElement('div'); front.className = 'card-front';
front.textContent = letter;
inner.append(back, front);
card.append(inner);
cardsContainer.append(card);
});
messageDiv.textContent = 'シャッフル中…';
}
/**
* シャッフルアニメーション
* @param {Function} callback アニメ完了後に実行
*/
function animateShuffle(callback) {
const cards = Array.from(document.querySelectorAll('.card'));
let count = 0;
const maxFlips = 10;
const interval = setInterval(() => {
// ランダムなカードをひっくり返す
const idx = Math.floor(Math.random() * cards.length);
cards[idx].classList.toggle('flipped');
count++;
if (count >= maxFlips) {
clearInterval(interval);
// 全て表側に
cards.forEach(c => c.classList.add('flipped'));
setTimeout(callback, 600);
}
}, 150);
}
/**
* ユーザー判定処理
*/
function handleGuess() {
const guess = guessInput.value.trim().toUpperCase();
if (!guess) return;
if (guess === currentWord) {
score++;
scoreSpan.textContent = score;
messageDiv.textContent = '正解!';
} else {
messageDiv.textContent = `不正解… 正解: ${currentWord}`;
}
currentRound++;
// 次ラウンドへ
setTimeout(nextRound, 1000);
}
/**
* ゲーム終了処理
*/
function endGame() {
gameScreen.classList.add('hidden');
finalScore.textContent = score;
endScreen.classList.remove('hidden');
}
// ================================================
// イベントリスナー設定
// ================================================
document.addEventListener('DOMContentLoaded', () => {
startBtn.addEventListener('click', startGame);
submitBtn.addEventListener('click', handleGuess);
guessInput.addEventListener('keyup', e => {
if (e.key === 'Enter') handleGuess();
});
restartBtn.addEventListener('click', showTitle);
showTitle();
});
</script>
</body>
</html>
アルゴリズムの流れ
ステップ | 処理内容 | 主な関数・命令 |
---|---|---|
1. タイトル表示 | タイトル画面を表示し、他画面を非表示 | showTitle() |
2. ゲーム開始 | 状態リセット・画面切替・第1ラウンド呼び出し | startGame() |
3. 単語シャッフル | currentWord を文字配列に→shuffleArray() でランダム並び替え | shuffleArray() |
4. カード描画 | .card 要素を生成し、裏面と表面(文字)を構築 | renderCards() |
5. シャッフル演出 | setInterval でランダムにflipped クラスを付け外しする | animateShuffle() |
6. 入力判定 | 入力値を大文字化し正解と比較→スコア更新&フィードバック表示 | handleGuess() |
7. 次ラウンドor終了 | currentRound を進め、終了判定→nextRound() or endGame() 呼出 | handleGuess() → endGame() |
関数の詳しい解説
関数名 | 説明 |
---|---|
showTitle() | 各画面の .hidden クラスを制御し、タイトル画面のみを表示 |
startGame() | 変数 (currentRound , score ) をリセット、スコア欄/総ラウンド数表示を初期化、ゲーム画面へ移行 |
shuffleArray(arr) | Fisher–Yates アルゴリズムで配列要素をランダムに並び替え |
renderCards() | .card 要素を生成し、シャッフル済み文字を表面にセット |
animateShuffle(cb) | 指定回数ランダムなカードに flipped クラスを付与し、裏表をひっくり返してシャッフル演出 |
handleGuess() | 入力欄の文字列を大文字化して正解と比較、スコア加算/フィードバック表示、次ラウンド呼出 |
endGame() | ゲーム画面を非表示、終了画面を表示し最終スコアを更新 |
改造のポイント
- 単語リスト追加
WORDS
配列に単語を追加すれば出題語が増加。多様なジャンルに対応可能。 - カード枚数変更
単語長に応じて.card
サイズを動的調整し、長い単語も見切れずに表示できるように。 - アニメーション強化
CSS keyframes や JavaScript でカードの回転速度・色変化をカスタムして、より派手に。 - 制限時間機能
各ラウンドにタイマーを設置し、時間内に回答しないと不正解扱いにして緊張感をアップ。 - ハイスコア記録
localStorage
に正解数やクリアタイムを保存し、タイトル画面でハイスコアを表示。
アドバイス:カードをクリック式にしたり、ドラッグ&ドロップで文字を並べ替えるインタラクションを追加すると、より直感的でゲーム性の高いUIになります。さらに、正解時にアニメーションや効果音を足すと、爽快感が増してリプレイ性が向上します!