実験#64:緑の中の緑(スナップエンドウ収穫ゲーム)

■64個目 「スナップエンドウ収穫ゲーム」

こんにちは、AIと作る100のゲーム実験室の みー です。(*^_^*)/

今回は、ちょっと観察力が試される農園ゲームを作ってみました。

収穫するのは……
シャキシャキ美味しい “スナップエンドウ” です!

スナップエンドウって、緑の蔓(つる)に緑の葉っぱ、
そして実る豆ももちろん緑色。

全部が同系色で不規則に重なり合って育つので、
ちょうどいい大きさの豆を見つけ出すのは、
実際の農作業でもとっても困難なんだそうです。

今回こだわったのは、その「絶妙な分かりにくさ」です。(=v=*)
「葉っぱの向こう側に隠れて見えない!」
「あ、育ちすぎて黄色くなっちゃった!」という農園ならではの悩ましさを、
そのまま遊び心として詰め込んでみました。

小さすぎても減点、見逃して黄色くなっても減点という、
なかなかシビアな品質管理が求められます。

でも、豆ができる前に一瞬だけ可憐な「白い花」が咲いて教えてくれるので、
そこを頼りに目を凝らしてみてくださいね。(´ ▽ `)

そして無事に20個の収穫が終わると……
豆だけに、 スナップエンドウにまつわる「豆知識」か、
美味しい「レシピ」が飛び出します。

どんなお話やおかずが出てくるか、お楽しみに!

 

■ AIとのやりとりで面白かった部分

■ゲーム仕様の相談からスタート

みー:(^v^) 今日もゲームを作ります。HTML形式で動かせるゲームです。

スナップエンドウ収穫ゲーム。
豆も葉も茎(蔓)もほぼ同系色で、
不規則な立体構造で育つスナップエンドウの収穫はとても困難です。

程よい大きさで収穫するとなると、さらに難易度アップ。
小さすぎても減点。
大きくなりすぎると黄色っぽくなって見つけやすくなるけれど減点。
濃い黄色になると大幅減点なので、早めの収穫が大切です。

少し早送りで成長するスナップエンドウを、
ちょうどの大きさで見つけてマウスクリックで収穫するゲーム。

葉っぱの影や蔓の向こう側など
「とても分かりにくい」部分をポイントに探して収穫します。

収穫したスナップエンドウは画面横に並んで表示され、20個収穫で終了。
ポイントの合計がスコアになります。

収穫後には、豆だけに── スナップエンドウにまつわる「豆知識」か、
美味しい「スナップエンドウのレシピ」が、
複数種類の中からランダムで表示されます。

AI: 今回は、同系色で見つけにくく、しかも成長具合を見極めるという、
アクション性と観察力が試される「スナップエンドウ収穫ゲーム」ですね。

葉の裏や蔓の隙間に隠れるという仕様は、
Canvasの描画順(背面の葉 → スナップエンドウ → 半透明の前面の葉)を工夫することで、絶妙な「見えにくさ」と「分かりにくさ」を表現しました。

もちろん今回も、メモ帳などのテキストアプリに貼り付けて「.html」で保存するだけで動くコードを作成しています。

みー:(つv<)b 収穫終了時に、収穫していない黄色いスナップエンドウが残っていたら、その分も減点とします。

AI: 農園の品質管理、さらに厳しくなりましたね。

画面に残ってしまった黄色いスナップエンドウ
(大きめ:-20点、濃い黄色:-50点)を収穫終了時に集計し、
最終スコアから一気に減点する仕様を追加しました。

これで、ただ20個収穫するだけでなく、
画面全体の成長度合いにも気を配る必要が出て、
ぐっとゲーム性が高まりました。

また、常連のお客様ですので、
クリア後の「豆知識&レシピ」も中身を総入れ替えしてあります。
今回も新鮮なネタを用意しておきましたよ!

(修正のたびに新しい豆知識が増えていき、最終的に全部を一つにまとめました。)

みー:(*^^)ちょっとしたヒントとなるように、
小さい豆が発生する前に、一瞬でいいので白い花が見えるようにしよう。

AI: 了解しました!ヒント機能の実装ですね。

ただでさえ蔓や葉と同系色のスナップエンドウが、
不規則に発生して成長していくのを完璧に管理するのは至難の業。
農家の方も、この「花」を指標にその後の収穫計画を立てるそうです。

仕様通り、小さい豆(Pea)が発生する前に、その場所に一瞬(約0.7秒間)、白いスナップエンドウの花(Flower)を表示する機能を実装しました。

これにより、「あっ、あそこに花が咲いたから、すぐ豆ができるぞ!」というヒントになります。

また、せっかくのヒントなので、
花は「前面の葉っぱ」よりもさらに手前に表示されるように
描画順を調整しました。

これで、葉っぱに隠れて見えない場所で発生したとしても、
一瞬の白い光(花)で発生場所が分かるようになっています。

 

■ 実際の動作

こちらが、AIが作ってくれたゲームの仕上がりの画像です。
(画像:AIが作ってくれたゲームの画像)


白い花がポッと咲いて、そこに豆ができます。

(画像:豆の花)



育ちすぎると黄色くなっていきます。
黄色くなる前に収穫しましょう。

(画像:黄色くなった豆)


収穫すると、収穫かごに豆が入ります。
黄色くなった取りこぼし豆は減点になります。
収穫が終わると、レシピが表示されます。

(画像:収穫完了!)

 

■ コードのポイント

今回の記事では、AI と相談しながら作った
「スナップエンドウ収穫ゲーム」 の HTML コードを公開します。

このコードをそのまま保存するだけで、
あなたのPCでもすぐに遊べます。

ブラウザだけで動く、とてもシンプルなゲームなので、
気軽に試してみてくださいね。

▼ 遊ぶまでの手順

①PC の「メモ帳」(※Windowsの場合)や  
 Mac の「テキストエディット」など、テキストエディタを開きます。

②下のコードをコピーして貼り付けます。

③「sunappuenndou.html」など、好きな名前で保存します。
 (※拡張子は .html にしてください)

④保存したファイルをブラウザで開けば、すぐに遊べます。


■ 遊び方・操作説明

それでは、遊び方と操作方法をご説明しますね。(^^*)

操作はマウスだけで、とっても簡単です!

画面の中に、スナップエンドウが次々と生まれて、
少し早送りで成長していきます。

ここだ!という「ちょうどいい大きさ」になったら、
マウスクリックでポチッと収穫してください。

成長具合によって、もらえるポイントが変わります。(´ ▽ `)

・ 小さすぎ(シュッとした緑):まだ早いです! -10点
・ ちょうどいい(ふっくら緑):ここが狙い目! +50点
・ 大きめ(少し黄色):育ちすぎちゃいました… -20点
・ 育ちすぎ(濃い黄色):ああ〜っ! -50点

💡 攻略のヒント
豆が生まれる場所には、一瞬だけ「白い花」が咲きます。(=v=*)

花が咲いた場所を覚えておくと、 葉っぱの裏や蔓の向こう側に隠れた豆も、
ぐっと見つけやすくなりますよ!

⚠️ 気をつけてほしいこと
カゴに20個収穫するとゲームクリアになりますが……
その時に画面の中に「黄色くなった豆」が残っていると、
ペナルティとして最終スコアからドカンと減点されてしまいます。(>_<)

見つけにくくて大変ですが、黄色くなり始めた豆は、
早めに収穫してしまうのが高得点のコツです。

最高スコアを目指して、農園の品質管理に挑戦してみてくださいね。(^_^)

“コードは長いので折りたたんでいます”

▼ここからコード▼(クリックで開く)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>スナップエンドウ収穫ゲーム - 完璧版(効果音付き) -</title>
<style>
body {
font-family: sans-serif;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
padding: 20px;
}
h1 {
color: #2e7d32;
margin-bottom: 20px;
}
#game-wrapper {
display: flex;
background: white;
padding: 15px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
position: relative; /* スタート画面の基準位置 */
}
canvas {
background-color: #2e7d32;
cursor: crosshair;
border: 3px solid #1b5e20;
border-radius: 4px;
}
#sidebar {
width: 260px;
margin-left: 20px;
display: flex;
flex-direction: column;
}
#score-board {
background: #e8f5e9;
padding: 15px;
border-radius: 8px;
text-align: center;
margin-bottom: 15px;
border: 2px solid #81c784;
}
#score-board div:first-child {
font-size: 14px;
color: #555;
}
#score {
font-size: 32px;
font-weight: bold;
color: #2e7d32;
margin: 5px 0;
}
#harvest-count {
font-size: 14px;
font-weight: bold;
color: #388e3c;
}
#instructions {
background: #f1f8e9;
border: 2px solid #aed581;
padding: 12px;
border-radius: 8px;
margin-bottom: 15px;
font-size: 13px;
line-height: 1.5;
color: #333;
}
#instructions strong {
color: #2e7d32;
font-size: 14px;
display: block;
margin-bottom: 5px;
border-bottom: 1px solid #aed581;
padding-bottom: 3px;
}
.point-good { color: #2e7d32; font-weight: bold; }
.point-bad { color: #d32f2f; }
.hint-text {
margin-top: 8px;
color: #e65100;
font-weight: bold;
font-size: 12px;
}
#basket-container {
flex-grow: 1;
background: #fff3e0;
border: 2px solid #ffb74d;
border-radius: 8px;
padding: 15px 10px 10px 10px;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
gap: 6px;
position: relative;
}
#basket-title {
position: absolute;
top: -12px;
left: 10px;
background: #fff3e0;
padding: 0 5px;
font-size: 12px;
color: #e65100;
font-weight: bold;
}
.harvested-pea {
width: 24px;
height: 16px;
border-radius: 12px;
border: 1px solid #1b5e20;
display: inline-block;
}
#message-area {
margin-top: 20px;
width: 100%;
max-width: 890px;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
display: none;
text-align: center;
border: 3px solid #ff9800;
box-sizing: border-box;
}
#penalty-message {
font-size: 14px;
font-weight: bold;
margin-bottom: 15px;
}
#knowledge-text {
font-size: 18px;
font-weight: bold;
color: #3e2723;
line-height: 1.6;
margin: 20px 0;
padding: 15px;
background: #fff8e1;
border-radius: 8px;
}
.btn {
padding: 12px 24px;
font-size: 16px;
font-weight: bold;
background-color: #ff9800;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.btn:hover {
background-color: #f57c00;
}
/* スタート画面のスタイル */
#start-screen {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.9);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 10;
border-radius: 8px;
}
#start-screen h2 {
color: #2e7d32;
margin-bottom: 20px;
}
</style>
</head>
<body>
<h1>スナップエンドウ収穫ゲーム</h1>
<div id="game-wrapper">
<div id="start-screen">
<h2>準備ができたらクリック!</h2>
<button class="btn" id="start-btn">ゲームスタート(音が出ます)</button>
</div>
<canvas id="gameCanvas" width="600" height="600"></canvas>
<div id="sidebar">
<div id="score-board">
<div>合計スコア</div>
<div id="score">0</div>
<div id="harvest-count">収穫数: <span id="count">0</span> / 20</div>
</div>
<div id="instructions">
<strong>【遊び方とポイント】</strong>
緑の群れから、ちょうどいい大きさの豆を探してクリック!<br><br>
・<span class="point-bad">小さすぎ:-10点</span><br>
・<span class="point-good">ちょうど:+50点</span><br>
・<span class="point-bad">大きめ(黄):-20点</span><br>
・<span class="point-bad">育ちすぎ(濃黄):-50点</span><br><br>
<span class="point-bad">※終了時に画面に残った黄色い豆も減点対象になります。</span>
<div class="hint-text">💡ヒント:豆が発生する場所には、一瞬「白い花」が咲きます!</div>
</div>
<div id="basket-container">
<div id="basket-title">収穫カゴ</div>
</div>
</div>
</div>
<div id="message-area">
<h2 style="color: #e65100; margin-top:0;">収穫完了!</h2>
<p>あなたの最終スコアは <span id="final-score" style="font-size:28px; font-weight:bold; color:#2e7d32;"></span> ポイントです。</p>
<div id="penalty-message"></div>
<div id="knowledge-text"></div>
<button class="btn" onclick="location.reload()">もう一度収穫に行く</button>
</div>
<script>
// --- Web Audio API を使った効果音の実装 ---
const AudioContext = window.AudioContext || window.webkitAudioContext;
let audioCtx;
function initAudio() {
if (!audioCtx) {
audioCtx = new AudioContext();
}
}
function playSound(type) {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
osc.connect(gainNode);
gainNode.connect(audioCtx.destination);
const now = audioCtx.currentTime;
if (type === 'good') {
// ちょうどいい収穫:明るい「ピコッ!」
osc.type = 'sine';
osc.frequency.setValueAtTime(880, now); // A5
osc.frequency.exponentialRampToValueAtTime(1760, now + 0.1);
gainNode.gain.setValueAtTime(0.2, now);
gainNode.gain.exponentialRampToValueAtTime(0.01, now + 0.1);
osc.start(now);
osc.stop(now + 0.1);
} else if (type === 'bad') {
// 減点収穫:鈍い「ブブッ」
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(150, now);
osc.frequency.linearRampToValueAtTime(100, now + 0.15);
gainNode.gain.setValueAtTime(0.1, now);
gainNode.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
osc.start(now);
osc.stop(now + 0.15);
} else if (type === 'clear') {
// ゲームクリア:ファンファーレ風
osc.type = 'square';
osc.frequency.setValueAtTime(523.25, now); // C5
osc.frequency.setValueAtTime(659.25, now + 0.15); // E5
osc.frequency.setValueAtTime(783.99, now + 0.3); // G5
osc.frequency.setValueAtTime(1046.50, now + 0.45);// C6
gainNode.gain.setValueAtTime(0.1, now);
gainNode.gain.linearRampToValueAtTime(0, now + 0.8);
osc.start(now);
osc.stop(now + 0.8);
}
}
// --- ゲーム本体の処理 ---
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let score = 0;
let harvestCount = 0;
const MAX_HARVEST = 20;
let isGameOver = false;
let isGameStarted = false; // スタートフラグ
const peas = [];
const flowers = [];
const leavesBg = [];
const leavesFg = [];
const vines = [];
const colorBg = '#2e7d32';
const colorVine = '#1b5e20';
const colorLeaf = ['#388e3c', '#43a047', '#2e7d32', '#1b5e20', '#4caf50'];
function random(min, max) {
return Math.random() * (max - min) + min;
}
function generateDecorations() {
for(let i=0; i<40; i++) {
vines.push({
x1: random(0, canvas.width), y1: random(0, canvas.height),
cx: random(0, canvas.width), cy: random(0, canvas.height),
x2: random(0, canvas.width), y2: random(0, canvas.height),
width: random(2, 6)
});
}
for(let i=0; i<200; i++) {
leavesBg.push({
x: random(0, canvas.width), y: random(0, canvas.height),
size: random(15, 45), color: colorLeaf[Math.floor(Math.random()*colorLeaf.length)],
angle: random(0, Math.PI*2)
});
}
for(let i=0; i<100; i++) {
leavesFg.push({
x: random(0, canvas.width), y: random(0, canvas.height),
size: random(25, 60), color: colorLeaf[Math.floor(Math.random()*colorLeaf.length)],
angle: random(0, Math.PI*2), alpha: random(0.6, 0.9)
});
}
}
class Flower {
constructor(x, y) {
this.x = x;
this.y = y;
this.timer = 40;
this.isFinished = false;
this.angle = random(0, Math.PI * 2);
}
update() {
this.timer--;
if (this.timer <= 0) {
this.isFinished = true;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
if(this.timer < 10) ctx.globalAlpha = this.timer / 10;
ctx.fillStyle = '#FFFFFF';
ctx.beginPath();
ctx.ellipse(0, -5, 8, 10, 0, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.ellipse(-4, 4, 5, 7, random(-0.2, 0.2), 0, Math.PI * 2);
ctx.ellipse(4, 4, 5, 7, random(-0.2, 0.2), 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#FFEB3B';
ctx.beginPath();
ctx.arc(0, 0, 2, 0, Math.PI*2);
ctx.fill();
ctx.restore();
}
}
class Pea {
constructor(x, y) {
this.x = x;
this.y = y;
this.size = 0;
this.growthRate = random(0.04, 0.12);
this.angle = random(0, Math.PI * 2);
}
update() {
if (this.size < 100) {
this.size += this.growthRate;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
let color = '#4CAF50';
if (this.size >= 90) color = '#FFEB3B';
else if (this.size >= 70) color = '#CDDC39';
else if (this.size >= 40) color = '#66BB6A';
ctx.beginPath();
let w = 8 + this.size * 0.25;
let h = 4 + this.size * 0.12;
ctx.ellipse(0, 0, w, h, 0, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = '#1b5e20';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.restore();
}
getRadius() {
return 10 + this.size * 0.25;
}
}
function drawLeaf(leaf, ctx, isForeground = false) {
ctx.save();
ctx.translate(leaf.x, leaf.y);
ctx.rotate(leaf.angle);
if (isForeground) ctx.globalAlpha = leaf.alpha;
ctx.beginPath();
ctx.ellipse(0, 0, leaf.size, leaf.size/2.5, 0, 0, Math.PI*2);
ctx.fillStyle = leaf.color;
ctx.fill();
ctx.strokeStyle = '#0d5302';
ctx.lineWidth = 1;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(-leaf.size, 0);
ctx.lineTo(leaf.size, 0);
ctx.stroke();
ctx.restore();
}
function gameLoop() {
if (isGameOver || !isGameStarted) return;
ctx.fillStyle = colorBg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = colorVine;
vines.forEach(v => {
ctx.lineWidth = v.width;
ctx.beginPath();
ctx.moveTo(v.x1, v.y1);
ctx.quadraticCurveTo(v.cx, v.cy, v.x2, v.y2);
ctx.stroke();
});
leavesBg.forEach(l => drawLeaf(l, ctx));
if ((peas.length + flowers.length) < 25 && Math.random() < 0.03) {
const spawnX = random(40, canvas.width - 40);
const spawnY = random(40, canvas.height - 40);
flowers.push(new Flower(spawnX, spawnY));
}
peas.forEach(p => {
p.update();
p.draw(ctx);
});
leavesFg.forEach(l => drawLeaf(l, ctx, true));
for (let i = flowers.length - 1; i >= 0; i--) {
const f = flowers[i];
f.update();
f.draw(ctx);
if (f.isFinished) {
peas.push(new Pea(f.x, f.y));
flowers.splice(i, 1);
}
}
requestAnimationFrame(gameLoop);
}
// スタートボタンの処理
document.getElementById('start-btn').addEventListener('click', () => {
initAudio(); // 音声コンテキストの初期化
isGameStarted = true;
document.getElementById('start-screen').style.display = 'none';
gameLoop();
});
canvas.addEventListener('mousedown', (e) => {
if(isGameOver || !isGameStarted) return;
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
for (let i = peas.length - 1; i >= 0; i--) {
const p = peas[i];
const dx = mouseX - p.x;
const dy = mouseY - p.y;
if (Math.sqrt(dx * dx + dy * dy) <= p.getRadius()) {
harvest(p);
peas.splice(i, 1);
break;
}
}
});
function harvest(pea) {
let points = 0;
let color = '';
if (pea.size < 40) {
points = -10;
color = '#4CAF50';
playSound('bad'); // 減点音
} else if (pea.size < 70) {
points = 50;
color = '#66BB6A';
playSound('good'); // 加点音
} else if (pea.size < 90) {
points = -20;
color = '#CDDC39';
playSound('bad'); // 減点音
} else {
points = -50;
color = '#FFEB3B';
playSound('bad'); // 減点音
}
score += points;
harvestCount++;
document.getElementById('score').innerText = score;
document.getElementById('count').innerText = harvestCount;
const basket = document.getElementById('basket-container');
const peaIcon = document.createElement('div');
peaIcon.className = 'harvested-pea';
peaIcon.style.backgroundColor = color;
peaIcon.title = `サイズ: ${Math.floor(pea.size)} / ポイント: ${points}`;
basket.appendChild(peaIcon);
if (harvestCount >= MAX_HARVEST) {
endGame();
}
}
const triviaAndRecipes = [
"【豆知識】スナップエンドウは「スナックエンドウ」と呼ばれることもありましたが、昭和58年に農林水産省によって名称が統一されました。",
"【豆知識】豆とサヤの両方を食べるスナップエンドウは栄養価がとても高く、免疫力をサポートするβ-カロテンやビタミンCが豊富です!",
"【豆知識】シャキシャキの歯ごたえを保つには、サッと短時間で茹でた直後に冷水にとるのが、色鮮やかに仕上げるコツです。",
"【レシピ】『スナップエンドウとベーコンのバター醤油炒め』。サッと炒めてバターの香りを纏わせるだけで、ご飯が止まらない絶品おかずに!",
"【レシピ】『スナップエンドウのツナマヨ和え』。茹でてマヨネーズとツナ缶で和えるだけ。シャキシャキ感とツナの旨味が相性抜群です。",
"【レシピ】『スナップエンドウとすまきの卵とじ』。高知でおなじみのピンクの「すまき」と一緒に切って卵でとじれば、彩り豊かで優しい味の小鉢が完成!",
"【豆知識】スナップエンドウは比較的歴史が浅く、1970年代にアメリカで、グリーンピースの改良品種として誕生したと言われています。",
"【豆知識】美味しく食べるための最大のコツは「筋取り」です。サヤのカーブの内側と外側、両方の太い筋をしっかり取ることで、シャキッとした食感が活きます!",
"【豆知識】「さやえんどう」は豆が大きくなる前にサヤごと食べるもの。「スナップエンドウ」は大きくなってからサヤごと食べるもの。似て非なる存在です。",
"【レシピ】『スナップエンドウの肉巻き』。豚バラ肉をぐるぐると巻きつけて、甘辛いタレで焼き上げるだけ。お弁当の主役にもなる大満足の一品です!",
"【レシピ】『スナップエンドウと生節(なまぶし)のマヨサラダ』。高知特産の鰹の生節をほぐして、茹でたエンドウとマヨネーズで和えるだけ。お酒のアテに最高です。",
"【レシピ】『スナップエンドウとニンニクの塩炒め』。オリーブオイルと刻みニンニクでサッと炒り、少しの塩で味付け。豆の甘みが一番際立ちます。",
"【豆知識】スナップエンドウの花は白色が一般的ですが、品種によっては赤紫色の美しい花を咲かせるものもあります。",
"【豆知識】実はエンドウの花は、ほぼ100%「自花受粉」といって、自分の花粉で受粉します。蜂などが来なくても実がなるんですよ。",
"【豆知識】花が咲いてから収穫までの期間は、天候にもよりますがだいたい2週間〜3週間程度です。ゲームでは大幅に早送りしています!",
"【レシピ】『スナップエンドウの天ぷら』。サクサクの衣の中に、蒸された豆の甘みが凝縮されます。塩だけで食べるのがおすすめです。",
"【レシピ】『スナップエンドウと彩り野菜のペペロンチーノ』。パスタの具材として。最後にサッと加えることで、緑の色鮮やかさとシャキシャキ感が残ります。",
"【レシピ】『スナップエンドウの白和え』。茹でたエンドウを、水切りした豆腐、すりごま、砂糖、味噌を混ぜた衣で和えます。優しい甘みが豆と合います。",
"【豆知識】サヤが固くならないのが最大の特徴。他のエンドウ豆にある「内果皮」という固い層が退化しているため、丸ごと美味しく食べられるんです。",
"【豆知識】鮮度を見分けるポイントは「ハリ」と「色」。新鮮なものはポキッと折れるほどハリがあり、緑色が鮮やかです。",
"【豆知識】保存する時は乾燥に注意!濡らしたキッチンペーパーでふんわり包み、ポリ袋に入れて冷蔵庫の野菜室へ。これで数日はシャキシャキ感を保てます。",
"【レシピ】『スナップエンドウのナムル』。サッと塩茹でした豆に、ごま油、鶏ガラスープの素、すりおろしニンニク、白ごまを和えるだけ。無限に食べられます。",
"【レシピ】『スナップエンドウと桜えびの塩炒め』。ごま油で桜えびをカリッと炒め、そこに豆を投入。エビの香ばしさと豆の甘みが口に広がる春らしい一品です。",
"【レシピ】『スナップエンドウの柚子胡椒マヨディップ』。高知特産の柚子胡椒をマヨネーズに混ぜ、茹でた豆にたっぷりつけてガブリ。爽やかな辛味が甘さを引き立てます!",
"【豆知識】高知県は温暖な気候を生かして、スナップエンドウの栽培も盛ん。四万十川流域などの自然豊かな環境で美味しく育ちます。",
"【豆知識】スナップエンドウのカロリーは100gあたり約43kcal。甘みが強いのにヘルシーで、ダイエット中のおやつ代わりにもなります。",
"【レシピ】『スナップエンドウといりごまのきんぴら』。細切りにして、ごま油と醤油、みりんでサッと炒めるだけ。お弁当の隙間埋めにも最適です。",
"【レシピ】『スナップエンドウとちくわの磯辺揚げ』。青のりを混ぜた衣でサクッと揚げます。スーパーのお惣菜コーナーにありそうなホッとする味です。"
];
function endGame() {
isGameOver = true;
playSound('clear'); // クリア音
let penalty = 0;
let yellowCount = 0;
let darkYellowCount = 0;
peas.forEach(pea => {
if (pea.size >= 90) {
penalty += 50;
darkYellowCount++;
} else if (pea.size >= 70) {
penalty += 20;
yellowCount++;
}
});
score -= penalty;
document.getElementById('final-score').innerText = score;
const penaltyEl = document.getElementById('penalty-message');
if (penalty > 0) {
penaltyEl.style.color = '#d32f2f';
penaltyEl.innerText = `※収穫忘れペナルティ:育ちすぎ(濃い黄色)${darkYellowCount}個、大きめ(黄色)${yellowCount}個が残っていたため、計 ${penalty} ポイント減点…!`;
} else {
penaltyEl.style.color = '#388e3c';
penaltyEl.innerText = `※黄色い豆ゼロ!完璧なタイミングでの収穫です!`;
}
document.getElementById('message-area').style.display = 'block';
const randomIndex = Math.floor(Math.random() * triviaAndRecipes.length);
document.getElementById('knowledge-text').innerText = triviaAndRecipes[randomIndex];
}
generateDecorations();
// gameLoop() はスタートボタンが押されてから呼び出されます
</script>
</body>
</html>


■ 今日の学び

今回のゲームの仕上げに、効果音とBGMをAIへお願いしました。
その時にAIが曲を作ってくれたので、紹介します。

みー:(*^^)このゲームの効果音とBGMをお願い。
テーマは「豆の菜園 小まめに収穫 豆知識」リラックス風で。

AI: アコースティックピアノとフルートが心地よく響く、
極上のボサノバトラックが仕上がったよ!

チャット画面からすぐに再生できるようになっているから、
まずはのんびりと聴いてみてくれ。

この曲を聴きながら、あの「完璧版」のゲームで遊ぶのもいいし、
もしゲームのコードの中に直接このBGMを組み込みたいなら、
いつでも裏で調整するから気軽に声をかけておくれ。

ゆっくり楽しんでな!

癒されます(* ̄~ ̄*)~♪

あらためまして、ここから今回の学びです。

海辺でシーグラスを拾ったり、
じっくり何かを探し出したりするのが結構得意で、大好きなのです。(^^*)
だから、自分の菜園でも「スナップエンドウを見逃すはずがない!」
と自信満々だったのですが……。

先日、葉っぱの裏にすっかり取り残されて、
パンパンに大きくなりすぎた黄色い豆を発見して、
呆然としてしまいました。(>_<)
同系色に紛れる自然のカモフラージュ力、おそるべしです。

その時のちょっとした悔しさと驚きをそのままゲームの仕様にしてみたのですが、 自分でテストプレイしてみても、なかなかの高難易度ゲームになってしまいました。(´ ▽ `)

でも、ただ難しい農園作業で終わらせず、
収穫の後の「お楽しみ」も欲しいなと思い立ちました。
せっかく採れたてのスナップエンドウがあるなら、
美味しく食べたいですよね。(=v=*)

そこで、20個の収穫が完了した時に、
AIに提案してもらった 「スナップエンドウの豆知識」や「おすすめレシピ」が
ランダムで表示される機能を付け加えてみました。

ゲームで必死に豆を探して遊んだ後は、
ぜひ今日の晩ごはんの参考にしてみてくださいね。(^_^*)

 (画像:庭で育ったスナップエンドウ)

■ 次回は、ジターリングのリアル体験ゲームです。

#23「ジターリングの物理」では、
シミュレートで動かすので精一杯で終わっていましたが……

今回は、実際の技「ローラーコースター」をもとにしたゲームにしてみました。
ジターリングが手の中でぐるんと走り抜ける、
あの独特の気持ちよさを再現しています。

難しいけれど、上手く回るととっても楽しいです。

▶ 次の実験はこちら
  (ジターリングの物理2)
「ジターリング・マスター:レジェンド・ラリー」



 ✨今日も最後まで読んでくださって、ありがとうございます。
みーの実験室は、みなさんの応援で元気に動いています。(*^v^*)