実験 #16:空に絵を描く(お絵描き雲)

■16個目「お絵描き雲」

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

 今回は、雲を描いて、風で変化していく様子を楽しむお絵描きゲームです。

 以前製作しました動くお絵描き「シュミラクラお絵描きゲーム」に続いて、絵画ゲーム第2弾を作ってみました。

 日頃から何かに形の似た雲や、ヒコーキ雲を見かけると、青空へ自分の思い通りの雲の絵をかいてみたいなと、よく思ってました。(*=v=)~♪

 AIと相談すればできるかもしれないと思い、作ってみたのがこのゲームです。

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

(^^*)今日もゲーム作ります。空のキャンパスに雲で絵を描く。

 雲は、ある程度輪郭がわかるモフモフ感のある線で、マウスドラッグで描けます。
 色や太さは調整できて、描いた雲は風に少しずつ流されて形が変わっていきます。

 風は右から左へ、ほんの少しだけ吹いていて、
 クリアボタンを押すと突風が吹き、描いた雲が全部流されて消えていきます。

 その時だけの作品で、常に変化する形。
 不思議で、ちょっと切なくて、素敵なお絵描き体験を再現したゲームです。

(AI)お疲れ様です!お絵描きゲーム第2弾ですね。
 「形が常に変化し、風に流されて消えていく」というコンセプト、
 非常に風情があって素敵です。

(AI)常連さまへの、空の小話

 今日もいいお絵描きですね。
 雲の「輪郭」についてですが、実は気象学でも
 **「雲の境界線」**を定義するのはとても難しいことなんです。

 近くで見ると雲は霧のような水滴の集まりで、
 どこからが空でどこからが雲か、はっきりした線はありません。

 私たちが「あ、雲だ」と認識できるのは、
 光が水滴に当たって散乱(ミー散乱)しているからなんですね。

「境界線が曖昧だからこそ美しい」というのは、
 デジタルなお絵描きでも同じかもしれません。

(*^o^)青空に雲、へいわな感じがいいね。

(AI)常連さまへの、空の小話 その2

 スマホで外を歩きながら、ふと見上げた空を描く……なんて素敵ですね。

 雲がゆっくり崩れていく様子を眺めることは、
 マインドフルネス(瞑想)に近いリラックス効果があると言われています。

 ところで、雲には10種類の基本形(十種雲形)がありますが、
 このアプリならそのどれもが再現可能です。
 細く描けば「巻雲(すじ雲)」、大きく重ねれば「積雲(わた雲)」……。

〈最初の目的の雲のお絵描きが実現したので満足です。〉(*^^)

■ 実際の動作

 AIが最初に作ってくれたゲームの状態。
 白なのにくろいつぶつぶがびっしりに(><;)修正したのが次の画像です…

 筆の太さと、色を決めたらマウスですぐ描けます(*^_^*)「

 時間経過で、雲は少しづつ形が崩れ流れていきます・・・

 描いていくうちから崩れていくので、その時だけの作品です。
「突風を吹かせる」で一周で吹き飛びます!

 製作中にAIのイメージしたゲーム画面です。

■ コードのポイント

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

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

 操作はとても簡単で、筆の太さと色を調節して、マウスでドラッグするだけで描けます。突風を含ボタンを押すと、全部吹き飛んで消えます。

▼ここからコード▼(クリックで開く)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Cloud Artist - 雲のキャンバス</title>
<style>
body {
margin: 0;
overflow: hidden;
background: linear-gradient(to bottom, #4A90E2 0%, #87CEEB 100%);
font-family: 'Hiragino Kaku Gothic ProN', sans-serif;
user-select: none;
}
#ui {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
padding: 20px;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
z-index: 10;
display: flex;
flex-direction: column;
gap: 15px;
width: 250px;
}
.row { display: flex; flex-direction: column; gap: 5px; }
label { font-size: 12px; color: #333; font-weight: bold; }
canvas { display: block; touch-action: none; }
button {
padding: 12px;
border: none;
border-radius: 8px;
background: #fff;
color: #4A90E2;
font-weight: bold;
cursor: pointer;
box-shadow: 0 4px 0 #ccc;
transition: 0.1s;
}
button:active { transform: translateY(2px); box-shadow: 0 2px 0 #ccc; }
input[type="range"] { width: 100%; cursor: pointer; }
</style>
</head>
<body>
<div id="ui">
<div class="row">
<label>筆の太さ</label>
<input type="range" id="size" min="10" max="80" value="40">
</div>
<div class="row">
<label>雲の色み(白ベース)</label>
<input type="color" id="cloudColor" value="#ffffff">
</div>
<button id="clear">突風を吹かせる</button>
</div>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const sizeInput = document.getElementById('size');
const colorInput = document.getElementById('cloudColor');
const clearBtn = document.getElementById('clear');
let particles = [];
let isDrawing = false;
let isGusting = false;
// --- 高速化のためのテクニック:オフスクリーンキャンバスに雲の素を事前描画 ---
const spriteCanvas = document.createElement('canvas');
const sCtx = spriteCanvas.getContext('2d');
const spriteSize = 200;
spriteCanvas.width = spriteCanvas.height = spriteSize;
function updateCloudSprite(color) {
sCtx.clearRect(0, 0, spriteSize, spriteSize);
const grad = sCtx.createRadialGradient(spriteSize/2, spriteSize/2, 0, spriteSize/2, spriteSize/2, spriteSize/2);
grad.addColorStop(0, color + "44"); // 中心は少し濃い(16進数+透明度)
grad.addColorStop(0.5, color + "22");
grad.addColorStop(1, color + "00"); // 外側は完全に透明
sCtx.fillStyle = grad;
sCtx.fillRect(0, 0, spriteSize, spriteSize);
}
updateCloudSprite(colorInput.value);
colorInput.addEventListener('input', (e) => updateCloudSprite(e.target.value));
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
class Particle {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.originalSize = size;
this.size = size;
this.vx = -0.1 - Math.random() * 0.2; // 基本の風(右から左)
this.vy = (Math.random() - 0.5) * 0.1;
// 形を崩すためのノイズ速度
this.dx = (Math.random() - 0.5) * 0.2;
this.dy = (Math.random() - 0.5) * 0.2;
this.opacity = 1.0;
}
update() {
// 風による移動
this.x += this.vx;
this.y += this.vy;
// 徐々に形が崩れる(拡散)
this.x += this.dx;
this.y += this.dy;
// 突風
if (isGusting) {
this.vx -= 0.8;
this.opacity -= 0.02;
}
return (this.x + this.size > 0 && this.opacity > 0);
}
draw() {
ctx.globalAlpha = this.opacity;
// 事前描画したスプライトを描画(これが一番速い)
ctx.drawImage(spriteCanvas, this.x - this.size/2, this.y - this.size/2, this.size, this.size);
}
}
function getPos(e) {
if (e.touches) return { x: e.touches[0].clientX, y: e.touches[0].clientY };
return { x: e.clientX, y: e.clientY };
}
function startDrawing(e) { isDrawing = true; addParticles(e); }
function moveDrawing(e) { if (isDrawing) addParticles(e); }
function stopDrawing() { isDrawing = false; }
function addParticles(e) {
const pos = getPos(e);
const s = parseInt(sizeInput.value);
for(let i=0; i<2; i++) {
const ox = (Math.random() - 0.5) * s;
const oy = (Math.random() - 0.5) * s;
particles.push(new Particle(pos.x + ox, pos.y + oy, s * (0.8 + Math.random())));
}
}
canvas.addEventListener('mousedown', startDrawing);
canvas.addEventListener('mousemove', moveDrawing);
window.addEventListener('mouseup', stopDrawing);
canvas.addEventListener('touchstart', startDrawing);
canvas.addEventListener('touchmove', moveDrawing);
canvas.addEventListener('touchend', stopDrawing);
clearBtn.addEventListener('click', () => {
isGusting = true;
setTimeout(() => { particles = []; isGusting = false; }, 2000);
});
function loop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 描画モードを「重ねると明るくなる」設定にして雲の密度を表現
ctx.globalCompositeOperation = 'screen';
particles = particles.filter(p => {
const alive = p.update();
if (alive) p.draw();
return alive;
});
ctx.globalCompositeOperation = 'source-over';
requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>


■ 今日の学び

 前回の記事で、スマホでの遊ぶ方法もAiに教えてもらってたので、この「お絵描き雲」のゲームも早速入れてみました。

 今回は、スマホでの操作性と雲の表現について新しい発見がありました。
 指で雲が欠ける感じが魔法っぽくて面白い♪
 スマホで遊ぶほうが個人的には気に入ってます。

 気づけば R8.3.14 の時点で 約60本のゲームになりました。
 記事の更新ペースにはムラがありますが、少し間が空いているときは新しいゲーム作りに夢中になってると思ってください(つv<)

 次回は「錯視実験」ゲームです。
 どうぞお楽しみに(*^v^)ノシ

次の実験はこちら(アクションと錯視): 「しょうが君錯視ゲーム」



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

コメントを残す

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

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

続きを読む