【ゲーム】JavaScript:61 カップシャッフルゲーム
目次
Toggle【ゲーム】JavaScript:61 カップシャッフルゲーム
「🥤 カップシャッフルゲーム 🥤」は、ボールを隠したカップを高速でシャッフルし、どのカップにボールが入っているかを当てる集中力テスト・マインドゲームです。カップの動きをしっかり目で追い、タイミングよくクリックして正解を狙います。
遊び方・操作方法- タイトル画面で「スタート」ボタンをクリック。
- ゲーム画面では、まずカップの下にボール(🔴)を一瞬だけ表示。
- ボールを隠すと同時にカップが自動でシャッフルを開始します(12回ランダムスワップ)。
- シャッフルが完了したら、「どのカップだ?」というメッセージに切り替わり、カップをクリックして選択。
- 正解なら「正解!」、不正解なら「残念!」が表示され、タイトル画面に戻ります。
- カップ数:3個
- シャッフル回数:12手順
- シャッフル速度:1ステップあたり約0.54秒のアニメーション
- 正解判定:プレイヤーがクリックしたカップIDと実際にボールを持つカップIDが一致
- 終了:一発勝負。正解/不正解後に結果画面へ。
以下のリンク先から実際にプレイできます。
61 カップシャッフルゲーム
素材のダウンロード以下のリンクから使用する素材をダウンロードできます。
js-cup_shuffleダウンロードcup_shuffle_title.pngcup_shuffle_bg.pngゲーム画面イメージプログラム全文(cup_shuffle.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('cup_shuffle_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.94); border-radius: 22px; box-shadow: 0 4px 24px rgba(0,0,0,0.13); padding-bottom: 32px; min-height: 500px; 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: 250px; padding: 16px 0 20px 0; user-select: none; } .cups-area { width: 600px; height: 160px; margin: 32px auto 16px auto; position: relative; } .cup { width: 120px; height: 130px; background: #fffbe6; border: 3px solid #1976d2; border-radius: 0 0 90px 90px / 0 0 110px 110px; position: absolute; left: 0; top: 0; box-shadow: 0 6px 14px #1565c044; cursor: pointer; display: flex; align-items: flex-end; justify-content: center; transition: left 0.55s cubic-bezier(.53,.1,.5,1.2), border-color 0.16s, box-shadow 0.16s; z-index: 1; } .cup.selected { border-color: #e53935; box-shadow: 0 0 25px #e5393544; z-index: 2; } .ball { position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%); font-size: 2.6em; z-index: 10; pointer-events: none; opacity: 1; transition: opacity 0.2s; } .hide-ball { opacity: 0; } .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; } .info { text-align: center; color: #1976d2; font-size: 1.13em; margin: 4px 0 0 0; } @media (max-width: 900px) { .container, .game-area { width: 98vw !important; min-width: 0; } .cups-area { width: 97vw !important;} .cup { width: 22vw; height: 23vw; min-width:80px; min-height:90px; } } </style> </head> <body> <div class="container" id="main-container"></div> <script> // ============================= // カップシャッフルゲーム // ============================= const NUM_CUPS = 3; const SWAP_COUNT = 12; let ballPos = 1; // ボールの位置(0,1,2) let cups = [0,1,2]; // カップのインデックス(実際の位置) let swapIndex = 0; let shuffling = false; let canSelect = false; let resultMsg = ""; // カップのX座標(ピクセル単位) const cupX = [40, 240, 440]; // タイトル画面を表示 function showTitleScreen() { document.getElementById('main-container').innerHTML = ` <h1>🥤 カップシャッフルゲーム 🥤</h1> <img src="cup_shuffle_title.png" class="title-img" alt="カップシャッフルゲーム タイトル"> <div class="rule-section"> <div class="rule-title">🥤 遊び方・ルール 🥤</div> <div class="rule-text"> ・ボールが入ったカップがシャッフルされます。<br> ・よく見て、ボールがどのカップに入っているか当てましょう!<br> ・カップをクリックして答えを選択します。<br> ・正解なら「正解!」、外れたら「残念!」と表示されます。 </div> </div> <button class="btn" id="start-btn">スタート</button> `; document.getElementById('start-btn').onclick = showGameScreen; } // ゲーム画面 function showGameScreen() { ballPos = Math.floor(Math.random() * NUM_CUPS); // ボール初期位置をランダム cups = [0,1,2]; swapIndex = 0; shuffling = false; canSelect = false; resultMsg = ""; renderGame(); setTimeout(()=>{ revealBall(false); // 一瞬見せる setTimeout(()=>{ revealBall(true); // ボール隠す setTimeout(startShuffle, 600); }, 900); }, 400); } // ボールを隠す/見せる function revealBall(hide) { // すべてのcupを確認し、ballPos(ボールの位置)のみボールを見せる const cupsNodes = document.querySelectorAll('.cup'); cupsNodes.forEach((cupNode, idx) => { const ball = cupNode.querySelector('.ball'); // ballPosは「どのカップIDにボールがあるか」、cups[idx]は「この位置にいるカップID」 // 位置idxのカップIDがballPosと一致するものだけボールを見せる if (cups[idx] === ballPos) { if(hide) ball.classList.add('hide-ball'); else ball.classList.remove('hide-ball'); } else { ball.classList.add('hide-ball'); } }); } // ゲーム画面描画(カップをアニメ配置) function renderGame(selectedIdx = null) { let cupHtml = ''; for(let i=0; i<NUM_CUPS; i++) { // cups配列は現在「座標に何番カップがあるか」= cups[物理位置]=カップID let cupId = cups[i]; // cupIdがballPosと一致するカップだけボールを描画 let showBall = (cupId === ballPos && !shuffling && !canSelect); cupHtml += ` <div class="cup${selectedIdx===i?' selected':''}" data-cup="${i}" style="left:${cupX[i]}px;"> ${showBall ? `<div class="ball">🔴</div>` : `<div class="ball hide-ball">🔴</div>`} </div> `; } document.getElementById('main-container').innerHTML = ` <h1>🥤 カップシャッフルゲーム 🥤</h1> <div class="game-area"> <div class="info" id="info">${shuffling ? "シャッフル中..." : canSelect ? "どのカップ?クリックで選ぼう!" : "カップの位置を覚えてください。"}</div> <div class="cups-area">${cupHtml}</div> ${resultMsg ? `<div class="message-box">${resultMsg}</div>` : ""} </div> `; } // シャッフル開始 function startShuffle() { shuffling = true; canSelect = false; swapIndex = 0; shuffleStep(); } // シャッフル手順(アニメで交換) function shuffleStep() { if (swapIndex >= SWAP_COUNT) { shuffling = false; canSelect = true; setTimeout(()=>{ renderGame(); }, 250); return; } // 2つのカップ物理位置をランダムで選ぶ let a = Math.floor(Math.random()*NUM_CUPS); let b; do { b = Math.floor(Math.random()*NUM_CUPS); } while (b === a); // 見た目の位置をアニメでスワップ let cupNodes = document.querySelectorAll('.cup'); let [leftA, leftB] = [cupX[a], cupX[b]]; cupNodes[a].style.left = leftB + "px"; cupNodes[b].style.left = leftA + "px"; // 実際の配列順もスワップ(見た目の動きが終わるまで配列は保留) setTimeout(()=>{ [cups[a], cups[b]] = [cups[b], cups[a]]; renderGame(); swapIndex++; setTimeout(shuffleStep, 380); }, 540); // アニメduration+α } // カップ選択時 function onSelectCup(idx) { if (!canSelect) return; canSelect = false; let cupId = cups[idx]; let isCorrect = (cupId === ballPos); resultMsg = isCorrect ? "🎉 正解!お見事!" : "😢 残念!またチャレンジしてね。"; renderGame(idx); setTimeout(showEndScreen, 1500, isCorrect); } // 終了画面 function showEndScreen(isCorrect) { document.getElementById('main-container').innerHTML = ` <h1>🥤 カップシャッフルゲーム 🥤</h1> <img src="cup_shuffle_title.png" class="title-img" alt="カップシャッフルゲーム タイトル"> <div class="message-box"> ${isCorrect ? "🎉 おめでとう!正解です!🎉" : "😢 残念!また挑戦してね。"} </div> <button class="btn center-btn" id="back-title-btn">タイトル画面に戻る</button> `; document.getElementById('back-title-btn').onclick = showTitleScreen; } // windowに選択関数を生やす window.onSelectCup = onSelectCup; // 初期表示 showTitleScreen(); </script> </body> </html>アルゴリズムの流れ手順処理内容1showTitleScreen() でタイトル画面の HTML を描画2「スタート」クリック → showGameScreen() でボール位置をランダム設定→一瞬見せ→隠す→シャッフル3startShuffle() → shuffleStep() で12回ランダムペアを選び、見た目と配列の両方をスワップ4シャッフル完了後に canSelect=true、画面内のカップをクリック可能5クリックされた位置 idx のカップID と ballPos を比較 → 正誤メッセージをセット61.5秒後に showEndScreen() を呼び出し、正解/不正解画面を描画7「タイトルに戻る」ボタンで再びタイトル画面へ関数の解説関数名機能概要詳細説明showTitleScreen()タイトル画面描画スタートボタン・ルール説明を含む初期画面を描画。クリックで showGameScreen() を呼び出し。showGameScreen()ゲーム画面初期化ballPos をランダム設定し、カップを初期配置。ボールの一瞬表示→隠蔽を経て startShuffle() を呼び出す。revealBall(hide)ボールの見せ/隠し切り替えcups 配列と ballPos の一致判定で対象カップのみボール要素 (.ball) のクラスを制御。renderGame(sel)画面再描画現在の cups 配列・状態フラグ (shuffling / canSelect) を反映し、カップ要素とメッセージを動的に再構築。startShuffle()シャッフル開始フラグ初期化後、shuffleStep() によって非同期スワップを連続実行。shuffleStep()シャッフルステップ(アニメ+配列)物理位置インデックス a,b のランダム選出 → CSS アニメーションで左右入れ替え → 配列内もスワップ → 次ステップへ再帰呼び出し。onSelectCup(i)プレイヤー選択処理canSelect フラグチェック後、選択位置 i のカップID と ballPos を比較。結果メッセージ設定→ showEndScreen() 呼び出し。showEndScreen(c)結果画面描画正誤に応じたメッセージと「タイトルに戻る」ボタンを表示。ボタンで showTitleScreen() を再呼び出し。改造のポイント- シャッフル難易度:SWAP_COUNT を増減させるほか、ステップごとのアニメ速度(setTimeoutの値)を調整し、初心者~上級者向けモードを実装。
- カップ数変更:NUM_CUPS を増やして難易度を上げる。多いほど視認が困難になります。
- マルチラウンド制:3ラウンド制など複数回チャレンジ制を導入し、得点合計を競わせるランキング要素を追加。
- 視覚・サウンド演出:シャッフル中のBGM、クリック時のSE、正解時のエフェクトを加えると没入感アップ。
- モバイル最適化:タッチ操作時のアニメ速度やクリック判定領域を微調整し、指でタップしやすくする。
アドバイスまずはシャッフル/選択の安定動作を最優先で実装し、次にUI演出・サウンドを足して「見て楽しい・聞いて楽しい」ゲーム体験を作りましょう。難易度やモードを段階的に増やすことで、長期的に遊んでもらえるコンテンツになります!
- X
- Bluesky
- Hatena
- Copy