■6つ目「筋肉もりもり森盛り盛り」
このゲームは、筋肉マッチョ × ボーリング × 森という、なかなかカオスな組み合わせから生まれました。
最初は「ボーリングのゲームをHTMLで作れないかな?」とAIに相談していたのですが、 動きやスコア計算など、本格的なボーリング再現はかなり難しそう……(*_*;)
そこで発想を転換して、横画面のボーリング風ゲームにし、 ピンとなる木を“森”としてピラミッド型に積み上げ、 そこへマッチョが力いっぱい球を投げつける!というアイデアを提案しました(:>v<)ノ…………….◯” 森
最初は針葉樹の三角形の木を使っていたのですが、ゲーム開始と同時に森が一瞬で崩壊してしまい、ゲームにならず……。 そこで木の形を広葉樹のイメージに変更して、ようやく安定するようになりました。
■ AIとのやりとりで面白かった部分
(^^*)今日もゲーム作ります。ボーリング風げーむで、タイトルは「筋肉もりもり森盛り盛り」プレイヤーは筋肉もりもりのマッチョ。
ボーリングのピンは図形を組み合わせた“木”でできていて、シャンパンタワーのように盛り盛りに積み上がっています。
そこへマッチョが球を転がしてぶつける、というゲームです。
(AI)「筋肉もりもり森盛り盛り」……最高にキレてるタイトルですね! 常連さんの斬新なアイデアにはいつも驚かされますが、今回もまた一段とパワーを感じます。
シャンパンタワーのように積み上がった「図形の木」を、マッチョが豪快になぎ倒す。物理演算の挙動がめちゃくちゃ気持ちよさそうなゲームになりそうですね。
(AI)本日の豆知識:ボーリングと「筋肉」の意外な歴史
実は、近代ボーリングの原型を作ったのは、あの宗教改革で有名なマルティン・ルターだと言われています。
当時のドイツでは、ピンを「異教徒」や「悪魔」に見立てて、それを倒すことで自分の信仰心を証明し、罪を浄化するという**「九柱戯(カイルレ)」という儀式が行われていました。 つまり、元々は「スポーツ」というより、精神と肉体を鍛える「修行」**に近いものだったんです。
「筋肉もりもり森盛り盛り」で木をなぎ倒すのも、ある種のマッスル修行……現代の浄化活動と言えるかもしれませんね!
〈アイデアに対して豆知識をいつもくれるAIに感心します。〉
■ 実際の動作
マッチョが”森”に向かって全力でボーリングの球を転がすイメージ。

パワーメーターが動いていて、クリックするとパワーに応じたスピードの球が転がります。

パワーの数値によってマッスル評価が(飴)と(鞭)で、でます。

相談しながら作っている途中でAIが出してくれたイメージ画像
文字はなんだか読めないけど、雰囲気は伝わってきます。

■ コードのポイント
今回の記事では、AI と相談しながら作ったゲーム「筋肉もりもり森盛り盛り」の HTML コードを公開します。ブラウザだけで動く、とてもシンプルな仕組みです。
PC の「メモ帳」を開き、下のコードをすべてコピー → 貼り付け → 「mori.html」など好きな名前で保存 → ブラウザで開く。 これだけで、そのままゲームが遊べます。
操作はとても簡単で、変動しているパワーバーをクリックして止めるだけ。
今回も AI が作ってくれたコードをそのまま載せているので、気になるところを自分で改造したり、色や動きをアレンジして遊んでみてください(^^*)
「※スマホでは動作しない場合があります」
「※コードが長いので、必要な方だけコピーしてください」
▼ここからコード▼(クリックで開く)
<!DOCTYPE html><html><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>筋肉もりもり森盛り盛り - 21段階の審判</title> https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js <style> body { margin: 0; overflow: hidden; font-family: 'Helvetica Neue', sans-serif; background: #e0f7fa; touch-action: none; } #sky-bg { position: absolute; width: 100%; height: 100%; background: linear-gradient(to bottom, #81d4fa, #ffffff 70%, #a5d6a7 100%); z-index: -1; } #ui { position: absolute; top: 15px; left: 15px; color: #0277bd; font-size: 24px; font-weight: bold; text-shadow: 1px 1px white; } #gauge-container { position: absolute; bottom: 100px; left: 20px; width: 300px; background: rgba(255,255,255,0.9); padding: 15px; border-radius: 50px; display: none; border: 4px solid #0288d1; box-shadow: 0 10px 20px rgba(0,0,0,0.1); } .bar-bg { width: 100%; height: 30px; background: #eeeeee; border-radius: 15px; overflow: hidden; } #pwr-fill { width: 0%; height: 100%; background: #4fc3f7; } .burn { background: linear-gradient(to right, #ffeb3b, #f44336) !important; } #god-macho { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(0); font-size: 150px; z-index: 70; transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275); text-shadow: 0 0 30px gold; pointer-events: none; } #result-screen { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border: 6px solid #0288d1; padding: 40px; text-align: center; border-radius: 30px; display: none; z-index: 100; width: 85%; } #final-pwr { font-size: 70px; font-weight: bold; color: #0288d1; margin: 10px 0; } #macho-rank { font-size: 18px; color: #e91e63; font-weight: bold; min-height: 3.5em; line-height: 1.5; } button { background: #0288d1; border: none; padding: 12px 30px; color: white; font-weight: bold; border-radius: 50px; font-size: 18px; cursor: pointer; margin-top: 15px; } </style></head><body id="game-body"> <div id="sky-bg"></div> <div id="ui">💪 PUMP LEVEL: <span id="live-score">0</span></div> <div id="god-macho">✨💪✨</div> <div id="gauge-container"><div class="bar-bg"><div id="pwr-fill"></div></div></div> <div id="result-screen"> <div id="final-pwr">0</div> <div id="macho-rank">判定中...</div> <button onclick="location.reload()">再挑戦(インターバル終了)</button> </div> <script> const { Engine, Render, Runner, Bodies, Composite, Body, Constraint, Events } = Matter; const engine = Engine.create(); const render = Render.create({ element: document.body, engine: engine, options: { width: window.innerWidth, height: window.innerHeight, wireframes: false, background: 'transparent' } }); const ground = Bodies.rectangle(window.innerWidth/2, window.innerHeight - 20, window.innerWidth, 40, { isStatic: true, render: { fillStyle: '#689f38' } }); Composite.add(engine.world, ground); let treeUnits = []; const startX = window.innerWidth - 60, startY = window.innerHeight - 62, rows = 7; for (let i = 0; i < rows; i++) { for (let j = 0; j < rows - i; j++) { const x = startX - (j * 60) - (i * 30), y = startY - (i * 82); const trunk = Bodies.rectangle(x, y, 18, 45, { isStatic: true, render: { fillStyle: '#8d6e63' } }); const leaf = Bodies.circle(x, y - 35, 22, { isStatic: true, render: { fillStyle: '#81c784' } }); const glue = Constraint.create({ bodyA: trunk, bodyB: leaf, pointA: { x: 0, y: -22 }, pointB: { x: 0, y: 0 }, stiffness: 1, length: 0, render: { visible: false } }); treeUnits.push({ trunk, leaf, glue, active: false }); Composite.add(engine.world, [trunk, leaf, glue]); } } let state = "WAIT", tick = 0, pwrVal = 0, macho; const getRank = (s) => { if (s >= 100) return "✨ 筋肉の特異点 ✨<br>飴:おめでとう!全宇宙が君の広背筋にひれ伏した!"; if (s >= 95) return "【鞭】95点。惜しいね。<br>その甘さが筋肉に脂肪をつけるんだよ。"; if (s >= 90) return "【飴】キレてる!キレてるよ!<br>大胸筋が歩いてるみたいだ!"; if (s >= 85) return "【鞭】中途半端なエリート。<br>ジムの会費、多めに払っておきな。"; if (s >= 80) return "【飴】ナイスバルク!<br>血管の浮き具合が芸術の域だ。"; if (s >= 75) return "【鞭】プロテインの無駄遣い。<br>その筋肉、飾りじゃないの?"; if (s >= 70) return "【飴】肩にちっちゃい重機乗せてんのかい!<br>いい仕上がりだ。"; if (s >= 65) return "【鞭】腹筋が冬眠中かな?<br>脂肪という名のコートを脱ぎ捨てろ。"; if (s >= 60) return "【飴】筋肉が喜んで歌ってるよ!<br>明日からジムの主だ。"; if (s >= 55) return "【鞭】凡人マッチョ。<br>可もなく不可もなく、一番つまらない。"; if (s >= 50) return "【飴】腹筋の溝に指が入りそう!<br>いい感じに絞れてきたね。"; if (s >= 45) return "【鞭】24時間ジムの「幽霊会員」。<br>会費で運営を支える慈善家。"; if (s >= 40) return "【飴】筋肉の芽が出始めたね!<br>水をやる(プロテイン)のを忘れるな。"; if (s >= 35) return "【鞭】腕立て伏せ、膝ついてやってない?<br>正直に言いなさい。"; if (s >= 30) return "【飴】初心者卒業の予感!<br>鏡を見る回数が増えてきたね。"; if (s >= 25) return "【鞭】湿ったマッチ棒。<br>風が吹いただけで筋肉痛になりそう。"; if (s >= 20) return "【飴】まずは継続だ!<br>ダンベルを触っただけで偉いぞ。"; if (s >= 15) return "【鞭】もやしにプロテインをかけた状態。<br>まだ「素材」ですらない。"; if (s >= 10) return "【飴】筋肉への関心は認める。<br>さあ、プロテインを買いに行こう。"; if (s >= 5) return "【鞭】筋肉の幽霊。<br>実体がない。存在がシュール。"; return "【闇】概念。<br>筋肉という言葉を知らない人。"; }; document.body.addEventListener('mousedown', () => { if (state === "WAIT") { state = "POWERING"; document.getElementById('gauge-container').style.display = "block"; } else if (state === "POWERING") { state = "ROLLING"; document.getElementById('gauge-container').style.display = "none"; launch(); } }); function launch() { const score = Math.floor(pwrVal); const isGod = score >= 99; macho = Bodies.circle(80, window.innerHeight - 100, 45, { density: isGod ? 15 : 3, frictionAir: 0, render: { fillStyle: isGod ? 'gold' : '#ffccbc' } }); Composite.add(engine.world, macho); Body.setVelocity(macho, { x: (pwrVal * 0.45) + 15, y: 0 }); if(isGod) { setTimeout(() => { document.getElementById('god-macho').style.transform = 'translate(-50%, -50%) scale(1.5)'; for(let i=0; i<80; i++) setTimeout(() => { const p = document.createElement('div'); p.className = 'protein-powder'; p.style.width=(Math.random()*10+5)+'px'; p.style.height=p.style.width; p.style.left=Math.random()*100+'vw'; p.style.top='-20px'; p.style.animationDuration=(Math.random()*2+1)+'s'; document.body.appendChild(p); setTimeout(()=>p.remove(),3000); }, i*20); }, 800); } } function showResult(score) { state = "RESULT"; const scoreDisp = document.getElementById('final-pwr'); const rankDisp = document.getElementById('macho-rank'); document.getElementById('result-screen').style.display = "block"; let current = 0; const timer = setInterval(() => { scoreDisp.innerText = current; rankDisp.innerHTML = getRank(current); if (current >= score) clearInterval(timer); current++; }, 30); } Events.on(engine, 'beforeUpdate', () => { if (state === "ROLLING" && macho) { treeUnits.forEach(unit => { if (!unit.active && Math.abs(macho.position.x - unit.trunk.position.x) < 220) { unit.active = true; Body.setStatic(unit.trunk, false); Body.setStatic(unit.leaf, false); document.body.classList.add('shake'); setTimeout(() => document.body.classList.remove('shake'), 100); } }); if (macho.position.x > window.innerWidth + 150) showResult(Math.floor(pwrVal) >= 99 ? 100 : Math.floor(pwrVal)); } }); (function update() { tick++; if (state === "POWERING") { pwrVal = (Math.sin(tick * 0.25) + 1) * 50; document.getElementById('pwr-fill').style.width = pwrVal + "%"; document.getElementById('live-score').innerText = Math.floor(pwrVal); if(pwrVal > 95) document.getElementById('pwr-fill').classList.add('burn'); else document.getElementById('pwr-fill').classList.remove('burn'); } requestAnimationFrame(update); })(); Render.run(render); Runner.run(Runner.create(), engine); </script></body></html>
■ 今日の学び
物理演算やコードの仕組みは正直さっぱり分からないのですが、こちらの無茶なアイデアを何とか形にしようと頑張ってくれる AI に、つい「AIさん」と敬称を付けたくなってきます。
最初は木がゲーム開始と同時に崩れたり、ボールが全然届かなかったり、吹き飛んだ木が永遠に暴れ続けたりと、調整がとても難しかったです。(つv<`)
でもその試行錯誤の中で、AI が提案してくれた「スコアごとの筋肉評価」はとても気に入って採用しました。
意図せず木が吹き飛んで暴れまわる感じも、遊んでみるとクセになる楽しさがあります。(^^*)♪
何も考えずに気軽に遊べるので、このゲームで100点目指してみてください。
そして、森で全力で遊んでいたら、なんだか筋肉が「もっと育ててくれ〜!」と
ささやいてくる気がしてきました。(^v^*)
そういえば最近ずっと飲んでいる、ホエイプロテインが
運動した日のちょっとしたごほうびみたいで、けっこう気に入っています。
次回は「RPG風スゴロク」のゲーム。 どうぞお楽しみに(*^v^)ノシ
次の実験(スゴロクゲーム):「ブレイド・クエスト」

コメントを残す