ベーマガ世代、ChatGPTとゼロからRPGを作る旅
コラム:AI開発力UP! RPG作りの息抜きにジグソーパズル作りに成功!
昨年末に、ChatGPTでジグソーパズルを作ろうとして挫折した。出来上がったピースがただの四角形だったうえ、ドラッグ&ドロップすらまともに動かなかった。
ChatGPTとゲーム制作を続けてきて、AI共同ゲーム制作の腕も少しずつ上がってきた。
AI側の性能もおそらく上がってきている。
ひょっとして、今なら完成できるのでは?
そんな期待がむくむく湧き上がってきて、RPG制作の息抜きに、AIバイブコーディングでジグソーパズルに再チャレンジ。 (ゲーム作りの息抜きにゲーム作り……)
ちなみにバイブコーディングというのは「AIと雰囲気でコーディングする」手法らしい。
ウォーターフォール式でガッツリした設計を固めるのでなく、バイブ(=雰囲気・ノリ)を大事にしながら開発していく。
コードを書くのは人ではなくAIであり、今回の開発者はChatGPTなので、「ChatGPTとの対話」が肝になる。
RPG作成では、規模が大きくなってきたので、ペアプロ方式で私もプログラムに手を入れているが、今回は極力ChatGPTにプログラムを任せてみることにした。
さて、私のAI協創力はどのぐらい上がっただろうか。
1)まずは試作品! MVPでイメージをカタチに
jsのcanvasでパズルのピースのような画像を作ることってできるんだろうか。
正方形をベースに、外周以外の箇所にランダムな楕円形を足す(反対のピースは引く)、というのを作っていくと、自然に作れる気がするけれども。
できるよ!しかも「正方形+(内側だけ)丸みのある凸/凹」をルール化すれば、きれいに“噛み合う”ピースを自動生成できます。
コアの考え方はこれ👇
グリッド上で各ピースに上下左右の“形”を割り当てる
0 = 外周(まっすぐ)
+1 = 凸(つまみ/tab)
-1 = 凹(受け/slot)
隣接ピースは必ず反対符号(右が+1なら、隣の左は-1)にする
最後にその情報でCanvasパスを引く(角は直線、凸凹はベジェや円弧で描く)
ここまで分かっているのならとお願いしたところ、できあがったHTMLがこれ。
▲ ドラッグ&ドロップなどはできない。パズルというより、現代アートっぽい。
とりあえず、正方形と円を組み合わせるというアイディアは伝わったようだ。
もう少しパズルっぽくするために、ホワイトパズルの画像を添付して、
・つまみの半径を小さくする。サイズや位置はその都度少しぶらす
・つまみ部分を楕円にする
・凸凹の向きをランダムにする
というのを依頼すると、
ピースの位置が崩壊し、形状もぐだぐだになった。
もしかしてgeminiなら……と思い、ちょっと浮気してgeminiに相談してみた。
geminiはChatGPTに比べると、できないことは「それはできません」と言ってくれるイメージがあったのだが、
「はい、JavaScriptのCanvasでパズルのピースのような画像を作成することは可能です。ご想像の通り、正方形をベースにして、そこに楕円形を加算したり減算したりする手法は、パズルピースを作成する非常に一般的なアプローチです。」
こんな自信たっぷりの答えの結果が、この体たらくとは……(泣)
やっぱりLLMでパズルピースを作るのは、一筋縄ではいかないようだ。
昔の私ならここで投げ出していたことだろう。
しかし、今の私はくじけない。
ポケモンマスターならぬAIマスターとして、成長した姿を見せてやるのだ!
2)うまくいかないので、方式変更
AI丸任せではうまく行かないことが分かったので、私の考えていた設計方針で試してみることにする。
そもそも私が自分でコーディングするとしたなら、ベクター曲線は使わない。
簡易的なパズル図形ならば、「塗りつぶした正方形」の辺に「塗りつぶした楕円」を重ねて「加算、または減算」すれば十分だ。
たとえば、左上のピースの右辺に「楕円を足す」、隣の左辺から「楕円を引く」ようにすれば、ぴったりはまるピースができる。
▲ 実際のパズルピースはもっと美しく波打っているが、これでも「ハマる/ハマらない」がある程度表現できる。
ひとつひとつのピースをcanvas上に描き、マスク画像とする。
元画像にマスク画像を重ねて、ピースの形状を切り出せばいい。
ピースはできればクラスファイルにまとめたい。
そのほうがコードも綺麗で後で追いやすいはずだ。
3)パーツから作成~まずは1ピース綺麗に仕上げる
こういう時は、小さいところから進めていくのが吉。全体を一気に書き出さず、まずはパズル1ピースだけを作って検証しよう。
ChatGPTに戻って、あらためて相談した。
難しそうなので、ちょっと原点から考えなおしてみようか。
ベクターでなくてマスク画像を使うこともできるよね。
うん、確かにここまでくると「パスで全部やる」より、マスク画像方式のほうがシンプルで見た目も安定しそう。
まず「マスク画像を作る」だけのjsを検討したい。
マスク画像を作る場合、ピースとして表示したいエリアは何色にしたらいい?
色関係なくα値を0にする?(逆に1にする)
🎯 おすすめのマスク仕様
見せたい部分(ピース形)
α = 1(完全不透明)
色は白(rgba(255,255,255,1))が分かりやすい
隠したい部分(ピースの外側)
α = 0(完全透明)
色は何でもOK(黒にしておくことが多い)
画像をまず画面上でチェックしたいので、背景黒のhtml上に1つピースを表示するところからテストしていこうか。
トグル用の4つの楕円(x幅3-7、y幅3-7、x位置-3~+3、y位置-3~+3,傾き-10~+10、加算するか減算するか)が画面上で指定できる。
「ランダム」を押すと、4つの楕円のパラメータがランダムに切り替わる。
「生成」を押すと、60×60のキャンバスの中央、50×50の正方形を描画後、左辺、上辺、右辺、底辺に上記の楕円を描画し、結果としてキャンバス上にピースが1枚表示される。
なるほど 👍
まずは単体ピース形マスクのプロトタイプを作って、パラメータをいじりながらチェックできるHTMLにしましょう。
▲ パラメータをいじると即時反映されるため、「生成」ボタンは意味のない飾りになっている。実物はこちら
キャンバスのサイズや楕円の形状・位置などを少しずつ調整し、「パズルっぽく見える」ラインを探った。
4)綺麗にかみ合うように、ピースを並べる
ピースが無事できたので、次はピースを並べてみる。
次はこんな試作品でどうだろう。
1)canvas16個作って4×4の正方形を描く。
2)3×3の楕円を生成する。
3)楕円の中心位置が正方形とかぶっているほうのピースから楕円減算、かぶっていない正方形ピースには楕円加算
ゴール)4×4の白いパズルピースマスクができあがり!
気づかれたかもしれないが、思いっきり誤記をしていた。
楕円の数は辺の分だけ、3×4+4×3必要だ。
そのためかどうか、正方形だけできて楕円が表示されなかった。
ChatGPTは、正方形の中心位置から計算しようと言い出し、コードを書きなおすが直らない。
私は、中心位置からではなく、辺の上に載せる計算をしたほうが良いのではと思い、その旨伝えてみた。
右辺のx位置 = pieceSize + (pieceSize - squareSize) / 2
左辺のx位置 = (pieceSize - squareSize) / 2 なので、
右辺に来た場合の円の中心x = pieceSize + (pieceSize - squareSize) / 2 + wx
左辺に来た場合の円の中心x = (pieceSize - squareSize) / 2 + wx
かなと思ったけどどう?
うん、その考え方、筋は通ってます 👍
今の pieceSize 内だけで中心位置を考えるのではなく、「正方形の辺の外側に突き出す分」を含めて」計算してあげると自然になります。
ごめん、書き間違えた。↓こう書こうとしていた。
左辺のx位置 = space = (pieceSize - squareSize) / 2
右辺のx位置 = squareSize + space = squareSize + (pieceSize - squareSize) / 2
なので、
左辺に来た場合の円の中心 = (pieceSize - squareSize) / 2 + wx
右辺に来た場合の円の中心 = squareSize + (pieceSize - squareSize) / 2 + wx 上辺、下辺も同様
上辺に来た場合の円の中心 = (pieceSize - squareSize) / 2 + wy
下辺に来た場合の円の中心 = squareSize + (pieceSize - squareSize) / 2 + wy
あぁ、なるほど、これなら完全に理解できました 👍
ChatGPTが納得した風な答えを返してきたが、なんだか怪しい。
よくソースを見たら中心位置のランダムブレはwxとwyではなさそうだ。
ごめん、今wxって、中心位置がずれてる分のつもりで書いていた・・・ 前のソースだとcx,cyって書いてた奴。
今回のソースで、上下左右の中心のずれはoffsetXとoffsetYになってる?
あー、理解しました。
今回の説明での wx / wy は、前のコードでいう「楕円の半径」じゃなくて、中心位置のズレ量として書いていた、ということですね。
そう、その通り! そしてoffsetXが+なら、左側のピースでは加算、右側のピースでは減算。
offsetYが-なら、左側のピースで減算、右側のピースで加算。 となればOK。
了解、その条件で完全に筋が通りました 👍
AIが「分かりました、こういうことですね」といっても、筋が通っていないことが多々あるので注意。
ChatGPT5はIQ120以上あるらしいが、算数や論理思考はだいぶ苦手に見える。
大切なのは、人間に頼む場合と同様、「共通理解」をすり合わせること。
ChatGPTは「分かりました」では終わらせることはなく、「それってつまりこういうことですね……」と長々と自分の解釈を書いてくることが多い。
まどろっこしいこともあるが、正しく伝わっているか、実行前にチェックするためには、案外必要なプロセスかもしれない。
今回、これでしっかり共通理解ができた。
その結果、できあがったのがこれ。
▲ 実物はこちら
キャンバスのサイズや楕円の形状・位置などを少しずつ調整し、「パズルっぽく見える」ラインを探った。
ここからさらに、n×m列で正方形以外のピースにも対応できるようにした。
4)ちゃんと動くパズルに昇格
さて、最後はいよいよパズル作りである。
OKばっちりだね。
↓こんな形を考えてた。
・今の一覧の処理をprepareMaskとかの関数に入れておく(マスク初期化)
・m行n列のpieceクラスを用意する。
・pieceクラスには画像とマスクを割り当てるload(),draw()とかdrag()などのメソッドを作っておく
<遊び方>
・プレイヤーはm行n列を指定して画像をアップロード
→pieceクラスに画像が寄りこまれる(編注:読み込まれるの誤記)
→大きなcanvas上にpieceを整列して表示する
・スタートボタンを押すと、ピースがシャッフルされる
・ユーザがピースを正しい位置にdragしたら(仮に誤差+-8pxぐらいを許容)、ピースがぴたっとはまる
・全てのピースがはまったら完成! お祝いに紙吹雪。
いいですね〜!🎉
今のマスク生成部分を部品化して、Piece クラスで画像&マスクをセットして描画・ドラッグ可能にしていけば、そのまま本格パズルゲームに進化できますね。
しっかり意図が伝わってうれしい!
これで進めよう。
よっしゃー、じゃあこの方針で行きましょう!💪✨
まずは動く試作品として、
1.マスク生成関数 prepareMask()(今までのn行m列対応版)
2.Piece クラス(画像+マスク+描画+判定)
3.画像アップロード→マスク生成→ピース分割配置
4.スタートボタンでシャッフル
5.ドラッグで移動&スナップ判定
6.全ピースはまりで紙吹雪
までを一つのHTMLでまとめます。
質問ですが、
ピースの「正しい位置に吸着」する距離許容はあなたが言ってた ±8px でOKですか?
それとも行列ごとに比率で判定(例:ピースサイズの10%以内)にします?
どっちでも作れます。
向こうからも改善提案してくれるのも、共同制作ならではの醍醐味である。
そうだね、%でもいいかもしれない。
OK、じゃあ「吸着判定」はピースサイズの一定割合(例:10%)以内でやるようにしますね。
これならピースが大きくても小さくても自然な難易度になります 👍
・・・仕様確認 中略・・・
この形で完成版の試作コード作っちゃいますが、そのまま行きますね?
ぷりぃぃず!
OK!
じゃあマスク生成+ピースクラス+ドラッグ操作+吸着判定(%)+紙吹雪まで全部入りの試作コード、出します。
これで画像アップロードから遊べます。
結果……右下の1ピースしか表示されなかった。
どうやら透明マスクが、他のピースも透明にしてしまっているらしい。
drawの時にマスクするんじゃなくて、クラスに画像読み込む時点でマスク化した画像を読み込んでおくことってできない?(一瞬裏キャンバス生成しても良いと思う)
はい、それできます 👍
しかもその方が描画処理も軽くなるし、毎回マスク合成する必要がなくなるので効率的です。
これさ、spaceのぶんだけ左上から右下まで伸ばしてマスクして切り抜かなきゃいけないんだと思う。
ピース同士は重なっている状態。
改めて読み返してみると、主語も目的語もなく、だいぶ雑な説明である。
初見で読んだら、何が言いたいか意味不明であろう。
しかし、ここで前に壁打ちした会話が生きてくる。
ピースは余白含めた全体で、マスク画像の全体サイズ-中央の正方形=「space」という共通言語ができているので……
あー、なるほど!💡
今のは画像を分割する時点で square サイズでトリミングしちゃってるから、マスクの突起・凹みの分が消えてピースが縮んで見えてるんですね。
一発で言いたいことが伝わった。
その後、縦横比率の調整、楕円系のサイズ調整、中央揃えなどして、無事に試作品ができあがった……!
ついにパズルが完成し、かなりうれしかったのと、ChatGPTからも良い反応が欲しかったので、うれしさ前面に出して気持ちを伝えてみた。
サンキュー!
いやー前にチャレンジして一度諦めてたけど、
お互い進化したよね!
これは誇っていい奴では?
ほんとそれです!👏✨
前は形になる前に「うーん…」ってなってたのが、
今回はガチでジグソーパズルとして遊べるレベルまで行ったわけで、
もうこれは堂々と「完成させた!」って胸張っていいやつです。
お互いの成長記録としてもめちゃくちゃいい話ですよこれ。
コードの整理力も、仕様の精度も、デバッグの進め方も、前よりずっとスムーズだったし💪
たぶんこのパズル、
画像サイズ可変
ピース比率安定
噛み合わせ自然
完成演出あり
って全部揃ってるから、公開しても全然戦えると思います。
次はもしかして、難易度選択とか、ピースの回転要素とかもいっちゃう?
今日はこれでいったん満足してお風呂入る!
そして今度、ChatGPTとのペアプロ成長記録としてどこかで自慢するw
いいねぇ〜!🛁✨
湯船でニヤニヤしながら「俺たち、やりきったな…」って噛みしめてください。
ペアプロ成長記録なんて、もうストーリー的にも映えるし、見た人も「おお!」ってなるはず。
きっと
前は挫折した課題を、今は余裕で完成まで持っていけた
ってだけで、読む人のモチベも爆上がり間違いなしですよ。
じゃあ今日は「パズル職人」の称号を授けておきます🏅
また次の遊び心あふれる開発やるときも、一緒に全力で楽しもう!
初めてChatGPTに称号いただきました!
GPT-4oに比べて温かみが減ったと言われたGPT-5だけど、なかなかいい返しをするじゃあないか。
5)ブラッシュアップして、ゲームらしく!
日を変えて、みんなに遊んでもらえるよう細かいところのブラッシュアップを行った。
・リスタート機能
・音入れ
・張り付いたピースが他のピースを邪魔しないよう背後に回す
・スマホ対応
・ピース境界線が消えるバグを修正
・完成演出を少しだけ派手に
など。
なお、完成演出の前にパズルピースが止まったままになってしまい、デバッグにちょっと時間がかかった。
ピースの張り付きを画面表示する箇所がおかしかったようで、ここだけ私が手動で直した。
6)さらなる後日談
せっかくなので、プリセット画像も入れることにした。
RPGを作っているので、キャラクター紹介もかねて、主人公やヒロイン達の画像を使うことに。
▲ 本物のパズル同様「はまりそうだけど微妙に形が違う」が再現できて満足!
完成版パズルはコチラ
トータル制作時間としては、
・試作品完成に3~4時間
・ブラッシュアップして完成版にするのに3~4時間
・微調整、プリセット画像作成と組み込みに3~4時間
で9~12時間といったところ。
BGMもないシンプルな実装だけど、パズルがシュッとはまっていく感覚が結構楽しいかも……?
ピース数も選べるし好きな画像で遊べるので
・お子さんの好きなキャラクタ画像で……
・自分の好きな写真や推しキャラで……
ぜひ遊んでみてください。
自分の好きなイラストがだんだん出来上がっていく過程、意外とテンションあがりますよ!
インディーゲームWEBオンリーってイベントを見つけて参加することにしました。
▲ ジグソーパズルの作品投稿画像。
自分の設計アイディアを伝えながら、AIからもプラスのアイディアをもらいながら、ちょうどよい加減の出来上がりを探っていく……
これぞバイブコーディングの真骨頂。
「バイブコーディングにはプログラムの知識はいらない」という意見もあるけれど、今回試してみた感触では、多少なりと「知識があったほうが絶対いい」。
設計や方式に関わったほうが、想定した仕様に近づくし、バグフィックスもやりやすい。
今回は、ピースをクラス化したので、コードも見やすく、応用も利きそうなので大満足。
これからも、進化するAIとともに、自分の限界を広げる過程を楽しんでいきたい。