見出し画像

【Roblox/ロブロックス制作】時間がない!でも推しゲーは作りたい!nano-bananaとGeminiで「チルノのクイズゲーム」を爆速リメイク✨


はじめに


推しへの愛は無限大、でも時間は有限…そんな悩みを抱えていませんか?
私も大好きな東方Projectの推しキャラ、チルノの日(9月9日)に、どうしてもオリジナルの推しゲーを作りたい!でも、今年は時間がない…と頭を抱えていました。

そんな時、ふと頭をよぎったのが、Google Geminiと画像生成AI「nano-banana」の存在でした。「これを使えば、もしかしたら短時間で間に合うかも!?」そう思い立ち、スコアごとに背景が切り替わるクイズゲームのリメイク制作に挑戦してみたので、その開発日誌をお届けします!

1)スコアごとに切り替える背景を作ろう


✅AIとの共同作業で、ハイクオリティな画像生成を短時間で実現しました。

1️⃣ まずは、Geminiに「9点ごと全22画像でどのような絵を作成すべきか」を提案してもらいました 。ゲームのコンセプトに合わせた多様なアイデアがすぐに手に入ります。

99問クイズを作り、正解・不正解数によりキャラの表情を変えたいです。

①女の子の表情・ポーズと、

②それにふさわしい背景イメージのパターンを23パターン考えてほしいです

点数が低ければ低いほど喜んで、点数が高くなれば悔しがるようにしたいです。

女の子の設定は子供っぽくていたずら好きでお調子者、氷の精霊なので暑くなると溶けます。
【0点:無表情】

表情・ポーズ: 何も感情を表さない、すまし顔で棒立ち。

背景イメージ: 澄み切った氷の結晶が舞う、静かで神秘的な空間。

【マイナス点数(元気がない・溶けかけ)】

-9点: むすっと膨れっ面

表情・ポーズ: 口を尖らせて、頬を膨らませた不満そうな顔。腕を組んでいる。

背景イメージ: ぬるい風が吹く冬の森。雪が少し溶け始めている。

-18点: 唇を噛んでしょんぼり

表情・ポーズ: 唇をぎゅっと噛みしめ、しょんぼりと眉をひそめている。

背景イメージ: 湿った雨が降る夜の街。ネオンがぼやけている。

-27点: 体育座りで俯く

表情・ポーズ: 体育座りをして、顔を膝にうずめている。肩が少し震えている。

背景イメージ: 誰もいない熱帯の図書館。窓の外は夕立。

-36点: 涙目でウルウル

表情・ポーズ: 瞳に涙をいっぱいためて、今にも泣き出しそうな表情。

背景イメージ: 靄が立ち込めた朝の庭。蒸気が漂っている。

-45点: 怒って地団駄

表情・ポーズ: 顔を赤くして、片足を上げて地団駄を踏むように怒っている。体が少し溶けて、足元に水が溜まっている。

背景イメージ: サウナ室のような暑い氷の宮殿の廊下。冷気が消えかかっている。

-54点: プイッとそっぽを向く

表情・ポーズ: 顔を横に向けて、拗ねたようにそっぽを向いている。少し汗ばんでいる。

背景イメージ: じめじめした小屋の中。少しひんやりとした空気が消えかかっている。

-63点: 両手で顔を覆って俯く

表情・ポーズ: 両手で顔を覆い、肩を落として深く俯いている。

背景イメージ: 曇り空の下のスケートリンク。氷が少し溶け始めている。

-72点: 溶けかかってぐったり

表情・ポーズ: ショックでぐったりとして、体が少し透けている。表情も力なく強張っている。

背景イメージ: 氷河の洞窟。奥から熱い蒸気が立ち上っている。

-81点: 熱い息を吐いて苦しむ

表情・ポーズ: 苦しさで震える手で、熱い息を吐いている。表情は苦しそう。

背景イメージ: 凍てつく湖の上。熱波が吹いている。

-90点: 溶けていくのを絶望的に見つめる

表情・ポーズ: 天を仰ぎ、絶望したように目を閉じている。体から湯気が発せられている。

背景イメージ: 猛暑の山頂。視界は暑さでぼやけている。

-99点: 完全に溶けかけて水たまりに

表情・ポーズ: 全身が水たまりと化し、その表情は悔しさと悲しみで固まっている。

背景イメージ: 灼熱の砂漠。蜃気楼がゆらゆらと揺れている。

【プラス点数(元気いっぱい・氷結)】

9点: 腕を組んで自信満々

表情・ポーズ: 腕を組み、口元に手を当てていたずらっぽく笑っている。

背景イメージ: 寒々しい冬の森。雪が降り積もっている。

18点: 勝利のガッツポーズ

表情・ポーズ: 片方の拳を高く掲げ、自信満々の表情。

背景イメージ: 凍った噴水のある公園。空気は澄み切っている。

27点: 氷の精霊らしく片手をひらひら

表情・ポーズ: 片手をひらひらと振りながら、楽しそうな笑顔。

背景イメージ: 誰もいない静かな図書館。窓の外は雪景色。

36点: 目をキラキラさせて見上げる

表情・ポーズ: 目を輝かせ、何か面白いものを見つけたように見上げている。

背景イメージ: 霜が降りた朝の庭。吐く息が白い。

45点: 両手を頬に当ててニッコリ

表情・ポーズ: 両手を頬に当てて、にこやかに微笑んでいる。少し上目遣い。

背景イメージ: 氷でできたお菓子屋さん。カラフルなジェラートが並ぶショーケース。

54点: 小首をかしげてにやり

表情・ポーズ: 小首をかしげ、いたずらっぽく笑っている。

背景イメージ: 雪が降り積もるかまくらの中。少しひんやりとした空気。

63点: 両手を広げて万歳!

表情・ポーズ: 空に向かって両手を大きく広げ、達成感に満ちた笑顔。体から冷気が発せられている。

背景イメージ: 氷でできた宮殿の廊下。冷気が漂う。

72点: 天真爛漫にスキップ

表情・ポーズ: 満面の笑みで、片足を上げて軽やかにスキップしている。

背景イメージ: 氷の世界の遊園地。氷のメリーゴーランドが光っている。

81点: 満面の笑みで大喜び

表情・ポーズ: 目を閉じて大きく口を開けて笑い、体はキラキラと輝いている。

背景イメージ: 氷河の洞窟。神秘的で冷たい青い光が満ちている。

90点: 勝利のポーズでドヤ顔

表情・ポーズ: 胸を張り、両手を腰に当てて自信満々のドヤ顔。

背景イメージ: 猛吹雪の山頂。氷の結晶が舞う。

99点: 完全に氷の女王化

表情・ポーズ: 氷の王冠をかぶり、威風堂々としたポーズ。顔は達成感に満ちた、でも子供っぽい満面の笑み。

背景イメージ: 永久凍土の地。氷の世界が広がり、オーロラが揺れている。

2️⃣ 1️⃣をもとに、nano-bananaに画像を生成してもらいます。元の絵を取り込んで指定のポーズを取らせるだけで、理想のキャラ絵が完成しました 。nano-bananaは元の顔をキープしつつ、髪型や服、背景を変更できるのが特徴です。

オリジナル

3️⃣ 次にGeminiで背景を作成。コンセプトに合わせた背景を効率的に生成します。

澄み切った氷の結晶が舞う、静かで神秘的な空間を描いて
永久凍土の地。氷の世界が広がり、オーロラが揺れている。を描いて

4️⃣ 2️⃣と3️⃣を合わせる作業では、「氷の妖精にしてはおかしい?」と感じる場面では、キャラクターに合わせて背景を修正してもらいました

これでメインの画像は完成です!ここまでの作業は、1日30分程度の隙間時間を使って約1週間で終えることができました。しかし、この時点で9月7日の夜9月9日の昼に東京に出かけるため、あと1日半しかありませんでした。

2)メインスクリプトを作る


✅AIの提案力は素晴らしいけれど、バグ修正には時間がかかることも。

1️⃣メインスクリプトは、1時間でざっくりと指示を作成
2️⃣Geminiに流れを整理させ、改善点があれば提案してもらう
3️⃣再度指示をし、提案してもらい。。。を繰り返し、これ以上改善点がなくなったところでスクリプト作成
4️⃣作成したスクリプトを実行し、動かなければその都度修正する
というアプローチを取りました。

  • 私の最初の指示

Robloxで以下の流れでクイズゲームを作ろうと考えています。流れを見やすい形で整理し、
矛盾点があれば直して


【全体の流れ】

①サーバー起動時メモリーストアから今週のランキングトップ30取得

①プレイヤーID、クリア時間取得し、

①プレイヤー表示名、サムネイル、クリア時間をランキングテーブルに保存

①表示名、サムネイルはプレイヤーIDを元に取得する

①ランキングテーブルの他に、今週の最速記録を変数で保存

①60秒に一回取得し直し。ランキングテーブルに保存し直し、今週の最速記録も保存し直し
それを各プレイヤーのplayerGUIに書き込む



【プレイヤー参加、プレイヤー離脱時の処理】

プレイヤー参加

①サーバースクリプトでプレイヤーごとに
 主キーplayerオブジェクト
 valueに「今何問目?とその答え、現在の点数」を表す連想配列作成

②サーバースクリプトでプレイヤーごとに以下のattribute作成
 クイズ問題(文字列型)
 クイズ選択肢 A(文字列型)
 クイズ選択肢 B(文字列型)
 クイズ選択肢 C(文字列型)
 クイズ選択肢 D(文字列型)
 クイズ点数合計(文字列型)
 プレイタイム

③サーバースクリプトでプレイヤーごとにplayerGUI取得
 ランキングテーブルに保存されたプレイヤー表示名、サムネイル、クリア時間を参加したプレイヤーのguiに反映する
 今週の最速記録をplayerguiに反映する

④サーバースクリプトでプロフィールストアに保存した開放済み点数テーブル取得
その開放済み点数に対応するplayerguiのcollectionguiのimageを表示させる



プレイヤー離脱
サーバースクリプトでプレイヤーごとに「今何問目?とその答え、現在の点数」を表す連想配列破棄



【ゲームスタートから終了まで】

①guiでゲームスタート押下後、クライアントからサーバーにスタート送る

②サーバースクリプトがattributeのプレイタイムの計測スタートし、ミリ秒ごとに更新。
attributechangedでguiに書き込む(mm:ss:ms形式)

③サーバースクリプトが一問目の問題、選択肢A、選択肢Bをbモジュールから取得し、attributeに記入

③attributechangedでguiに反映

④サーバーの「今何問目?とその答え、現在の点数」のテーブル更新

⑤プレイヤーがguiでクイズの選択肢を選び、クライアントスクリプトが選択肢をサーバーに送る

⑥サーバーの「その答え」で記憶したものと一致しているか確認

 不一致だったらしていたら減点してクイズ点数合計のみattributeに記入、
サーバーの「今何問目?とその答え、現在の点数」の現在の点数のみ更新

 一致していたら、加点したクイズ点数合計をattributeに記入し、

  1)「今何問目?」が99の場合、クイズ終了
  2)「今何問目?」が99の場合、次の問題をランダムに取得し、次の問題の問題、
  選択肢をattributeに記入

    問題が下一桁9の場合はaモジュールからのクイズをランダムに取得
    それ以外の場合はbモジュールからのクイズをランダムに取得
    サーバーの「今何問目?とその答え、現在の点数」のテーブル更新

⑦attributechangedでクイズ点数合計の変更検出し、一定点数に変わったらイラスト変更



【クイズ終了処理】

①プレイタイムをストップする

②プレイタイムをクリア時間のメモリーストアに書き込む

③現在の点数をプロフィールストアの開放済み点数テーブルに書き込む
 更新されれば、新しく追加された開放済み点数に対応するplayerguiの
 collectionguiのimageを表示させる

④今回のクリアタイムがプロフィールストアの最速クリアより早ければ更新
 playerguiのTopguiのbestTimeの表示を変更する

⑤playergui、リザルトguiに以下を表示する

あなたの点数:attributeのクイズ点数合計
あなたの記録:attributeのプレイタイム

⑥リスタートボタン押下で【ゲームスタートから終了まで】に戻る

Geminiの最初の応答

整理されたゲームフロー 🎮
1. サーバー起動時の処理
MemoryStoreから「今週のランキングトップ30」と「今週の最速記録」を読み込み、
サーバー内の変数に保存します。

2. プレイヤー参加時の処理
プレイヤ参加時 (Players.PlayerAdded):

サーバー側で、プレイヤーごとのゲーム情報を管理するテーブル(連想配列)を作成します。

例: playerData[player] = { currentQuestion = 0, currentScore = 0, correctAnswer = "" }

プレイヤーオブジェクトに以下のAttributeを作成(初期化)します。

QuizQuestion (文字列型): 問題文

QuizChoiceA, B, C, D (文字列型): 選択肢

TotalScore (数値型): クイズの合計点数(初期値: 0)

PlayTime (文字列型): プレイタイム(初期値: "00:00:000")

サーバーに保存されているランキング情報と最速記録を、
参加したプレイヤーのPlayerGUIに送信・表示させます。

DataStoreから、そのプレイヤーが過去に獲得した点数(コレクション解放状況)と、
自己ベストタイムを読み込みます。

読み込んだデータに基づき、PlayerGUIのコレクションUIや自己ベストタイム表示を更新します。

3. ゲームプレイ中の処理
スタート:

プレイヤーがGUIのスタートボタンを押すと、クライアントからサーバーへRemoteEventで通知します。

プレイタイム計測:

サーバー側でタイマーを開始し、一定間隔(例: 0.1秒ごと)で
プレイヤーのPlayTime Attributeを更新します。

クライアント側はAttributeChangedイベントを検知し、GUIのタイマー表示を更新します。

問題出題:

サーバー側で最初の問題(または次の問題)をモジュールスクリプトから取得します。

取得した問題文、選択肢をプレイヤーの各Attributeに設定します。

サーバー内の管理テーブル(playerData)に現在の問題番号と正解を保存します。

クライアント側はAttributeChangedを検知し、GUIの問題文と選択肢を更新します。

回答と正誤判定:

プレイヤーがGUIで選択肢を選ぶと、クライアントからサーバーへRemoteEventで回答を送信します。

サーバー側で、管理テーブルに保存した正解とプレイヤーの回答を比較します。

正解の場合:

スコアを加算し、TotalScore Attributeを更新します。

現在の問題番号が99問目未満であれば、次の問題を取得して出題します(上記 3. の処理に戻る)。

現在の問題番号が99問目であれば、【クイズ終了処理】へ進みます。

不正解の場合:

スコアを減点し、TotalScore Attributeのみを更新します。

コレクションの更新:

クライアント側でTotalScore Attributeの変更を検知し、
特定の点数に達していたら、コレクションのイラストUIを更新します。

4. クイズ終了時の処理
サーバー側でプレイタイムの計測を停止します。

今回のプレイタイム(記録)と合計点数を変数に保存します。

ランキング更新:

MemoryStoreに今回のクリアタイムを書き込みます。
この時、ソート済みマップ(SortedMap)を使うとランキング管理が容易です。

個人データ更新:

DataStoreに保存されている自己ベストタイムを比較し、今回の記録が早ければ更新します。

DataStoreに今回の合計点数を書き込み、コレクションの解放状況を更新します。

リザルト表示:

サーバーからクライアントへRemoteEventで終了を通知し、リザルトGUIを表示させます。

リザルトGUIには、TotalScoreとPlayTime Attributeの最終的な値を表示します。

自己ベストが更新された場合は、その旨も表示します。

リスタート:

プレイヤーがリスタートボタンを押すと、【ゲームプレイ中の処理】の 1. に戻ります。

5. プレイヤー離脱時の処理
プレイヤー離脱時 (Players.PlayerRemoving):

サーバー側で作成した、そのプレイヤー用の管理テーブル(playerData[player])を
破棄(nilを代入)してメモリを解放します。

6. ランキングの同期処理 (バックグラウンド)
サーバーは60秒に一度、MemoryStoreから最新のランキング情報と最速記録を取得します。

取得した最新情報を、サーバー内にいる全プレイヤーのGUIに反映させます。

矛盾点と改善案 💡
元のフローにはいくつか改善できる点や、矛盾の可能性がある箇所がありました。

【改善案】ランキングの更新方法

元のフロー: 60秒に一回、ランキングテーブルと最速記録を「保存し直し」ていました。

問題点: 全サーバーが60秒ごとに書き込みを行うと、データが上書きし合ったり、
MemoryStoreへのリクエストが多くなりすぎたりする可能性があります。

提案: ランキングの書き込み(保存)はクイズ終了時に都度行い、
各サーバーは**定期的に読み込み(取得)**して同期する形に分離する方が、安全で効率的です。

【矛盾点】データ型の指定

元のフロー: クイズ点数合計が文字列型になっていました。

問題点: 点数を文字列で保存すると、加算・減算のたびに数値型への変換が必要になり、
コードが複雑になったり、予期せぬバグの原因になったりします。

修正: TotalScore Attributeは**数値型(number)**で管理しましょう。

【矛盾点】問題進行の条件分岐

元のフロー: 正解時、「今何問目?」が99の場合、次の問題をランダムに取得 となっていました。

問題点: 99問目は最後の問題のはずなので、ここで次の問題を取得すると100問目に進んでしまいます。

修正: 「今何問目?」が99未満の場合に次の問題を取得し、
99の場合はクイズ終了処理に進むように修正しました。

【改善案】プレイタイムの更新頻度

元のフロー: ミリ秒ごとにAttributeを更新していました。

問題点: ミリ秒単位での更新は、ネットワークへの負荷が非常に高くなる可能性があります。

提案: 0.1秒(100ミリ秒)ごとなど、少し間隔を空けて更新するのがおすすめです。
これでもプレイヤーにとっては十分に滑らかな表示に見えます。

AIは、ネットワークの負荷、データ型指定など自分が配慮を忘れた提案をしてくれるなど、大変気が利きます。しかし、自分が一から作成した時には起こったことのないバグの修正に時間がかかったりもしたので、最初から自分で書くか、書いてもらうかは本当に迷うところです。

3)GUIを作る

✅「無茶ぶり」でGUIを生成、最終調整は人間が。

「現在のスクリプトの内容に合うGUIを作成するスクリプトを考えて」という「無茶ぶり」でスクリプトを作成してもらい、それを実行してできたGUIを手直しして作成しました 。


必要条件は満たしてくれるものの、人間の目から見るとレイアウトがぐちゃぐちゃになることもあります。これもメインスクリプトと同様に、最初から自分で書くか、書いてもらうか、本当に判断が難しい点だと感じました。

4)タイトル画像を作ろう

✅わずかな指示で、画像が背景に自然と馴染む驚き

タイトル画像は、黒板の前に立つチルノを、オリジナルを元に作成しました 。最初、黒板だけがリアルでチルノが浮いて見えたのですが、指示一つで黒板になじませられるのはすごい技術だと感じました 。

オリジナル
馴染ませ前
馴染ませ後

5)アイコンを作ろう


✅「豪華にして!」の一言で、魅力的なアイコンが完成。

ゲームのアイコンは、オリジナルとリメイク後のスコア最低得点画像を元に「豪華にして!」「顔は意地悪そうに!」という「無茶ぶり」で作ってもらいました。これ一つで、ゲームの第一印象がぐっと引き締まります。

オリジナルゲームのアイコン
今回のリメイクゲームのアイコン

🎮完成!そして公開へ🎮


こうして、翻訳がやや英語で残ってしまった部分もありましたが、9月9日の午後には無事にゲームを公開することができました !

AIをクリエイティブな相棒とすることで、時間がない中でも推しへの愛を形にできるという、新たな可能性を見出すことができました。

おわりに


推しへの愛とAIの力を組み合わせることで、時間がない中でもこんなに素敵なゲームが完成しました!AIは私たちの創造性を制限するものではなく、むしろ新たな可能性を広げてくれる最高のパートナーだと実感しています。

今回の私の経験が、「時間がないけど、何か作りたい!」というクリエイティブなあなたの背中を押すきっかけになれば嬉しいです。ぜひ皆さんも、AIを活用して推しゲー作りやクリエイティブな挑戦を楽しんでみてくださいね!


こんな記事も書いてます。見てね!!

自己紹介

Roblox開発記事まとめ

私の制作したRoblox作品まとめ

万博旅行記

仕事の悩み・自分語り

イベント参加報告


#AI活用 #生成AI #nano_banana #Gemini #ゲーム開発 #ゲーム制作 #推しゲー #東方Project #チルノ #クイズゲーム #短時間制作 #クリエイター #開発日誌 #AIとクリエイティブ #Roblox #ロブロックス #RobloxStudio

いいなと思ったら応援しよう!

くろにゃんこ@Robloxクリエイター 本当に私なんかがもらっていいんですか?いただいたチップはクリエイターとしての活動費に使わせていただきますが、よく考えたほうが(((( ;゚д゚)))アワワワワ

この記事が参加している募集