■11個目「目隠し波動砲」
今回はこの「目隠し波動砲」のゲームを作ってみました。ヽ(*^o^*)ノ
このゲームは、時間を勘で当てるゲームです。
シンプルで絵の少ないゲームを作ってみたいと思いました。そこでAIと相談しながら形にしていきました。
最初の5秒はカウントが表示されます。ちょうど20秒と思ったところで発射ボタンを押します。20秒に近いほどスコアが高くなります。
・・・ただし、船長の独り言が頭の中のカウントの邪魔をしてきます。自分の感覚を信じてボタンを押してみましょう。(><;)
■ AIとのやりとりで面白かった部分
(^^*)今日もゲーム作ります。ゲームは目隠し波動砲ゲーム。SFの世界で、敵艦隊発見。敵の母艦へ必殺の波動砲を発射しようとするけど、20秒前から敵艦隊を隕石群が目隠しのように横切っていく。そこをちょうど20秒で発射ボタンを押す。早すぎても遅すぎても致命傷にはならないのでちょうどを狙う。ちょうどに近いほどポイントが高い。
(AI)常連さんもいよいよLv.2といったところでしょうか。少しずつ、あうんの呼吸でいけるようになってきた気がします。今回のゲーム、シンプルですが「見えない恐怖」と「体内時計」の勝負で熱くなりそうですね。
〈まだまだレベル低いです〉(つv<`)
(=v=*)船長の独り言は20秒を数えるプレイヤーの集中を削ぐような、気になる独り言を言い続ける感じが欲しい。
(AI)あはは、なるほど!船長は味方でありながら、最大の「お邪魔キャラ」というわけですね。Lv.2の常連さん、なかなかドSな仕様変更を提案されますね。
20秒を数えている頭の中に、絶妙に「数字」を混ぜたり、どうでもいい世間話を振ってきたりして、体内時計を狂わせにくる……そんな**「集中力ブレイカー船長」**を実装しました。
■ 実際の動作
5秒までは、カウント見えます。

船長が意地の悪い独り言をつぶやきます。

惑わされずに、あてましょう(^^;)

途中AIの作ったイメージです。絵はかっこいいけど、誤字がひどいです。(^^;)

■ コードのポイント
今回の記事では、AI と相談しながら作った「目隠し波動砲」の HTML コードを公開します。ブラウザだけで動く、とてもシンプルなゲームです。
PC の「メモ帳」(※Windowsの場合)を開き、下のコードをコピーして貼り付け、「mekakusi.html」など好きな名前で保存 → ブラウザで開く。これだけで遊べます。
操作はとても簡単で、20秒だと思ったタイミングで「発射ボタン」を押すだけ。
今回も AI が作ってくれたコードをそのまま載せているので、気になるところを自分で改造したり、色や動きをアレンジして遊んでみてください(^^*)
「※スマホでは動作しない場合があります」
「※コードが長いので、必要な方だけコピーしてください」
▼ここからコード▼(クリックで開く)
<!DOCTYPE html><html lang="ja"><head> <meta charset="UTF-8"> <title>波動砲発射シミュレーター:精神汚染</title> <style> @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Noto+Sans+JP:wght@400;700&display=swap'); body { background: #000; color: #fff; text-align: center; font-family: 'Noto Sans JP', sans-serif; overflow: hidden; margin: 0; display: flex; align-items: center; justify-content: center; min-height: 100vh; } #game-window { position: relative; width: 95vw; max-width: 950px; height: 85vh; background: radial-gradient(circle at center, #050510 0%, #000 100%); border: 2px solid #0ff; box-shadow: 0 0 20px rgba(0,255,255,0.3); display: flex; flex-direction: column; align-items: center; justify-content: center; overflow: hidden; } .meteor-container { position: absolute; top: 0; left: 0; width: 100%; height: 100%; display: none; z-index: 10; pointer-events: none; } .meteor { position: absolute; background: #444; border-radius: 50%; animation: slide linear infinite; } @keyframes slide { from { transform: translateX(110vw); } to { transform: translateX(-20vw); } } /* 独り言の演出:大きく、フェード付き */ #status-message { height: 120px; font-size: 2.2em; color: #ffcc00; font-weight: bold; margin: 20px 0; z-index: 20; width: 90%; display: flex; align-items: center; justify-content: center; text-shadow: 0 0 15px rgba(255, 204, 0, 0.7); transition: opacity 0.8s ease-in-out; opacity: 0; } .fade-in { opacity: 1 !important; } #timer { font-family: 'Orbitron', sans-serif; font-size: 8em; margin: 20px 0; text-shadow: 0 0 30px #fff; } #result-display { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: rgba(0,0,0,0.98); border: 4px solid #ffd700; padding: 40px; display: none; z-index: 100; border-radius: 15px; width: 75%; } .btn { padding: 25px 70px; font-size: 2em; font-family: 'Noto Sans JP', sans-serif; background: linear-gradient(to bottom, #f00, #800); color: #fff; border: none; cursor: pointer; border-radius: 10px; box-shadow: 0 0 25px rgba(255,0,0,0.5); z-index: 30; } .fire-shake { animation: fire-shake-anim 0.05s linear infinite; } @keyframes fire-shake-anim { 0% { transform: translate(15px, 15px); } 50% { transform: translate(-20px, -10px); } 100% { transform: translate(0, 0); } } </style></head><body><div id="game-window"> <div class="meteor-container" id="meteor-field"></div> <h1 style="font-family: 'Orbitron'; color: #0ff; letter-spacing: 5px;">WAVE MOTION CANNON</h1> <div id="status-message"></div> <div id="timer">0.00</div> <div id="result-display"> <div id="score-rank" style="font-size: 3.5em; color: #ffd700; font-family: 'Orbitron';"></div> <div id="error-info" style="font-size: 1.8em; margin: 15px 0;"></div> <div id="comment-text" style="font-size: 1.5em; line-height: 1.6; color: #fff;"></div> <button class="btn" style="margin-top: 40px;" onclick="resetGame()">再装填</button> </div> <button class="btn" id="fireBtn" onclick="fire()">波動砲 発射! (SPACE)</button></div><audio id="fireSound" src="https://www.soundjay.com/buttons/sounds/button-7.mp3"></audio><audio id="explosionSound" src="https://www.soundjay.com/explosions/sounds/explosion-02.mp3"></audio><script> let startTime, animId, lastQuoteTime = 0, isFired = false; const target = 20.00; const captainQuotes = [ "「……15、16……。あ、悪い、今の私の独り言だ」", "「昨日の昼飯、何食ったっけかなぁ……」", "「おい、さっきの隕石、13個くらいあったか?」", "「……そういえば、君の給料、来月から3%カットだってよ」", "「7、8、9……いや、なんでもない。続けてくれ」", "「……このボタン、押し心地が18倍くらい良くなってるな」", "「今の振動、12段階評価でいうと5くらいか?」", "「あ、書き置き忘れた。……ガス代払ったかな……」", "「……宇宙の終わりって、あと何十億年後だっけ?」", "「君、今……何秒だと思ってた?」", "「……蚊がいるな、このコックピット。プ~ン……」", "「19……あ、いや、まだかな。……どう思う?」", "「……腹減ったな。牛丼屋、17時までだっけか」", "「なあ、今の誤差1.4秒くらいズレてなかったか?」", "「……12、13……。おっと、口に出てたか」", "「君の故郷、今ちょうど18時くらいじゃないか?」", "「……11、12、13……。よし、順調だな(笑)」", "「あ、さっきの敵艦、19隻いた気がするな」", "「……14、15、……。いや、数えてないぞ。断じてな」", "「……20……。あ、これは私のラッキーナンバーだ」" ]; const timerDisp = document.getElementById('timer'); const msgDisp = document.getElementById('status-message'); const mField = document.getElementById('meteor-field'); const resDisp = document.getElementById('result-display'); const fireBtn = document.getElementById('fireBtn'); const windowEl = document.getElementById('game-window'); function createMeteors() { mField.innerHTML = ''; for(let i=0; i<45; i++) { let m = document.createElement('div'); m.className = 'meteor'; let size = Math.random() * 25 + 10; m.style.width = size + 'px'; m.style.height = (size * 0.7) + 'px'; m.style.top = Math.random() * 100 + '%'; m.style.animationDuration = (Math.random() * 0.6 + 0.3) + 's'; m.style.animationDelay = (Math.random() * 2) + 's'; mField.appendChild(m); } } function resetGame() { isFired = false; lastQuoteTime = 0; resDisp.style.display = 'none'; fireBtn.style.display = 'block'; timerDisp.style.visibility = 'visible'; mField.style.display = 'none'; windowEl.classList.remove('fire-shake'); msgDisp.innerText = "「20.00秒で発射だ。私語は厳禁だぞ」"; msgDisp.classList.add('fade-in'); startTime = performance.now(); update(); } function update() { if (isFired) return; const elapsed = (performance.now() - startTime) / 1000; if (elapsed >= 5.0) { timerDisp.style.visibility = 'hidden'; mField.style.display = 'block'; if(mField.children.length === 0) createMeteors(); // 1.5秒ごとにフェードしながら更新 if (elapsed - lastQuoteTime > 1.8) { msgDisp.classList.remove('fade-in'); setTimeout(() => { if (!isFired) { msgDisp.innerText = captainQuotes[Math.floor(Math.random() * captainQuotes.length)]; msgDisp.classList.add('fade-in'); } }, 400); lastQuoteTime = elapsed; } } else { timerDisp.innerText = elapsed.toFixed(2); } animId = requestAnimationFrame(update); } function fire() { if (isFired) return; isFired = true; cancelAnimationFrame(animId); const finishTime = (performance.now() - startTime) / 1000; const diff = Math.abs(target - finishTime); document.getElementById('fireSound').play(); setTimeout(() => document.getElementById('explosionSound').play(), 400); windowEl.classList.add('fire-shake'); setTimeout(() => windowEl.classList.remove('fire-shake'), 1200); mField.style.display = 'none'; timerDisp.style.visibility = 'visible'; timerDisp.innerText = finishTime.toFixed(3) + "s"; fireBtn.style.display = 'none'; msgDisp.classList.remove('fade-in'); let rank, comment; if (diff < 0.05) { rank = "S: 神業"; comment = "「……チッ。私の独り言に惑わされないとはな。認めよう、君の勝ちだ」"; } else if (diff < 0.4) { rank = "A: 直撃"; comment = "「ほう! 奇跡的に当たったな。今のは私の『20』が効いたか?」"; } else if (diff < 1.2) { rank = "B: 命中"; comment = "「惜しいな、私の数え間違いに釣られたか? 悪い悪い(笑)」"; } else { rank = "C: 失敗"; comment = "「……あ、今の失敗、全部君の責任ってことで報告書出しとくよ」"; } document.getElementById('score-rank').innerText = rank; document.getElementById('error-info').innerText = `誤差: ${diff.toFixed(3)}秒`; document.getElementById('comment-text').innerText = comment; resDisp.style.display = 'block'; } window.addEventListener('keydown', (e) => { if (e.code === 'Space') { e.preventDefault(); fire(); } }); resetGame();</script></body></html
■ 今日の学び
船長の独り言は、目をつぶってプレイしてみると意外といい点が取れました。音声で読み上げる仕組みもAIから提案がありましたが、今回はここまでの仕上がりです。
毎日1個のゲーム作りも順調で、R8.3.7の時点でいよいよ50個目前です。(^~^*)
ブログ作りもゲーム作りも、集中しすぎると時間があっという間に過ぎてしまい、つい夜更かししてしまいます。(><、)
AIを有料版にしてからは、作っている途中で「思ってたのと違う」方向に行くことが減った気がします。こちらの意図が伝わりやすく、返ってくる答えも丁寧なので、無料版より頼りになる感覚があります。
次回は「雪の妖精シマエナガ」ゲームです。
かわいいので、どうぞお楽しみに(*^v^)ノシ
次の実験はこちら(横スクロール集団飛翔アクションゲーム): 「シマエナガ大好きゲーム」

コメントを残す