実験#77:緑と手ざわり(癒しの緑の集合体)

■77個目 「癒しの緑の集合体」

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

今回は、日々の疲れをふわりと溶かしてくれるような、
ちょっと不思議で癒される空間を作ってみました。

そのテーマは……ずばり「無数のみどり」です!

画面の中に広がるのは、無重力空間をゆったりと漂う、
丸い苔玉のような緑の集合体。

今回こだわったのは、
なんといっても「一つとして同じものがない」ということです。(=v=*)

何千、何万と集まる緑の円たちは、大きさも違えば、
色合いもすべてバラバラ。

光を浴びた新芽のような黄緑、深い森の奥の暗緑色、水辺を思わせる青緑……。

無数の個性が集まって、ひとつの美しい塊を作っているんです。

まるで水の中をたゆたうように、やんわり、ゆらゆらと動く緑たち。

じーっと眺めているだけで、すーっと肩の力が抜けていくような、
そんな穏やかな世界を目指しました。(´▽`*)

さてさて、この不思議な緑の苔玉たちですが……
実は、ただ眺めているだけではありません。

画面の中で直接触れ合って遊ぶことができるんです!

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

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

みー:(^v^) 今日もゲームを作ります。
HTMLで動かせる、緑の集合体ゲームです。

何百もの緑の円が集まっていて、一つとして同じ色はありません。
大きさは似ているけれど、まったく同じものは存在しない。

丸い苔玉のような無数の緑が、無重力空間を水のように漂っています。

マウスカーソルで触れると、緑たちは優しく散らばり、
しばらくするとまた元の位置に戻って集合体の一部に戻る。

そんな癒しのゲームを作りたいです。

AI: 今日もまた素敵なアイデアですね。
無重力を漂う苔玉のような、一つとして同じもののない無数の緑。

触れると優しく散らばり、またゆっくり集まっていく……

想像しただけで肩の力が抜けるような、美しい癒しの空間です。

みー:(^o^) 緑の数をもっと増やして、存在感を高めたいです。
もっといろんな緑を入れることはできますか?

AI: かしこまりました。
では、緑の種類をさらに深く、豊かにブレンドしてみましょう。

春の新芽のような明るい黄緑から、森の奥の暗緑色、水辺を思わせる青緑まで、
ありとあらゆる「緑」を混ぜ込みました。

数の暴力……というより、圧倒的な生命力を感じていただけるはずです。

みー:(^_^) サイズは最初くらいでいいので、もっと濃縮した感じに。
白さが強すぎる緑は控えめにして、
触れた時の動きは少し吹き飛ぶくらいにしましょう。

AI: なるほど、少し眩しすぎましたね。
では、ベースのレシピに戻しつつ、より濃厚な味わいに仕立て直します。

白飛びの原因だった光の重なり(加算ブレンド)を外し、
深みのある緑から鮮やかな緑まで、落ち着いたトーンを中心に抽出しました。

元のコンパクトな器(サイズ)のまま、5000粒の緑をぎゅっと濃縮しています。

触れた時の反応も、グラスの氷をマドラーで弾くように強めに調整しました。

勢いよくフワッと散らばり、またゆっくり戻ってくるはずです。

みー:( ̄v ̄) いい感じです。緑の数はこれで限界ですか?
もっと増やせるなら見てみたいです。

AI: 限界、ですか。ふふ、お客様の探求心にはいつも驚かされます。

お使いのパソコンやスマートフォンの性能にもよりますが、限界はもう少し先にあります。 では今回は、一気に3倍の 1万5000粒 まで増やしてみましょう。まさに「特濃」のひと玉です。

数が多すぎるとただの緑の塊に見えてしまうため、
一粒一粒を少し小さくし、透け感を強めに調整しました。

これにより、1万5000の層が重なり合う
「圧倒的な密度と深み」を味わえるはずです。

■ 実際の動作

AI が作ってくれたゲーム画面がこちらです。
左上のバーで、緑の粒の個数を自由に調整できます。

● 15,000個のとき
画面いっぱいに“特濃の緑”が広がります。
密度が高く、まるで深い森の奥に入り込んだような雰囲気です。

(画像:15,000個)


● 10,000個のとき

少しだけ粒が大きくなり、全体の印象も柔らかくなります。
同じ緑でも、数が変わるだけで雰囲気がガラッと変わるのが面白いところ。

(画像:10,000個)

● 5,000個のとき

粒がさらに大きくなり、ひとつひとつの存在感が際立ちます。
“苔玉の集合体”らしさが一番わかりやすい状態かもしれません。

(画像:5,000個)

● マウス操作で動きが変わる

カーソルを近づけると、緑の粒たちがふわっと避けるように動きます。
まるで生き物のように、そっと道を開けてくれる感じです。

(画像:マウス近づける)


● クリックすると逆に吸い寄せられる

マウスの左クリックを押すと、今度は反対に、
粒たちがすーっと集まってきます。
触れ方によって反応が変わるので、眺めているだけでも楽しいです。

(画像:マウスクリック)


■ コードのポイント

今回の記事では、AI と相談しながら作った
「癒しの緑の集合体」 の HTML コードを公開します。

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

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

▼ 遊ぶまでの手順

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

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

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

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

 

■ 操作方法

気になる操作方法は……とってもシンプルです!(^_^)

① マウスでそっと触れる(フワッと吹き飛ぶ動き)

画面の中でマウスを動かして、緑の集合体に触れてみてください。
すると、触れた部分の緑たちが「ざばっ」と
心地よく吹き飛ぶように散らばります。(〇o〇*)

でも、少し待つと…… まるで名残惜しそうに、
やんわりと元の場所へ戻ってきて、またひとつの塊に。

この“健気な戻り方”が、とっても愛おしいんです。

② クリック長押しでギュッと引き寄せる(引力の魔法)

ここからがさらに面白いところ!
マウスの左クリックを押し続けると、今度は逆の力が働きます。

指先が小さなブラックホールになったみたいに、
遠くの緑たちが「ぎゅーっ」と吸い寄せられてくるんです。(≧∇≦*)

たくさん集めてからパッと離すと、
ふわああっと波のように広がっていく……
まるで“緑の魔法”を使っているような気分になれます。

③ 左上の「ツマミ」で濃さを調整(粒とサイズの不思議な連動)

画面左上には、緑の濃度を自由に変えられる
スライダー(バー)を付けています。(=v=*)

最初のすっきりした 1500粒 から、
画面がみどりでいっぱいになる 15000粒 まで、
いつでもツマミを動かして調節できます。

● 粒の数で見た目が変わる理由

実はこれ、ただ数が変わるだけじゃありません。

  • 粒が少ないとき  → 栄養を吸ったみたいに、
              一粒一粒が“ぷっくり大きく”育ちます。
  • 粒が多いとき  → 一粒が小さくなり、
              ぎゅっと濃縮された“超高密度のもふもふ感”に。

数が少ないときの“とろりとした重み”のある動きも、
数が多いときの“圧倒的な生命力”も、
どちらも違った癒しがあります。(´▽`*)

その日の気分やお疲れ度に合わせて、
あなたにとって一番心地いい
バランス(スイートスポット)を探してみてくださいね。


“コードは長いので折りたたんでいます。必要な方は開いてご覧ください。

▼ここからコード▼(クリックで開く)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>癒しの緑の集合体 - 引力コントロール</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
background-color: #060a06;
font-family: 'Helvetica Neue', Arial, sans-serif;
/* クリック中のテキスト選択などを防ぐ */
user-select: none;
-webkit-user-select: none;
}
canvas {
display: block;
}
#ui-container {
position: absolute;
top: 20px;
left: 20px;
background: rgba(10, 20, 10, 0.7);
padding: 15px 20px;
border-radius: 8px;
border: 1px solid rgba(50, 150, 50, 0.3);
color: rgba(150, 220, 150, 0.9);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
#ui-container label {
display: block;
font-size: 14px;
margin-bottom: 10px;
letter-spacing: 1px;
}
#particleCount {
width: 200px;
cursor: pointer;
accent-color: #4caf50;
}
#countDisplay {
font-weight: bold;
color: #81c784;
}
</style>
</head>
<body>
<div id="ui-container">
<label for="particleCount">緑の濃度: <span id="countDisplay">15000</span></label>
<input type="range" id="particleCount" min="1500" max="15000" step="100" value="15000">
</div>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
const particles = [];
let currentNumParticles = 15000;
// --- マウスの状態を管理 ---
const mouse = {
x: -1000,
y: -1000,
radius: 140, // 通常時の弾く範囲
isDown: false // 左クリックが押されているかどうか
};
window.addEventListener('mousemove', (e) => {
mouse.x = e.x;
mouse.y = e.y;
});
window.addEventListener('mouseout', () => {
mouse.x = -1000;
mouse.y = -1000;
mouse.isDown = false; // 画面外に出たらリセット
});
// 左クリックが押された時
window.addEventListener('mousedown', (e) => {
if (e.button === 0) { // 0は左クリック
mouse.isDown = true;
}
});
// 左クリックが離された時
window.addEventListener('mouseup', (e) => {
if (e.button === 0) {
mouse.isDown = false;
}
});
const PI2 = Math.PI * 2;
const MIN_COUNT = 1500;
const MAX_COUNT = 15000;
const MIN_SIZE_SCALE = 1.0;
const MAX_SIZE_SCALE = 4.0;
class Particle {
constructor() {
const angle = Math.random() * PI2;
const radiusDist = Math.pow(Math.random(), 0.8) * 200;
this.originX = canvas.width / 2 + Math.cos(angle) * radiusDist;
this.originY = canvas.height / 2 + Math.sin(angle) * radiusDist;
this.x = this.originX + (Math.random() - 0.5) * 300;
this.y = this.originY + (Math.random() - 0.5) * 300;
this.vx = 0;
this.vy = 0;
this.baseRadius = Math.random() * 1.5 + 1.0;
this.radius = this.baseRadius;
const h = Math.floor(Math.random() * 80 + 80);
const s = Math.floor(Math.random() * 50 + 40);
const l = Math.floor(Math.random() * 30 + 15);
const alpha = Math.random() * 0.3 + 0.3;
this.color = `hsla(${h}, ${s}%, ${l}%, ${alpha})`;
this.randomAngle = Math.random() * PI2;
this.randomSpeed = Math.random() * 0.002 + 0.001;
}
update(time) {
const t = (currentNumParticles - MIN_COUNT) / (MAX_COUNT - MIN_COUNT);
const scale = MAX_SIZE_SCALE + (MIN_SIZE_SCALE - MAX_SIZE_SCALE) * t;
this.radius = this.baseRadius * scale;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const currentAngle = Math.atan2(this.originY - centerY, this.originX - centerX);
const currentDist = Math.hypot(this.originX - centerX, this.originY - centerY);
const floatX = Math.cos(time * this.randomSpeed + this.randomAngle) * 15;
const floatY = Math.sin(time * this.randomSpeed + this.randomAngle) * 15;
const targetX = centerX + Math.cos(currentAngle) * currentDist + floatX;
const targetY = centerY + Math.sin(currentAngle) * currentDist + floatY;
const dx = mouse.x - this.x;
const dy = mouse.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
// --- 引力と反発のコントロール ---
// クリック中は影響範囲を少し広げて、吸い込みやすくする
const effectiveRadius = mouse.isDown ? mouse.radius * 2.0 : mouse.radius;
if (distance < effectiveRadius) {
const forceDirectionX = dx / distance;
const forceDirectionY = dy / distance;
const force = (effectiveRadius - distance) / effectiveRadius;
if (mouse.isDown) {
// 【吸い寄せる】クリック中
const pullStr = 1.2;
// 中心に集まりすぎて弾け飛ばないよう、一定距離以上離れている時だけ引力をかける
if (distance > 10) {
this.vx += forceDirectionX * force * pullStr;
this.vy += forceDirectionY * force * pullStr;
} else {
// 中心付近では摩擦を強くして動きを落ち着かせる
this.vx *= 0.8;
this.vy *= 0.8;
}
} else {
// 【吹き飛ばす】通常時(これまで通り)
const pushStr = 3.0;
this.vx -= forceDirectionX * force * pushStr;
this.vy -= forceDirectionY * force * pushStr;
}
}
const spring = 0.01;
this.vx += (targetX - this.x) * spring;
this.vy += (targetY - this.y) * spring;
const friction = 0.92;
this.vx *= friction;
this.vy *= friction;
this.x += this.vx;
this.y += this.vy;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, PI2);
ctx.fillStyle = this.color;
ctx.fill();
}
}
for (let i = 0; i < currentNumParticles; i++) {
particles.push(new Particle());
}
const countSlider = document.getElementById('particleCount');
const countDisplay = document.getElementById('countDisplay');
countSlider.addEventListener('input', (e) => {
const newCount = parseInt(e.target.value, 10);
currentNumParticles = newCount;
countDisplay.textContent = currentNumParticles;
if (newCount > particles.length) {
const diff = newCount - particles.length;
for (let i = 0; i < diff; i++) {
particles.push(new Particle());
}
}
else if (newCount < particles.length) {
particles.length = newCount;
}
});
function animate(time) {
ctx.fillStyle = 'rgba(6, 10, 6, 0.4)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
particles.forEach(particle => {
particle.update(time);
particle.draw();
});
requestAnimationFrame(animate);
}
animate(0);
</script>
</body>
</html>


■ 今日の学び

今回のゲームには、私の「みどり」への愛がたっぷり詰まっています。

とにかく 緑色が大好き なんです。
きれいなお花ももちろん素敵ですが、
どうしても葉っぱの緑に心を奪われてしまいます。

特に新緑の季節になると、世界中にいろんな種類の緑があふれて、
それだけで心が満たされます。

風に揺れる木の葉が、それぞれ違うリズムでサラサラと踊るように揺れる――
その光景をぼーっと眺めている時間が、私にとって最高の癒やしです。

そして、苔(こけ)の渋い緑色もたまりません……!
丸い苔玉、マリモ、スイカなど、
“丸くて緑のもの” は、私にとって癒しがそのまま形になったような存在です。

そんな大好きな「いろんな緑」を、画面いっぱいに表現して、
しかも自分の手で触って、かき混ぜてみたい――。

そんな“好き”を全力で詰め込んだのが、今回のゲームです。

そして、いつも「こういうのが見たい!」という私のわがままを
形にするのを手伝ってくれる AI には、本当に感謝しています。

“好き”を思いきり実現してくれる存在は、とても心強いです。

気づけば、この実験室で作ってきたゲームは 140作品を超えていました。

最初は小さな実験から始まった場所が、
“好き”を原動力に続けてきたことで、
こんなにたくさんの作品たちが生まれていました。

これからも、“好き”から生まれる、 ちょっと不思議で、
でもどこかホッとするゲームを どんどん増やしていきたいと思います。

140作を超えても、実験室はまだまだ止まりません。

”好き”で育てている庭も、緑でいっぱいのグリーンガーデンに。(*^v^*)

(画像:緑の庭の一部)

■ 次回は、 風船のシミュレーターゲームです。

シンプルでカラフルな風船を、ぷくーっと膨らませて飛ばすゲーム。
膨らませすぎると……もちろん、パーンと割れます。

飛ばした風船は、たくさん飛ばせば飛ばすほど、
あとで気持ちよく割ることができます。

ちょっとしたストレス発散にもぴったりの、
軽やかで楽しいミニゲームになっています。

次回もぜひ遊びに来てくださいね。(*^v^)ノシ

次の実験はこちら  
  (膨らまして飛ばして割る
「風船膨張・飛翔シミュレーター」



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

~このブログについて~

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

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


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

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

続きを読む