■86個目 「虹色リボン」
こんにちは、AIと作る100のゲーム実験室の みー です。(*^_^*)/
今回は、都会の夜を舞台にした、とっても幻想的なゲームを作ってみました。
主役は……
そう、新体操のようにひらひらと舞う「虹色のリボン」です!
影絵のように真っ黒な建物が並ぶ静かな夜の街。
その闇の中を、マウスに合わせて虹色のリボンがスーッと滑るように動き、
煌めく光の軌跡を描いていきます。
今回こだわったのは、その “動かしていて気持ちいい” という感覚。(=v=*)
静かな都会の夜に、色と光がふわっと広がる瞬間を作りたかったんです。
忙しないイメージのある都会も、
影絵にしてみると意外と静かで、
光がとても綺麗に見えるんですよね。(^.^)
そんな静寂の中で、虹色のリボンを自由に操りながら、
夜空に煌めく星たちを集める……。
まるで、
自分が都会を彩る小さな魔法使いになったような気分になれるゲームです。
マウスを自由に動かして、自分だけの虹を描いてみてくださいね。
どんな煌めきが生まれるのか、楽しみにしています。
■ AIとのやりとりで面白かった部分
● ゲーム仕様の相談からスタート
みー:(^v^)
今日もゲームを作ります。HTML形式で動かせるゲーム。
新体操のリボンのように虹を自由に動かしたいです。
背景は影絵風の都会で、虹が映える感じにします。
AI: 新体操のリボンのような虹が、
影絵風の夜景に美しく映えるゲーム画面をイメージして作成しました。
このビジュアルを基に、HTML5 Canvasで虹の軌跡を描き、
マウス操作で動かせるようにすると雰囲気が出そうですね。
みー:(^v^*)
まずはリボンの動きの再現から進めましょう。
AI: リボンの動きから作るのは大正解。
一番「動かして気持ちいい」部分だからね。
マウスに追従して滑らかに動くリボンと、
影絵風の夜景を組んでみたよ。
みー:(*」 ̄v ̄)ノ∼*
リボンは持ち手の棒の先端についている感じにしましょう。
クリックしたときに星屑パーティクルが出るようにしたいです。
AI: 持ち手の棒を追加して、
クリックで星屑が弾ける演出だね。
一気に華やかさが増す良いアイデアだよ。
みー:(^v^)
クリックを押し続けると、
パーティクルがどんどん激しくなるようにできますか?
AI: 押しっぱなしでエネルギーがチャージされる演出は気持ちいいよね。
夜景にも映えるし、魔法の杖みたいで楽しいと思う。
みー:(^v^)
少しゲーム性を足したいので、
ランダムに現れるポイントを追いかける感じにしましょう。
ポイントにマウスが重なると次の位置に現れるように。
AI: ついにゲーム性の追加だね。
シンプルだけど、リボンの動きと組み合わせると中毒性が出る良いアイデア。
虹色のリボンで光のポイントを集める姿は、
夜空を彩る魔法使いみたいだよ。
■ 実際の動作
こちらが、仕上がったゲーム画面です。
画面の中に次々と現れる“ひかりの点”を、
虹色のリボンで追いかけてつないでいきます。
夜の街に小さな光が増えていく感じが、とても気持ちいいんです。

(仕上がりのゲーム画面)
マウスをクリックすると、星屑のパーティクルがふわっと弾けます。

(仕上がりのゲーム画面)
クリックを押しっぱなしにすると、
小さなパーティクルが連続して出続けて、
まるで魔法の杖を振っているような感覚になります。

(仕上がりのゲーム画面)
■ コードのポイント
今回の記事では、
AI と相談しながら作った 「虹色リボン」 の HTML コードを公開します。
このコードをそのまま保存するだけで、
あなたの PC でもすぐに遊べます。
ブラウザだけで動く、とてもシンプルなゲームなので、
「コードは初めて…」という方でも気軽に試してみてくださいね。
▼ 遊ぶまでの手順(むずかしさ:やさしい)
① テキストエディタを開く
Windows の場合は「メモ帳」、
Mac の場合は「テキストエディット」を使います。
(※検索バーで「メモ帳」と入力するとすぐ出てきます)
② 下のコードをコピーして貼り付けます。
③ 「niji.html」など、好きな名前で保存します。
(※拡張子は .html にしてください)
④ 保存したファイルをダブルクリックすると、
ブラウザでゲームが起動します。
■ 操作方法
世界観を楽しんでいただいたところで、
さっそくこの「虹色リボン」の操り方をご紹介しますね。
操作はとってもシンプルですが、
動かしているだけで癒やされる、
不思議な感覚になれると思います。(^^*)
基本の操作は、あなたの相棒「マウス」だけです!
🎀 虹色の軌跡を描こう
画面の中でマウスを動かすと、
それがそのままリボンの持ち手(スティック)になります。
あなたの手の動きに合わせて、
虹色のリボンがツツーイと滑らかに夜空を舞います。(^.^)
円を描いたり、波を打たせたり……。
自由に動かして、あなただけの光のアートを楽しんでみてくださいね。(=v=*)
✨ 魔法の星屑を散りばめて
そして、マウスをクリックすると、さらに素敵な魔法が使えます!
クリックの仕方で、
星屑(パーティクル)の出方が変わるんですよ。(’▽’)/.・*”
① ワンクリックで「キラキラ爆発」
マウスをポンッと一度だけクリックして指を離すと、
スティックの先から大きめの光の欠片が飛び出し、
空中で細かくキラキラと弾けます。
まるで小さな花火みたいで、とっても綺麗ですよ。(^^*)
② 長押しで「じわじわチャージ」
マウスを押しっぱなしにすると、星屑がじわじわと出続けます。
押す時間が長いほど、
星屑の量も勢いも粒の大きさもどんどん激しくなっていきます!(>ω<)
「ここぞ!」というときにパワーを溜めて、
画面いっぱいを光で満たしてみてくださいね。
🌟 夜空のポイントを集めよう
ゲームの目的は、夜空にランダムに現れる
「光のポイント(ターゲット)」を集めることです。
リボンの動きを楽しみながら、
マウスポインタをその光のポイントに重ねてみてください。(^^)
うまく重なると、ポイントが虹色に弾けてスコアがアップ!
そしてまた、次の場所に新しいポイントが現れます。
静かな都会の夜空で、虹色のリボンを操り、光の粒を集める……。
操作に慣れてくると、まるで流れるような演技をしているような、
心地よいリズムが生まれてくるはずです。(=v=*)
ぜひ、あなたらしい虹色の魔法で、
夜空をいっぱいの光で満たしてあげてくださいね。(^_^)
“コードは長いので折りたたんでいます。必要な方は開いてご覧ください。
▼ここからコード▼(クリックで開く)
<html lang="ja"><head><meta charset="UTF-8"><title>Rainbow Ribbon in the City - Step 8 (Target Collection Game)</title><style> body { margin: 0; overflow: hidden; background-color: #000; } canvas { display: block; }</style></head><body><canvas id="gameCanvas"></canvas><script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); let width = canvas.width = window.innerWidth; let height = canvas.height = window.innerHeight; // --- 影絵の街並みデータ --- const buildings = []; function createBuildings() { buildings.length = 0; let currentX = 0; while(currentX < width) { let bw = 30 + Math.random() * 60; let bh = 50 + Math.random() * (height * 0.4); buildings.push({ x: currentX, w: bw, h: bh }); currentX += bw + (Math.random() * 10 - 5); } } // --- リボンの関節データ --- const points = []; const numPoints = 60; for(let i = 0; i < numPoints; i++) { points.push({ x: width / 2, y: height / 2 }); } // --- パーティクルデータ --- const particles = []; // --- ★追加:ゲーム性のための変数 --- let score = 0; // ターゲット(光のポイント)のデータ const target = { x: 0, y: 0, r: 25, active: true }; // rは当たり判定の半径 // ターゲットをランダムな位置に再配置する関数 function resetTarget() { // 画面の端すぎないようにマージンを設ける const margin = target.r + 50; target.x = margin + Math.random() * (width - margin * 2); target.y = margin + Math.random() * (height - margin * 2); } // ヒット(収集)時の演出パーティクル function triggerHitExplosion(x, y) { for(let i = 0; i < 15; i++) { const angle = Math.random() * Math.PI * 2; const speed = 3 + Math.random() * 5; const hue = (i * 20) % 360; // ヒット時は虹色に弾けさせる particles.push({ x: x, y: y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: 0.8, decay: 0.02 + Math.random() * 0.02, size: Math.random() * 3 + 2, hue: hue, type: 'hit' // 新しいタイプ }); } } // 初期化処理 function initGame() { createBuildings(); resetTarget(); score = 0; } initGame(); // マウス座標 let mouse = { x: width / 2, y: height / 2 }; window.addEventListener('mousemove', (e) => { mouse.x = e.clientX; mouse.y = e.clientY; }); // --- マウスクリックの状態とチャージ量を管理 --- let isMouseDown = false; let clickCharge = 0; const stickLength = 60; const stickAngle = -Math.PI / 4; window.addEventListener('mousedown', () => { isMouseDown = true; }); window.addEventListener('mouseup', () => { isMouseDown = false; const tipX = mouse.x + Math.cos(stickAngle) * stickLength; const tipY = mouse.y + Math.sin(stickAngle) * stickLength; // チャージが溜まっていない(短押し)なら、多段階爆発 if (clickCharge < 5) { triggerSingleClickExplosion(tipX, tipY); } clickCharge = 0; }); window.addEventListener('mouseleave', () => { isMouseDown = false; clickCharge = 0; }); window.addEventListener('resize', () => { width = canvas.width = window.innerWidth; height = canvas.height = window.innerHeight; initGame(); // リサイズ時にターゲットも再配置 }); // ワンクリック用の強力な多段階爆発 function triggerSingleClickExplosion(x, y) { for(let i = 0; i < 20; i++) { particles.push({ x: x, y: y, vx: (Math.random() - 0.5) * 14, vy: (Math.random() - 0.5) * 16 - 6, life: 1.0, decay: 0.008 + Math.random() * 0.01, size: Math.random() * 4 + 4, hue: 45, type: 'parent', splitTime: 0.3 + Math.random() * 0.2 }); } } function animate() { // 1. 背景 const grad = ctx.createLinearGradient(0, 0, 0, height); grad.addColorStop(0, '#0a0a2a'); grad.addColorStop(1, '#2a1a4a'); ctx.fillStyle = grad; ctx.fillRect(0, 0, width, height); // 2. 影絵の街並み ctx.fillStyle = '#050505'; buildings.forEach(b => { ctx.fillRect(b.x, height - b.h, b.w, b.h); }); // --- ★追加:ターゲットのロジックと描画 --- if (target.active) { // 当たり判定:マウスポインタとターゲットの距離を計算 const dx = mouse.x - target.x; const dy = mouse.y - target.y; const distSq = dx * dx + dy * dy; // 計算軽量化のため距離の2乗で判定 if (distSq < (target.r * target.r)) { // ヒット! score++; triggerHitExplosion(target.x, target.y); resetTarget(); // 新しい位置へ移動 } // ターゲットの描画(煌めく虹色の光球) ctx.shadowBlur = 20; ctx.shadowColor = `hsla(${Date.now() * 0.1 % 360}, 100%, 70%, 0.8)`; // 影で発光表現 // 中心が白く、外側が虹色のグラデーション const gradient = ctx.createRadialGradient(target.x, target.y, target.r * 0.2, target.x, target.y, target.r); gradient.addColorStop(0, 'white'); gradient.addColorStop(0.3, `hsl(${Date.now() * 0.1 % 360}, 100%, 85%)`); gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); // 外側は透明に ctx.fillStyle = gradient; ctx.beginPath(); ctx.arc(target.x, target.y, target.r, 0, Math.PI * 2); ctx.fill(); ctx.shadowBlur = 0; // 影をリセット } // --- ★追加:スコアの描画 --- ctx.fillStyle = 'white'; ctx.font = 'bold 28px Arial'; ctx.fillText('SCORE: ' + score, 30, 50); // 3. 持ち手の棒(スティック) const tipX = mouse.x + Math.cos(stickAngle) * stickLength; const tipY = mouse.y + Math.sin(stickAngle) * stickLength; ctx.beginPath(); ctx.moveTo(mouse.x, mouse.y); ctx.lineTo(tipX, tipY); ctx.strokeStyle = '#aaaaaa'; ctx.lineWidth = 4; ctx.lineCap = 'round'; ctx.stroke(); // 長押し処理 if (isMouseDown) { if (clickCharge < 30) { clickCharge += 0.5; } const numNewParticles = Math.floor(1 + clickCharge / 8); for(let i = 0; i < numNewParticles; i++) { const spread = 4 + clickCharge * 0.3; particles.push({ x: tipX, y: tipY, vx: (Math.random() - 0.5) * spread, vy: (Math.random() - 0.5) * spread - 1, life: 1.0, decay: 0.02 + Math.random() * 0.03, size: Math.random() * 2 + 1.0 + (clickCharge * 0.05), hue: 40 + Math.random() * 20, type: 'normal' }); } } // 4. リボンの動きを計算 points[0].x += (tipX - points[0].x) * 0.4; points[0].y += (tipY - points[0].y) * 0.4; for(let i = 1; i < numPoints; i++) { points[i].x += (points[i-1].x - points[i].x) * 0.45; points[i].y += (points[i-1].y - points[i].y) * 0.45; } // 5. リボンの描画 ctx.lineJoin = 'round'; for(let i = 1; i < numPoints; i++) { ctx.beginPath(); ctx.moveTo(points[i-1].x, points[i-1].y); ctx.lineTo(points[i].x, points[i].y); const hue = (i * 5 - Date.now() * 0.1) % 360; ctx.strokeStyle = `hsl(${hue}, 100%, 60%)`; ctx.lineWidth = Math.max(1, 25 - (i / numPoints) * 25); ctx.stroke(); } // 6. 星屑パーティクルの計算と描画 for (let i = particles.length - 1; i >= 0; i--) { let p = particles[i]; p.x += p.vx; p.y += p.vy; // ヒット演出パーティクルは重力を弱く、ふわっと煌めかせる if (p.type === 'hit') { p.vy += 0.1; // 弱い重力 p.vx *= 0.98; // 少ない空気抵抗 p.vy *= 0.98; } else { p.vy += 0.35; // 通常の重力 p.vx *= 0.96; // 空気の抵抗 p.vy *= 0.96; } p.life -= p.decay; if (p.type === 'parent' && p.life <= p.splitTime && p.life > 0) { particles.splice(i, 1); const numChildren = Math.floor(10 + Math.random() * 5); for(let j = 0; j < numChildren; j++) { const angle = Math.random() * Math.PI * 2; const speed = 1.5 + Math.random() * 3; particles.push({ x: p.x, y: p.y, vx: p.vx * 0.3 + Math.cos(angle) * speed, vy: p.vy * 0.3 + Math.sin(angle) * speed, life: 0.3 + Math.random() * 0.1, decay: 0.02 + Math.random() * 0.03, size: Math.random() * 2 + 2.5, hue: p.hue + (Math.random() - 0.5) * 10, type: 'child' }); } } else if (p.life <= 0) { particles.splice(i, 1); } else { // 描画。ヒット演出は明るく if (p.type === 'hit') { ctx.fillStyle = `hsla(${p.hue}, 100%, 80%, ${p.life})`; // 明るい虹色 } else { ctx.fillStyle = `hsla(${p.hue}, 100%, ${p.type === 'child' ? 90 : 70}%, ${p.life})`; } ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fill(); } } requestAnimationFrame(animate); } animate();</script></body></html>
■ 今日の学び
(^^*)さて、今回のゲーム作りを通して感じた「学び」や、
込めた想いについて少しお話ししますね。
ゲームを作るたびに、毎回ちょっとした気づきがあって、
それがまた次のアイデアにつながっていきます。
今回のゲームは、「虹を自由自在に動かせたらいいな」という、
ふとした空想から始まりました。(^_^)
皆さんも、空に虹を見つけると、なんだか嬉しくなりませんか?
実際の虹って、彩雲やハロ、ダブルレインボーなど、
いろんな姿で現れてくれます。
その中でも、以前見た“色がくっきりした見事な虹”が、
まるで空を舞う新体操のリボンのように見えたことがあって……。
今回のゲームは、その時の記憶がそっと背中を押してくれました。(0゚*u0゚)
虹って、その時、その場所、その瞬間にしか見られない、とても特別なもの。
だからこそ、見つけた瞬間に胸がふわっと温かくなるんですよね。
今回のゲームでは、マウス操作によって、その瞬間にしか生まれない
「あなただけの虹の軌跡」を描けるようにしました。
クリックで弾ける星屑も、その場限りの煌めきです。
「その時、その場所だけしか見られない、特別な瞬間」
デジタルの中ではありますが、そんな一期一会の感覚を、
リボンの動きと光の演出で少しでも表現できていたら嬉しいです。(`・ω・´)ゞ
ぜひ、自分だけの特別な虹を、都会の夜空に描いてみてくださいね。
画像は、夕方の薄暗い時間にふと現れた虹です。
一日の終わりに、こんな優しい色に出会えると、
それだけで少し救われた気持ちになります。

(画像:夕焼け虹)
■ 次回は、癒され涼しいゲームです
不思議なドーナッツ状の小川の真ん中の島では、
狸が気持ちよさそうにお昼寝をしています。
川の流れに、いろんなものをそっと浮かべて流していく……
そんな、見ているだけでも涼しくなる癒しゲームです。
次回もぜひ遊びに来てくださいね。(*^v^)ノシ
✨今日も最後まで読んでくださって、ありがとうございます。
みーの実験室は、みなさんの応援で元気に動いています。(*^v^*)






🌈はじめての方へ
🌳 実験一覧
🌱 このブログについて
🍃 プライバシーポリシー
🐣未記事ゲーム展示室