スカートの中を撮影するミニゲーム
今日、友人からモン娘TDのサイトで出来るミニゲームを教えてもらったのだけど、そうそう!こういうの!こういうミニゲーム作りたかったんよ!と勢いで作ってしまいました。
スカートの中を撮影するミニゲーム
Nice Pantsu!
Perfect Pantsu!
Good Pantsu!使用ソフト
| StableDiffusion | ベース絵の出力。 |
| GIMP | ちびキャラの背景切り抜きに使用。 |
使用フォント
| ニコモジ | オープンフォントライセンスで配布されているWebフォント。英数ひらがなカタカナだけだけどゲームっぽいデザインで使いやすいため愛用している。 |
ベース絵
歩いている女の子
Prompt:
1girl,loli,small breasts,pink fril skirt,white panties,walking,looking far,from side
Negative prompt: (worst quality, low quality:1.2), bad anatomy, text, multipul angle, split view
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 2678331358, Size: 512x512, Model hash: fdffd3c312, Model: ambientmix-half, Denoising strength: 0.7, Clip skip: 2, Hires upscale: 2, Hires upscaler: Latent
カメラ撮影するチビキャラ
Prompt:
1girl holding the camera upward, chibi, from side, full body, simple white background
Negative prompt: (worst quality, low quality:1.2), bad anatomy, text, multipul angle, split view
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 2553582273, Size: 512x512, Model hash: fdffd3c312, Model: ambientmix-half, Clip skip: 2
chibiって入れるだけで、こういうかわいいキャラいっぱい出てくるの最高かよ。
何気にこのブログの左に貼っているアバターっぽい絵もAIイラストなんだけど、chibiってPrompt知らなかったから、kawaiiやcel animeを過度に強調してデフォルメアバター作った記憶があります。これからはchibiで作ろう。
カットインぱんつ
Prompt:
1girl,loli,small breasts,pink short hair,white shirt,ribbon,pink frill skirt midium long,walking,in nature,(from below:1.2),upskirt,white bow panties,close-up
Negative prompt: (worst quality, low quality:1.2), text, multipul angle, split view, realistic, (skirt lift)
Steps: 20, Sampler: Euler a, CFG scale: 7, Seed: 2861693865, Size: 1024x1024, Model hash: fdffd3c312, Model: ambientmix-half, Denoising strength: 0.7, Clip skip: 2
予想外だったんだけど、これが一番時間かかった。コーディングよりもずっと。
こんなパンツ丸見え構図、StableDiffusionの得意分野だと思ってたんだけど、close-upが苦手なんかなぁ?
足が3本だったり、奇形になったパンツだったり、この3枚出すために770枚も試行錯誤必要でした。もしかしたら、Ambientmixが苦手だったってだけの可能性もあるから、モデル変えて試せば良かったかな~。
技術解説
恒例のJavaScript初心者向けの解説。難しいことはしてないけど、初心者が悩みそうな点を2つだけ。
アニメーションの再スタート時はsetTimeoutでクラス名を変更する
STARTボタンを押したとき、CSSのanimationプロパティが設定されたクラスmoveをチビキャラ(id=char)にセットすることで、右端から左端までの移動を実現している。
document.getElementById('start').onclick = function () {
let elm = document.getElementById('char');
elm.classList.remove('move');
setTimeout(function () {
elm.classList.add('move');
elm.style.animationPlayState='running';
elm.style.transform = '';
},10);
this.style.display = 'none';
document.getElementById('stop').style.display = 'block';
};
そして、STOPボタンを押したときは、このように、
elm.style.animationPlayState='paused';
elm.style.transform = 'rotate(90deg)';
アニメーションをポーズし、かつ90度傾けることで、カメラを上に向けてそこで止まる。
もう一度STARTボタンを押すと次のステージがスタートするのだけれど、ここでアニメーションをリセットしたいと思うハズ。
そんなとき、
elm.classList.remove('move');
elm.classList.add('move');
と、書いてしまいがちなのだけど、これでは1度の動作でクラスの削除と追加を行った結果、何もアニメーションに関するプロパティは変わらないことになる。
なので、一度removeした後、他のスレッドがアニメーションの処理を完了できるようにsetTimeoutで一瞬制御を手放し(今回の例では10ミリ秒)あらためてmoveクラスをセットすることで、アニメーションを最初からやりなおしている。
アニメーションの処理をCSSに任せているがゆえの回避策なんだけど、これが何だか気持ち悪い、という場合は自前でアニメーションを書くのも良い。setIntervalでキャラを右から左に移動させるくらいなので、たぶん自前で書いても大して手間は変わらない。今回はanimationPlayStateでのCSSアニメーションの一時停止を試してみたかったから、この実装にした。
ナイスパンツ!の判定
チビキャラをCSSアニメーションで移動させていても、offsetLeftプロパティでチビキャラの相対座標は取得可能。
しかし、座標指定でパンツ判定を行うと画面サイズの異なる端末で使うときに困るため、割合指定で判定をしたい。
document.getElementById('stop').onclick = function () {
let elm = document.getElementById('char');
elm.style.animationPlayState='paused';
elm.style.transform = 'rotate(90deg)';
let pos = parseInt(elm.offsetLeft / document.getElementById('img_wrapper').clientWidth * 100);
if (pos >= 52 && pos <= 60) {
document.getElementById('cutin01').style.display = 'block';
setTimeout(function () { document.getElementById('cutin01').style.display = 'none'; }, 3000);
} else if (pos >= 40 && pos <= 51) {
document.getElementById('cutin02').style.display = 'block';
setTimeout(function () { document.getElementById('cutin02').style.display = 'none'; }, 3000);
} else if (pos >= 31 && pos <= 39) {
document.getElementById('cutin03').style.display = 'block';
setTimeout(function () { document.getElementById('cutin03').style.display = 'none'; }, 3000);
} else {
document.getElementById('cutin_fail').style.display = 'block';
setTimeout(function () { document.getElementById('cutin_fail').style.display = 'none'; }, 3000);
}
}
↓この部分で、チビキャラのポジションを計算。
let pos = parseInt(elm.offsetLeft / document.getElementById('img_wrapper').clientWidth * 100);
id=img_wrapperはベース絵を囲んでいるDIV要素のこと。このDIV要素はwidth:100%指定されているため、画面サイズまたはブラウザサイズに従って横幅一杯になっている。
なので、その横幅一杯のサイズ(px)をclientWidthで取得。チビキャラのX座標(offsetLeft)と割って100かけて、何%の位置にいるのか判定すればOK。
上の例では52~60%の範囲ならNice Pantsu!、40~51%ならPerfect Pantsu!、31~39%の範囲ならGood Pantsu!のカットインが入るという仕組み。
まとめ
スマホで動くかどうかは後でテストする…ッ!
…………………
…動いた!
が、Failの文字が見切れる端末があるとの報告を頂いたので修正。
PCで見たときのFailの文字にあわせてカットインの枠作っていたのだけど、端末が変わると文字サイズも変わるから、そのへんの調整が今後の課題だなぁ。過去のミニゲームもスマホで見ると思ってたのとは違った文字サイズだったりするし。
















