床をピカピカにするミニゲーム
おや? 床が黒いペンキで汚れてしまったようですよ。メイドさんに指示してお掃除してもらいましょ。
昨日UPした「スカートの中を撮影するミニゲーム」のコメント欄で、天才的な発明のアクリルスタンドを見てどうしても作りたくなりました。
使用ソフト
| StableDiffusion | ベース絵の出力。 |
| GIMP | 地面に写るキャラの変形、ちびキャラの背景切り抜き、マスク用画像の作成に使用。 |
使用フォント
| ニコモジ | オープンフォントライセンスで配布されているWebフォント。英数ひらがなカタカナだけだけどゲームっぽいデザインで使いやすいため愛用している。 |
ベース絵
立ち絵2枚
Prompt:
,1girl,loli,small breasts,navy blue school uniform with pleated skirt
,clear face
,standing,full body
,simple white background
,(white panties,from below:0)
Negative prompt: (worst quality, low quality:1.2),white panties
Steps: 25, Sampler: Euler a, CFG scale: 7, Seed: 1702493476, Size: 1024x1024, Model hash: fdffd3c312, Model: ambientmix-half, Denoising strength: 0.75, Clip skip: 2, Mask blur: 4
最初はAIイラスト1枚でいけるかな?と思ったんですよ。
standing on mirrorとか、reflected floorみたいな文言で、キャラの立ち絵+床に写ったパンツの表現ができるかと思ったのだけれど、そんな甘いことはなかった。
というのも、AIイラストは「鏡」という概念を理解しているわけではないので、mirrorの文言があると鏡の中に写った少女は描けるけど、鏡と少女を別々には描けない。
よしんば2人の少女が出てきても、それは鏡の中にいる少女Aと鏡の外にいる少女Bでそれらは別人。Twinsとか指定すればいけるかもしれないけど、どのみちこのパターンで出てくる鏡は壁掛け鏡のみ。
床に鏡が置いた状態の絵もたまに出せるけれど、そこにキャラが映ることは稀で、写ったとしてもくずれる。人物の一部分しか映っていないからか、人とは認識しないため、ぐちゃぐちゃのクリーチャーが鏡の中に表示されることが多い。
何千回も試行すれば1枚くらいは出そうな気配もあったけど、そこまでする気力がなかったので、上下反転した絵を作ることにしました。
上の例でわざわざNegativeにwhite pantiesが入っているのは、先にパンチラ絵を作ってInpaint(部分修正)したため。
つまり、こっちが先。このパンツ部分を黒塗りして、スカートで隠したバージョンをimg2imgで作りました。
GIMPによる合成
単にさかさまにして縦に貼り付けるだけだとちょっと違和感あったので、ケージ変形を使用。
上記のように台形上に太もも方向を太くし、上下反転して繋げた。
ちびキャラ
Prompt:
1girl wiping the floor with a mop
,chibi,from side,simple white background
Negative prompt: (worst quality, low quality:1.2)
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 1785562823, Size: 512x512, Model hash: fdffd3c312, Model: ambientmix-half, Clip skip: 2
かわいい。最高。
ちびキャラ生成してるときが一番楽しい、まである。
GIMPでマスクデータ作成
黒ペンキを消すときにこのマスクデータの形状で消えていく処理にしています。
最初はリアルの掃除と同じで、モップの先端だけで掃除していたのだけれど、ぜんぶのペンキ除去するのにえらい時間かかって面倒くせーなと感じたので、上記のようになりました。
つまり、このメイドさんは体全体で掃除している。すごい。
技術解説
JavaScript初心者向けの解説…なのだけれど、canvasによるグラフィック操作なので本当の初心者にはちと難しいかも知れない。canvasの概念から説明はじめると別ブログ開設できそうな分量になるため、申し訳ないが最低限canvasについては理解している前提で、このゲーム特有の処理だけ解説する。
HTML部分
<div class="img_wrapper" id="canvas_wrapper">
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
<canvas id="canvas3"></canvas>
<div id="cutin"><span>Clear!</span></div>
</div>
<button id="btn_clean">GO</button>
JavaScriptが複雑な分、HTMLは超単純。canvasを3枚重ねしているだけ。
1枚目…立ち絵(鏡面含む)1024px×1500px
2枚目…黒ペンキぶちまけるエリア 1024px×500px
3枚目…ちびキャラ描画エリア 1024px×500px
立ち絵が1024px×1500pxなのは、StableDiffusionで出力する絵が1024x1024だから。ここに鏡面部分(パンチラ部分)を+476px分加えているから。
黒ペンキエリアとちびキャラエリアをわざわざ分けたのは、お掃除が完了した判定のため。
お掃除の完了判定はエリア内のすべてのドットが透過属性になったかどうかを判定している。そのため、同じエリアにちびキャラを残しておいてはいつまでたっても完了判定できないため、別エリアとした。
ランダムにペンキをぶちまける処理
初期にぶちまけられているペンキは実はランダムで描いている。
↓このペンキ。
以前、「AIイラストの衣装を脱がしたい。…脱がしたくない?」で使ったものと同じ。
それを下記のとおり、ある程度ランダムな大きさ(1.0~2.0倍の間)かつランダムな配置で50個置いている。
let canvas2 = document.getElementById('canvas2');
let ctx2 = canvas2.getContext('2d', { willReadFrequently: true });
img2.onload = function () {
ctx2.drawImage(img2, 400, 300);
//ランダムにペンキを配置
for (let i = 0; i < 50; i++) {
let x = parseInt(Math.random() * (1000) - 200);
let y = parseInt(Math.random() * (500) - 200);
let scale = Math.random()*2 +1;
ctx2.resetTransform();
ctx2.scale(scale, scale);
ctx2.drawImage(img2, x, y);
}
}
しかし、ランダムペンキとは別に1個だけ、X座標400、Y座標300には必ずペンキを出すようにしている。最初からパンツが見えていたらツマラナイからだ。
ちびキャラを動かす処理については、以前投稿した「AIイラストを雑にアニメーションしつつ動画にするツールを試作してみた」あたりでも解説しているのでここでは省略する。
画像が全て透過になったかどうかの判定
見た目は「白」だが、内部的にはペンキは「透明」になっているため、ペンキエリア(id=canvas2)のすべてのドットが透過になっているか確認する必要がある。
let flgClear = true;
let imageData = ctx2.getImageData(0, 0, canvas2.width, canvas2.height);
let data = imageData.data;
for(let i = 0, len = data.length; i < len; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
let a = data[i + 3];
if (a != 0) {
flgClear = false;
break;
}
}
getImageDataでピクセルデータが取得でき、そのピクセルデータにはR,G,B,A(赤、緑、青、透)の4データが並んで入っている。つまり、縦×横×4色分の配列ということである。
なので、上記のとおり、4つずつ配列にセットして、RGBAを取得しつつ、今回必要なのはA(透過情報)のみなので、そこが0か0ではないのかで黒ペンキがすべて取り除かれたかどうかを判定している。
まとめ
アクリルスタンドに感動したのは本当だけれど、ちびキャラが動くところ見たくてミニゲームにしたことは否定できない。














