実験 #18:早口言葉実況(アメンボレース)

■18個目「アメンボレース」

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

 今回は、レース実況ゲームを作ってみました。(*^_^*)
 レースをするのは……なんと “アメンボ” です!

 4匹のアメンボが左側からスタートし、水面をスーッと滑りながらゴールを目指します。

 今回こだわったのは、アメンボたちの名前です。(=v=*)
 「実況の人が早口で熱く語っているのに、名前がとんでもなく言いにくかったらどうなるんだろう?」

 そんな遊び心から生まれたレースになっています。

 アメンボ4匹には、それぞれ特徴的な名前があります。

 ① アカアシアオアメンボ(赤足青アメンボ)
 ② アメマアタマアメンボ(雨間頭アメンボ)
③ アメアガリアメンボ(雨上りアメンボ)
④ アクマアザアメンボ(悪魔痣アメンボ)

 アメンボ特有の「ツツーイ」とした水上移動でレースは進行し、途中で実況による進捗も流れます。

 そしてゴールすると……アメンボ川柳が聞けます。
 どんな一句が飛び出すか、お楽しみに!

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

(^^*)今日のゲームは「アメンボ実況レース」。
 まず、1位になりそうなアメンボを選びます。
 ①アカアシアオアメンボ
 ②アメマアタマアメンボ
 ③アメアガリアメンボ
 ④アクマアザアメンボ
 レースは左端に4匹が並んでスタート。
 アメンボ特有の「ツーイ・ツツーイ」という動きで前進します。
 進むテンポはランダムで、1ゲームは約30秒ほど。
 10秒ごとに実況が入り、早口言葉みたいで面白いです。
 ゴールすると、各アメンボごとの“アメンボ川柳”が読み上げられます。
 予想が当たると虹が出て、リスタートで何度でも遊べます。

(AI)「アメンボ実況レース」ですね!承知いたしました。
 今日は **「早口言葉のような実況」** と **「シュールな川柳」** を詰め込んだ一作をご用意しました。

(AI)🦟 本日のトリビア:アメンボの秘密
 アメンボが水に浮けるのは、足の毛に油がついているから……ではなく、
 **表面張力 × 足の微細な毛の構造** の合わせ技なんです。
 さらに「飴のような匂い」がする個体がいることから、この名前がついたと言われています。
〈甘い匂い…さすがに嗅いだことはありませんでした〉(^^;)

(つ 、<)一瞬でゴールするので、ゲームにならないよ。  ゴールまで30秒くらいは変わらないようにして。

(AI)大変失礼いたしました!  摩擦と加速のバランスが“プロ級アメンボ”すぎましたね。
 「30秒間のドラマ」を楽しめるように、移動計算を根本から調整しました。
 最高速度を抑えつつ、「溜めてから進む」動きを強調し、実況が3〜4回入るペースにしています。

(AI)🦟 本日のトリビア:アメンボの「歩幅」
 一度のキックで体長の数倍を滑ることができます。
 人間に換算すると、一歩で10メートル以上進んでいるようなものです。
 今回のゲームでは、その“余韻”を長めにして、ゴールまでの時間を調整しています。

■ 実際の動作

 まず、優勝しそうなアメンボを選びます。
 アメンボというより、どちらかというと“蚊のイラスト”に近い見た目です。

 レース中は、途中で実況が入ります。  早口言葉のようなテンポで、状況をどんどん伝えてくれます。

 ゴールすると、勝利したアメンボごとに川柳が読み上げられます。  予想が当たると虹が出て、ちょっと嬉しい演出になっています。  「もう一度勝負する」ボタンで、何度でもチャレンジできます。

■ コードのポイント

 今回の記事では、AI と相談しながら作った「アメンボレース」の HTML コードを公開します。ブラウザだけで動く、とてもシンプルなゲームです。

 PC の「メモ帳」(※Windowsの場合)を開き、下のコードをコピーして貼り付け、「amenbo.html」など好きな名前で保存してください。保存したファイルをブラウザで開くだけで遊べます。

【操作方法】 ・まず、優勝しそうなアメンボを1匹選びます ・レース終了後は「もう一度勝負する」ボタンで何度でもチャレンジできます

▼ここからコード▼(クリックで開く)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>超・アメンボ実況レース(川柳増量版)</title>
<style>
body { background: #b0bec5; font-family: sans-serif; text-align: center; margin: 0; padding: 5px; overflow: hidden; }
#commentary {
background: #fff; padding: 20px; border-radius: 15px; min-height: 120px;
margin: 10px auto; width: 95%; border: 4px solid #0288d1;
font-size: 2.2em; font-weight: 900; line-height: 1.2;
display: flex; align-items: center; justify-content: center;
box-shadow: 0 4px 10px rgba(0,0,0,0.3); color: #333;
}
.live-prefix { color: #d32f2f; font-size: 0.8em; margin-right: 10px; }
#track-viewport {
position: relative; width: 98%; height: 350px;
border: 8px solid #01579b; border-radius: 25px;
margin: 10px auto; overflow: hidden; background: #29b6f6;
}
#course-content { position: absolute; width: 200%; height: 100%; left: 0; }
.line { position: absolute; width: 20px; height: 100%; top: 0; z-index: 2; }
#start-line { left: 80px; background: rgba(255,255,255,0.9); }
#goal-line { right: 80px; background: repeating-linear-gradient(0deg, #fff, #fff 25px, #000 25px, #000 50px); }
.amenbo { position: absolute; font-size: 45px; z-index: 10; transition: transform 0.1s; filter: drop-shadow(3px 5px 2px rgba(0,0,0,0.3)); }
.ripple {
position: absolute; border: 2px solid rgba(255,255,255,0.7); border-radius: 50%;
pointer-events: none; animation: ripple-ani 1s ease-out; z-index: 1;
}
@keyframes ripple-ani { from { width: 0; height: 0; opacity: 1; } to { width: 120px; height: 120px; opacity: 0; transform: translate(-60px, -60px); } }
.rainbow { display: none; font-size: 70px; animation: bounce 0.8s infinite; margin-top: 15px; }
@keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-30px); } }
button { padding: 15px 30px; font-size: 20px; cursor: pointer; background: #0288d1; color: white; border: none; border-radius: 10px; margin: 5px; font-weight: bold; box-shadow: 0 4px 0 #01579b; }
</style>
</head>
<body>
<div id="commentary"><span class="live-prefix">実況</span>優勝予想をどうぞ!</div>
<div id="controls">
<button onclick="select(0)">①アカアシアオ</button>
<button onclick="select(1)">②アメマアタマ</button>
<button onclick="select(2)">③アメアガリ</button>
<button onclick="select(3)">④アクマアザ</button>
</div>
<div id="track-viewport">
<div id="course-content">
<div id="start-line" class="line"></div>
<div id="goal-line" class="line"></div>
<div id="a0" class="amenbo" style="top: 25px;">🦟<small style="font-size:12px"></small></div>
<div id="a1" class="amenbo" style="top: 105px;">🦟<small style="font-size:12px"></small></div>
<div id="a2" class="amenbo" style="top: 185px;">🦟<small style="font-size:12px"></small></div>
<div id="a3" class="amenbo" style="top: 265px;">🦟<small style="font-size:12px"></small></div>
</div>
</div>
<div id="rainbow" class="rainbow">🌈 的中!虹が出た! 🌈</div>
<button id="restart" style="display:none;" onclick="location.reload()">もう一度勝負!</button>
<script>
const names = ["アカアシアオアメンボ", "アメマアタマアメンボ", "アメアガリアメンボ", "アクマアザアメンボ"];
// 【新】各アメンボ専用・ランダム川柳リスト
const senryuList = [
[ // ①アカアシアオ
"赤い足 青い背中に 虹かかる", "自慢の足 今日もどこかで 踏み外す", "青すぎて 水と区別が つかぬ日々", "信号機 みたいな名前と 言われけり", "赤と青 混ぜたら紫 嫌だわあ"
],
[ // ②アメマアタマ
"雨を待ち 頭を濡らして 突き進む", "その頭 意外と絶壁 気に選る", "間寛平 みたいな名前で 30年", "雨の日は 頭の上が 賑やかだ", "アメマアタマ 逆さから読み 挫折する"
],
[ // ③アメアガリ
"雨上がり 虹の彼方へ ひとっ飛び", "傘捨てて 水面を叩く 独走劇", "決死の気 水たまりさえ 宇宙なり", "虹が出る 予想が当たれば 虹が出る", "晴れ男 呼ばれて久しい 水の上"
],
[ // ④アクマアザ
"悪魔の痣 水面に刻む 勝利の輪", "痣の形 よく見りゃハート 恥ずかしい", "悪魔でも 水の上なら 礼儀正し", "痣というか 実は寝癖で ございます", "黒い影 悪魔というより 煤渡り"
]
];
const liveTexts = [
"実況「さあ一斉にスタート!30秒の長い戦い!」",
"実況「アメンボ赤いなあいうえお!言えた!言えましたよ!」",
"実況「見てくださいこの水さばき!まさに水上のスピードスター!」",
"実況「アカアシアオアメマアタマ!あわやの混戦状態!」",
"実況「おっと一匹足を取られたか!しかしすぐさまツツーイ!」",
"実況「右のアメンボ左のアメンボ、どっちを向いてもアメンボだ!」",
"実況「実況の舌がもつれてきましたがレースは熱い!」",
"実況「水面が揺れる!アメンボたちの意地がぶつかり合う!」",
"実況「アメアガリが上がればアカアシアオも青くなる激戦!」",
"実況「浮き雲に小海老泳いでる場合じゃない!猛追です!」",
"実況「水上のバレリーナ!無駄のないキックが炸裂中!」",
"実況「誰が勝ってもおかしくない!残り15秒のドラマ!」",
"実況「アクマアザが不気味な動き!水面を切り裂く!」",
"実況「アメマの頭が光る!前方の視界は良好か!?」",
"実況「ツーイ!ツツーイ!ツツツーイ!もはやダンスだ!」",
"実況「波紋が語る!勝負の行方はまだ誰にもわからない!」",
"実況「アメンボアメンボアメンボ!実況も必死です!」",
"実況「青い水面に描く、4つの勝利への軌跡!」",
"実況「おっとっと!滑走距離が伸びている!風に乗ったか!?」",
"実況「ラストスパート!ゴール線が右から迫るぞー!」"
];
let selected = -1, isRacing = false, startTime, racers = [ {d:110,v:0}, {d:110,v:0}, {d:110,v:0}, {d:110,v:0} ];
let viewportWidth = window.innerWidth * 0.98;
const courseWidth = viewportWidth * 1.9;
function select(id) {
if (selected !== -1) return;
selected = id;
document.getElementById('commentary').innerHTML = `<span class="live-prefix">実況</span>【${names[id]}】の単勝!レディー・ゴー!`;
setTimeout(() => { isRacing = true; startTime = Date.now(); update(); }, 1000);
}
function createRipple(x, lane) {
const r = document.createElement('div');
r.className = 'ripple';
r.style.left = x + "px";
r.style.top = (25 + lane * 80 + 25) + "px";
document.getElementById('course-content').appendChild(r);
setTimeout(() => r.remove(), 1000);
}
function update() {
if (!isRacing) return;
let elapsed = (Date.now() - startTime) / 1000;
let order = racers.map((r, i) => ({d:r.d, i:i})).sort((a,b)=>b.d-a.d);
let topName = names[order[0].i].replace("アメンボ","");
for (let i = 0; i < 4; i++) {
racers[i].v *= 0.95;
if (Math.random() > 0.97) {
racers[i].v += Math.random() * 2.8 + 0.5;
createRipple(racers[i].d + 20, i);
document.getElementById('a' + i).style.transform = "scaleX(1.3)";
setTimeout(()=> document.getElementById('a' + i).style.transform = "scaleX(1.0)", 150);
}
racers[i].d += racers[i].v;
document.getElementById('a' + i).style.left = racers[i].d + "px";
}
let leadDist = Math.max(...racers.map(r => r.d));
let scrollX = Math.min(Math.max(0, leadDist - viewportWidth / 2), courseWidth - viewportWidth);
document.getElementById('course-content').style.left = (-scrollX) + "px";
let step = Math.floor(elapsed / 6);
if (this.lastStep !== step) {
let baseText = liveTexts[Math.floor(Math.random() * liveTexts.length)];
if (Math.random() > 0.6) baseText = `実況「${topName}が逃げ切るか!?デッドヒートだ!」`;
document.getElementById('commentary').innerHTML = baseText;
this.lastStep = step;
}
let goalPos = document.getElementById('goal-line').offsetLeft;
for (let i = 0; i < 4; i++) {
if (racers[i].d >= goalPos - 40) {
isRacing = false;
finish(i);
return;
}
}
requestAnimationFrame(update);
}
function finish(winner) {
// ランダム川柳の選択
const randomSenryu = senryuList[winner][Math.floor(Math.random() * 5)];
document.getElementById('commentary').style.fontSize = "1.8em";
document.getElementById('commentary').innerHTML = `<span class="live-prefix">🏁 優勝</span>${names[winner]}!<br>『${randomSenryu}』`;
document.getElementById('restart').style.display = 'inline-block';
if (winner === selected) document.getElementById('rainbow').style.display = 'block';
}
</script>
</body>
</html>


■ 今日の学び

 このアメンボ実況レース、音声で実況を再現できるのかどうかを AI に相談してみました。

 「可能」とのことだったので実際に試してみたのですが、今回は“想像しながら楽しむレース”のほうが雰囲気に合っていたので、音声版は採用しませんでした。

 もし気になる方は、紹介しているコードを AI に
 
「実況を音声で話すようにして」

 とお願いしてみてください。対応したコードを作ってくれると思います。(*>v<)b”

 次回は「カメとのふれあい」ゲームです。
 のんびり楽しんでいただければと思います。(*^v^)ノシ

次の実験はこちら(飼育観察ゲーム): 「我が家のイシガメ観察日記」



 ゲーム作りが楽しくて、どんどん作品が増えています(*^v^*)
 もし応援していただけたら、とても励みになります。

~このブログについて~

AIと一緒に遊び、
100の実験でゲームを育てるブログ

🌈初めての方へ
🌱このブログについて
🌳実験一覧
🍃プライバシーポリシー


AIと作る100のゲーム実験室(*^v^*)をもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む