■69個目 「究極のふわふわ」
こんにちは、AIと作る100のゲーム実験室の みー です。(*^_^*)/
今回は、いろいろな「ふわふわ」の感触をひとつの画面に
ぎゅっと集めたシミュレーターゲームを作ってみました。
「もし、画面の向こう側の質感を、マウス越しに指先で感じられたら……?」
そんな小さな願いから始まった今回の実験。
まずは「動物の毛並み」のなで心地を再現し、
次に「羽毛のほわほわ感」、
そして「水風船のようなぷにっとした弾力」。
3つの異なる心地よさを組み合わせて生まれたのが、
画面中央でぷかぷかと漂う不思議な「丸いふわふわ」です。
さらにおまけとして、この毛玉に「スヤスヤ眠るハムスター」のような、
ふんわり優しい表情も付けてみました。(^_^)
ただ浮かんでいるだけではなく、
あなたのアクションに合わせていろいろな反応を見せてくれます。
■ AIとのやりとりで面白かった部分
● ゲーム仕様の相談からスタート
みー:(^v^) 今日もゲームを作ります。html形式で動かせるゲーム。
今回は「ふわふわの再現」をどこまでできるかの検証です。
動物の毛(ハムスターなど)、羽毛、植物の綿毛(たんぽぽ・ラムズイヤー)、
お菓子のふわふわ(シフォンケーキ・ホイップ・綿菓子)、
クッションやスポンジなど……。
まずは動物の体毛のふわふわから再現できそうかな。
AI: 「AIと作る100のゲーム実験室」、
本日のテーマは『ふわふわ』の再現ですね。
視覚とマウス操作だけで“触っている錯覚”を生み出す、
とても面白い挑戦です。
まずは王道の「動物の体毛」からいきましょう。
キャンバス上に数千本の毛を描画し、
マウスの動きに合わせて毛がなびいたり
起き上がったりする物理演算を組んでみました。
みー:(*^o^*)すごくたのしい。癒されます。
もう少し柔らかめとか、毛先を細くとかもできるのかな。
AI: もちろん可能です。
より極上のふわふわ感を出すために、コードの心臓部を少し調整しましょう。
ちなみに究極の柔らかさを持つ動物といえばチンチラ。
1つの毛穴から50〜80本も毛が生えているため、
あの圧倒的なふわふわ感が生まれるんですよ。
● 羽毛のふわふわへ
みー:(^v^) 続いて羽毛のふわふわ感のリアル再現。
触れて舞うイメージできるかな。
AI: 動物の毛の「撫でる」感触から一転、
今度は「空気をはらんで舞う」羽毛ですね。
羽毛のふわふわ感は「軽さ」と「空気抵抗」。
重力より空気の影響を強く受けるため、ゆっくり落ちたり、
少しの風(マウス)でふわりと舞い上がる物理演算を組み込みました。
● スポンジの「ぶにっ」とした柔らかさ
みー:(^v^) 続いて柔らかスポンジのふわふわ感も再現してみましょう。
AI: 毛や羽毛が「空気」や「しなり」の表現だったのに対し、
スポンジは「弾性体」のシミュレーションになります。
指で押し込むと「ぐっ」、離すと「ぽふっ」と戻る、あの感触ですね。
みー:(^v^) もっと大きくぶにっと感を出せるかな。
形も丸くしましょう。
AI: 了解です。 四角いグリッド構造だった前回から、
今回は「表面張力」と「水風船の弾力」を持つ
円形モデルへ大幅に変更しました。
内部にゼリーや水が詰まっているような質感を出すため、
円周上に60個の点を配置し、
互いに引っ張り合いながら丸い形を保つ計算式を使っています。
● 3つのふわふわを合体!
みー:(^v^) 最初の毛玉と羽毛と、水風船のようなの。
この3つのふわふわを1つにまとめてみましょう。
イメージとしては、毛玉+水風船の一体化で、周りに羽毛が漂う感じ。
AI: 毛玉、羽毛、水風船……3つの物理演算を統合する。
いよいよ本格的なゲームエンジンの領域に入りましたね。
「押し込むと変形するベース(水風船)」の表面座標をリアルタイム計算し、
そこに「何千本もの毛」の根元を追従させています。
さらに周囲には、独立した空気抵抗を持つ羽毛を漂わせました。
重くならないよう最適化も施してあります。
エディタに貼り付けて、クリックしたままぐりぐりと撫でてみてください。
(このあと、ふわふわな表情・動き・効果音も追加しました。)
■ 実際の動作
こちらが、AIが最初に作ってくれた「毛のふわふわシミュレーター」です。
なんだか、いがぐりみたいな見た目です。

(画像:最初のふわふわ)
そして、こちらが完成版。
上から羽毛がふわふわと降ってきて、
マウスカーソルの動きに合わせて舞い上がります。

(画像:羽毛が舞うふわふわ)
これは通常状態のふわふわ玉。
マウスを近づけると、ふわっと避けるように動きます。
画面中央に“ゆるい重力”を設定しているので、
離すと自然に中央へ戻っていきます。

(画像:ふわふわ玉)
掴んで動かすと、こんなふうに形が変わります。
水風船のように、むにっと伸びて、ゆっくり元に戻ります。

(画像:変形するふわふわ)
毛をなでると、動きに合わせてふわふわと毛が動きます。
そして、表情もふわふわな雰囲気です。

(画像:ふわふわ)
■ コードのポイント
今回の記事では、AI と相談しながら作った
「究極のふわふわ」 の HTML コードを公開します。
このコードをそのまま保存するだけで、
あなたのPCでもすぐに遊べます。
ブラウザだけで動く、とてもシンプルなゲームなので、
気軽に試してみてくださいね。
▼ 遊ぶまでの手順
①PC の「メモ帳」(※Windowsの場合)や
Mac の「テキストエディット」など、テキストエディタを開きます。
②下のコードをコピーして貼り付けます。
③「fuwafuwa .html」など、好きな名前で保存します。
(※拡張子は .html にしてください)
④保存したファイルをブラウザで開けば、すぐに遊べます。
■ 遊び方・操作説明
基本はマウスを使うだけの、とってもシンプルな操作です。
ぜひ、いろんな触り方を試して癒やされてみてくださいね。
① そっと近づいてみる(マウスカーソルを近づける)
丸いふわふわにカーソルを近づけると、
空気の風圧に反応して ぽわ〜ん とゆっくり逃げていきます。
まるで風船のように、画面の中をふわふわ漂う姿を楽しめます。
マウスをサッと動かすと、周りを舞っている羽毛も フワッ と
風にのって舞い上がります。
② 優しくなでる・つかむ(クリックしたまま動かす)
ふわふわの上でクリックしたままにすると、
逃げるのをやめて大人しくなります。
そのまま、ぐりぐりとなでてみてください。(^_^)
水風船のような ぶにっ とした弾力と、
毛並みが ふさっ と揺れる感触が同時に味わえます。
スヤスヤ眠るお顔もむにゅっと潰れるので、
お餅みたいに柔らかくなった姿も楽しめます。
③ じっと見守る(操作しない)
何もせずに放っておくと、ふわふわ玉は ゆっくりと
画面中央の“定位置”へ戻っていきます。
ただ漂いながら眠り続ける姿を眺めているだけでも、
とても癒やされます。(=v=*)
休日のリラックスタイムや、ちょっと疲れたときの息抜きに、
ぜひこの「究極のふわふわ」を触りにきてくださいね。
“コードは長いので折りたたんでいます。必要な方は開いてご覧ください。”
▼ここからコード▼(クリックで開く)
<html lang="ja"><head><meta charset="UTF-8"><title>究極のふわふわキメラ(サウンド付き・居眠りハムスター妖精)</title><style> body { margin: 0; overflow: hidden; background: linear-gradient(135deg, #a1c4fd 0%, #c2e9fb 100%); display: flex; justify-content: center; align-items: center; height: 100vh; color: #333; font-family: sans-serif; } canvas { background-color: transparent; cursor: grab; } canvas:active { cursor: grabbing; } #instructions { position: absolute; bottom: 20px; text-align: center; line-height: 1.5; pointer-events: none; color: #4a5568; font-weight: bold; text-shadow: 0 1px 3px rgba(255,255,255,0.8); } #start-screen { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.85); display: flex; justify-content: center; align-items: center; flex-direction: column; z-index: 100; cursor: pointer; font-size: 24px; font-weight: bold; color: #4a5568; } #start-screen span { font-size: 16px; font-weight: normal; margin-top: 10px; color: #718096; }</style></head><body> <div id="start-screen"> 画面をクリックしてスタート <span>(ふんわりした音が出ます)</span> </div> <canvas id="canvas"></canvas> <div id="instructions"> 近づくと風圧で「ぽわんっ」と動きます。<br> クリックしながら撫でると大人しくなり、「むにゅっ」と癒やしてくれます。 </div> <script> // ========================================== // サウンドエンジン (Web Audio API) // ========================================== let audioCtx; function initAudio() { const AudioContext = window.AudioContext || window.webkitAudioContext; audioCtx = new AudioContext(); playBGM(); } // BGM:ゆったりとした三角波のオルゴール風アンビエント function playBGM() { if (!audioCtx) return; const notes = [261.63, 329.63, 392.00, 523.25]; // C, E, G, C let step = 0; setInterval(() => { let osc = audioCtx.createOscillator(); let gain = audioCtx.createGain(); osc.type = 'triangle'; // 角の丸い柔らかい波形 osc.frequency.value = notes[step % notes.length] / 2; // 1オクターブ下で落ち着きを // ふんわり鳴って、ふんわり消える gain.gain.setValueAtTime(0, audioCtx.currentTime); gain.gain.linearRampToValueAtTime(0.08, audioCtx.currentTime + 1.0); gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 3.0); osc.connect(gain); gain.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 3.0); step++; }, 2500); // 2.5秒間隔でゆっくり } // 効果音:逃げる時の「ぽわん」 function playFuwa() { if (!audioCtx) return; let osc = audioCtx.createOscillator(); let gain = audioCtx.createGain(); osc.type = 'sine'; // 一番丸い波形 osc.frequency.setValueAtTime(300, audioCtx.currentTime); osc.frequency.exponentialRampToValueAtTime(150, audioCtx.currentTime + 0.4); gain.gain.setValueAtTime(0.15, audioCtx.currentTime); gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.4); osc.connect(gain); gain.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 0.4); } // 効果音:掴んだ時の「むにゅっ」 function playBuni() { if (!audioCtx) return; let osc = audioCtx.createOscillator(); let gain = audioCtx.createGain(); osc.type = 'sine'; osc.frequency.setValueAtTime(200, audioCtx.currentTime); osc.frequency.exponentialRampToValueAtTime(500, audioCtx.currentTime + 0.15); // ピッチが上がる gain.gain.setValueAtTime(0.2, audioCtx.currentTime); gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.2); osc.connect(gain); gain.connect(audioCtx.destination); osc.start(); osc.stop(audioCtx.currentTime + 0.2); } // ========================================== // ゲームメインロジック // ========================================== document.getElementById('start-screen').addEventListener('click', function() { this.style.display = 'none'; initAudio(); }); const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const centerX = canvas.width / 2; const centerY = canvas.height / 2; let blobCenterX = centerX; let blobCenterY = centerY; let blobVX = 0; let blobVY = 0; let mouseX = -1000; let mouseY = -1000; let pmouseX = -1000; let pmouseY = -1000; let mouseVX = 0; let mouseVY = 0; let isMouseDown = false; let wasHovered = false; // 音の連続再生を防ぐためのフラグ window.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; mouseVX = mouseX - pmouseX; mouseVY = mouseY - pmouseY; pmouseX = mouseX; pmouseY = mouseY; }); window.addEventListener('mousedown', () => { isMouseDown = true; playBuni(); // 掴んだ時の効果音 }); window.addEventListener('mouseup', () => isMouseDown = false); window.addEventListener('mouseleave', () => { isMouseDown = false; mouseX = -1000; }); window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); const numPoints = 40; const baseRadius = 130; const blobPoints = []; const stiffness = 0.03; const surfaceTension = 0.2; const frictionBlob = 0.85; class BlobPoint { constructor(angle) { this.angle = angle; this.ox = blobCenterX + Math.cos(angle) * baseRadius; this.oy = blobCenterY + Math.sin(angle) * baseRadius; this.x = this.ox; this.y = this.oy; this.vx = 0; this.vy = 0; } updateTarget() { this.ox = blobCenterX + Math.cos(this.angle) * baseRadius; this.oy = blobCenterY + Math.sin(this.angle) * baseRadius; } } for (let i = 0; i < numPoints; i++) { blobPoints.push(new BlobPoint((i / numPoints) * Math.PI * 2)); } function updateBlob() { let dxCenter = centerX - blobCenterX; let dyCenter = centerY - blobCenterY; blobVX += dxCenter * 0.001; blobVY += dyCenter * 0.001; let dxMouse = blobCenterX - mouseX; let dyMouse = blobCenterY - mouseY; let distMouse = Math.sqrt(dxMouse * dxMouse + dyMouse * dyMouse); let influenceRange = baseRadius + 140; // 触れた瞬間に一度だけ「ぽわん」と鳴らす if (distMouse < influenceRange && !isMouseDown) { if (!wasHovered) { playFuwa(); wasHovered = true; } let escapeForce = 0.0015; let windForce = 0.006; let force = (influenceRange - distMouse) * escapeForce; blobVX += (dxMouse / distMouse) * force; blobVY += (dyMouse / distMouse) * force; blobVX += mouseVX * windForce; blobVY += mouseVY * windForce; } else if (distMouse > influenceRange + 50) { wasHovered = false; // 離れたらフラグをリセット } blobVX *= 0.94; blobVY *= 0.94; blobCenterX += blobVX; blobCenterY += blobVY; for (let i = 0; i < numPoints; i++) { blobPoints[i].updateTarget(); } for (let i = 0; i < numPoints; i++) { let p = blobPoints[i]; p.vx += (p.ox - p.x) * stiffness; p.vy += (p.oy - p.y) * stiffness; if (isMouseDown) { let dx = p.x - mouseX; let dy = p.y - mouseY; let dist = Math.sqrt(dx * dx + dy * dy); if (dist < 120) { let force = (120 - dist) * 0.04; p.vx += (dx / dist) * force; p.vy += (dy / dist) * force; } } } for (let i = 0; i < numPoints; i++) { let p = blobPoints[i]; let pPrev = blobPoints[(i - 1 + numPoints) % numPoints]; let pNext = blobPoints[(i + 1) % numPoints]; let targetX = (pPrev.x + pNext.x) / 2; let targetY = (pPrev.y + pNext.y) / 2; p.vx += (targetX - p.x) * surfaceTension; p.vy += (targetY - p.y) * surfaceTension; } for (let i = 0; i < numPoints; i++) { let p = blobPoints[i]; p.vx *= frictionBlob; p.vy *= frictionBlob; p.x += p.vx; p.y += p.vy; } } const hairs = []; const numHairs = 1800; class Hair { constructor() { this.baseAngle = Math.random() * Math.PI * 2; this.rRatio = Math.sqrt(Math.random()); this.length = 25 + Math.random() * 30; this.targetAngle = this.baseAngle + (Math.random() - 0.5) * 0.5; this.currentAngle = this.targetAngle; this.stiffness = 0.01 + Math.random() * 0.02; let hue = 35 + Math.random() * 10; let sat = 30 + Math.random() * 20; let light = 70 + Math.random() * 25; this.color = `hsla(${hue}, ${sat}%, ${light}%, 0.8)`; this.thickness = 1.5 + Math.random() * 2.0; } update() { let idx = (this.baseAngle / (Math.PI * 2)) * numPoints; let i1 = Math.floor(idx) % numPoints; let i2 = (i1 + 1) % numPoints; let t = idx - i1; let sx = blobPoints[i1].x * (1 - t) + blobPoints[i2].x * t; let sy = blobPoints[i1].y * (1 - t) + blobPoints[i2].y * t; this.baseX = blobCenterX + (sx - blobCenterX) * this.rRatio; this.baseY = blobCenterY + (sy - blobCenterY) * this.rRatio; this.tipX = this.baseX + this.length * Math.cos(this.currentAngle); this.tipY = this.baseY + this.length * Math.sin(this.currentAngle); let dx = mouseX - this.tipX; let dy = mouseY - this.tipY; let dist = Math.sqrt(dx * dx + dy * dy); let interactionRadius = isMouseDown ? 100 : 50; if (dist < interactionRadius) { let speed = Math.sqrt(mouseVX * mouseVX + mouseVY * mouseVY); if (speed > 0.5) { let moveAngle = Math.atan2(mouseVY, mouseVX); let force = Math.min(speed * 0.015, 0.4); let diff = moveAngle - this.currentAngle; while (diff < -Math.PI) diff += Math.PI * 2; while (diff > Math.PI) diff -= Math.PI * 2; this.currentAngle += diff * force; } } else { let diff = this.targetAngle - this.currentAngle; while (diff < -Math.PI) diff += Math.PI * 2; while (diff > Math.PI) diff -= Math.PI * 2; this.currentAngle += diff * this.stiffness; } } draw() { this.tipX = this.baseX + this.length * Math.cos(this.currentAngle); this.tipY = this.baseY + this.length * Math.sin(this.currentAngle); let cpX = this.baseX + (this.length * 0.5) * Math.cos(this.currentAngle); let cpY = this.baseY + (this.length * 0.5) * Math.sin(this.currentAngle); let nx = Math.cos(this.currentAngle + Math.PI / 2); let ny = Math.sin(this.currentAngle + Math.PI / 2); let halfThick = this.thickness / 2; ctx.beginPath(); ctx.moveTo(this.baseX + nx * halfThick, this.baseY + ny * halfThick); ctx.quadraticCurveTo(cpX + nx * (halfThick * 0.4), cpY + ny * (halfThick * 0.4), this.tipX, this.tipY); ctx.quadraticCurveTo(cpX - nx * (halfThick * 0.4), cpY - ny * (halfThick * 0.4), this.baseX - nx * halfThick, this.baseY - ny * halfThick); ctx.closePath(); ctx.fillStyle = this.color; ctx.fill(); } } for (let i = 0; i < numHairs; i++) { hairs.push(new Hair()); } const downs = []; const numDowns = 60; class Down { constructor() { this.x = Math.random() * canvas.width; this.y = Math.random() * canvas.height; this.vx = (Math.random() - 0.5) * 0.5; this.vy = Math.random() * 0.5 + 0.1; this.size = 10 + Math.random() * 15; this.angle = Math.random() * Math.PI * 2; this.spin = 0; this.drag = 0.96 + Math.random() * 0.03; this.barbs = []; let barbsCount = 15 + Math.random() * 10; for(let i = 0; i < barbsCount; i++) { this.barbs.push({ angle: Math.random() * Math.PI * 2, length: this.size * (0.6 + Math.random() * 0.6), curve: (Math.random() - 0.5) * this.size * 1.5 }); } } update() { this.vx *= this.drag; this.vy *= this.drag; this.vy += 0.015; let dx = this.x - mouseX; let dy = this.y - mouseY; let dist = Math.sqrt(dx * dx + dy * dy); if (dist < 200) { let force = (200 - dist) / 200; this.vx += mouseVX * force * 0.05; this.vy += mouseVY * force * 0.05; this.vx += (dx / dist) * force * 1.5; this.vy += (dy / dist) * force * 1.5; this.spin += (mouseVX - mouseVY) * force * 0.005; } this.angle += this.spin; this.spin *= 0.95; this.x += this.vx; this.y += this.vy; if (this.y > canvas.height + 50) { this.y = -50; this.x = Math.random() * canvas.width; this.vy = Math.random() * 0.5; this.vx = 0; } if (this.x > canvas.width + 50) this.x = -50; if (this.x < -50) this.x = canvas.width + 50; } draw() { ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(this.angle); ctx.beginPath(); ctx.arc(0, 0, this.size * 0.2, 0, Math.PI * 2); ctx.fillStyle = 'rgba(255, 255, 255, 0.4)'; ctx.fill(); ctx.lineWidth = 0.8; this.barbs.forEach(barb => { ctx.beginPath(); ctx.moveTo(0, 0); let endX = Math.cos(barb.angle) * barb.length; let endY = Math.sin(barb.angle) * barb.length; let cpX = Math.cos(barb.angle) * (barb.length * 0.5) - Math.sin(barb.angle) * barb.curve; let cpY = Math.sin(barb.angle) * (barb.length * 0.5) + Math.cos(barb.angle) * barb.curve; ctx.quadraticCurveTo(cpX, cpY, endX, endY); ctx.strokeStyle = `rgba(255, 255, 255, ${0.3 + Math.random() * 0.3})`; ctx.stroke(); }); ctx.restore(); } } for (let i = 0; i < numDowns; i++) { downs.push(new Down()); } function drawHamsterFace() { ctx.save(); ctx.translate(blobCenterX, blobCenterY); let lookX = (mouseX - blobCenterX) * 0.08; let lookY = (mouseY - blobCenterY) * 0.08; lookX = Math.max(-12, Math.min(12, lookX)); lookY = Math.max(-12, Math.min(12, lookY)); ctx.translate(lookX, lookY); ctx.fillStyle = 'rgba(255, 130, 130, 0.5)'; ctx.beginPath(); ctx.ellipse(-35, 12, 16, 10, 0, 0, Math.PI * 2); ctx.ellipse(35, 12, 16, 10, 0, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; ctx.beginPath(); ctx.ellipse(0, 10, 22, 14, 0, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#5c4033'; ctx.lineWidth = 4; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(-45, -2); ctx.quadraticCurveTo(-30, 8, -15, -2); ctx.moveTo(15, -2); ctx.quadraticCurveTo(30, 8, 45, -2); ctx.stroke(); ctx.fillStyle = '#ff8888'; ctx.beginPath(); ctx.ellipse(0, 6, 6, 4, 0, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.moveTo(-8, 14); ctx.quadraticCurveTo(0, 20, 8, 14); ctx.stroke(); ctx.restore(); } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); mouseVX *= 0.85; mouseVY *= 0.85; updateBlob(); ctx.beginPath(); let midX = (blobPoints[0].x + blobPoints[1].x) / 2; let midY = (blobPoints[0].y + blobPoints[1].y) / 2; ctx.moveTo(midX, midY); for (let i = 1; i < blobPoints.length; i++) { let nextI = (i + 1) % blobPoints.length; let nextMidX = (blobPoints[i].x + blobPoints[nextI].x) / 2; let nextMidY = (blobPoints[i].y + blobPoints[nextI].y) / 2; ctx.quadraticCurveTo(blobPoints[i].x, blobPoints[i].y, nextMidX, nextMidY); } ctx.closePath(); ctx.fillStyle = '#eed9c4'; ctx.fill(); hairs.forEach(hair => { hair.update(); hair.draw(); }); drawHamsterFace(); downs.forEach(down => { down.update(); down.draw(); }); requestAnimationFrame(animate); } animate(); </script></body></html>
■ 今日の学び
「ふわふわ」というオノマトペをシミュレーションゲームにしたら、
一体どんな“手触り”になるんだろう?
今回は、そんな素朴な好奇心から始まった実験でした。(^_^)
私がハムスター好きということもあって、
途中でスヤスヤ眠る表情などを足していくうちに、
最終的にはすっかり“かわいいハムスターの
ふわふわ玉”のような姿になりました。(^^*)
でも、この作品の裏側にある本当のテーマは、
最初から最後まで一貫して 「究極のふわふわ」 を追求することでした。
今回の実験を通して得た気づきは、大きく分けてこの2つです。
- AIと一緒にリアリティを探求する面白さ
毛先の細さや空気抵抗など、「もっとリアルにしたい!」と
AIに相談しながら調整していく過程がとても刺激的でした。
ブラウザ上とは思えないほどの心地よさに近づけたのは、
この対話のおかげです。(=v=*) - “感覚そのもの”を作品にする楽しさ
視覚・音・マウス操作を組み合わせるだけで、
画面越しでも“触っている感覚”が生まれることに驚きました。
ゲームというより、ひとつの“触感アート”に近い体験になった気がします。
明確なクリアやスコアはありませんが、
そのぶん「ふわふわに特化した世界観」を、
時間を忘れてゆったり楽しんでもらえたら嬉しいです。(´▽`)
■ 次回は、レトロな雰囲気のコレクションゲームです。
日常にひそむ “バグモン” を見つけていくゲームで、
クリックすると姿を現し、ちょっと不思議な形に変形します。
見つけたバグモンは図鑑にコレクションされていくので、
特徴的なバグたちをぜひコンプリートしてみてください。
次回もぜひ遊びに来てくださいね。(*^v^)ノシ
✨今日も最後まで読んでくださって、ありがとうございます。
みーの実験室は、みなさんの応援で元気に動いています。(*^v^*)






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