ここまで敵もキャラも「データ」として扱ってきた。最後に必要なのは“姿”だ。立ち絵・背景・アイコン。でも、絵は描けない。Archipelago Saga の答えは「AIで生成し、機械的に整える」。画像生成は素材を量産できるが、背景付き・サイズばらばらのままではゲームに置けない。鍵は「生成 → 整える」を流れ作業にすること。第5回は、絵が描けなくても世界観の揃った素材が揃う量産フローを、実コードで開く。
▶ 第4回(マップ・シーン・セーブ)の続き。全体像は連載ハブ。完成版の立ち絵・背景が、この工程の成果物だ。
目次
第5回のゴール
AI生成画像を「ゲームで使える透過アセット」に変える量産パイプラインを理解する。生成 → 候補選別 → 透過切り出し(BiRefNet) → 正規化 → 配置、の5工程。実際に作った素材は 敵立ち絵93+ボス6・主人公18(9職×男女)・戦闘背景67・アイコン290。1枚ずつ手作業では無理な量を、型にして回す。
Step 1. 生成は量産できる。だが、そのままでは使えない
AI画像生成は確かに素材を量産する。問題はその後だ。生成物は背景付きで、被写体の大きさも位置もバラバラ。これをそのままゲームに並べると、背景が四角く残り、キャラのサイズが不揃いになる。だから「生成」と同じくらい「整える工程」が重要で、そこを自動化できるかどうかで量産速度が決まる。
用途別にスクリプトを分けて生成する(ai_enemies.py / ai_characters.py / ai_bosses_comfy.py / ai_items.py / ai_maptiles.py …)。生成原画は捨てず、_raw / cand(候補)に退避し、採用版だけを本番ディレクトリへ移す。「やり直せる」状態を常に残すのがコツだ。
Step 2. 候補から選ぶ:生成は当たり外れがある
AI生成は1回で完璧は出ない。1つの被写体に複数枚生成し、HTMLの一覧(asset_review.html)で目視して採用版を選ぶ。透過の確認には市松模様(チェッカーボード)背景を敷くと、抜けの良し悪しが一目で分かる。良いものを apply_cand.py で本番へ確定し、ダメなものはそのidだけ再生成する。

asset_review.html。市松模様の背景で透過の抜けを目視し、作り直したいものだけ NG にチェック→チェック分だけの再生成リクエストを出す。Step 3. 透過切り出し:BiRefNetで背景を抜く(核心)
パイプラインの心臓が、背景除去だ。BiRefNet(高精度の背景除去モデル)を rembg 経由で使い、被写体だけのタイトな透過PNGにする。再利用できる関数 cut() に全工程を閉じ込めてある。
# scripts/cutout_single.py(抜粋)
from rembg import remove, new_session
session = new_session("birefnet-general") # ← モデルは一度だけロード(バッチで使い回す)
def cut(in_path, out_path):
im = Image.open(in_path).convert("RGB")
res = remove(im, session=session, alpha_matting=True, # 背景除去+アルファマット(柔らかい境界)
alpha_matting_foreground_threshold=240,
alpha_matting_background_threshold=12).convert("RGBA")
arr = np.asarray(res); rgb, alpha = arr[:,:,:3].copy(), arr[:,:,3].copy()
# ① 最大の連結成分だけ残す(背後の台座や浮きゴミを落とす)
fg = (alpha > 30).astype(np.uint8)
n, lab = cv2.connectedComponents(fg)
if n > 2:
best = max(range(1, n), key=lambda i: (lab == i).sum())
alpha = (alpha * (lab == best)).astype(np.uint8)
# ② 内部の穴を埋める+境界の色にじみ除去(decontaminate)
rgb = decontaminate(rgb, alpha)
# ③ 余白を切り詰め、被写体が枠に触れないよう透明border(pad)を足す
ys, xs = np.where(alpha > 8)
out = crop_to(ys, xs); out = pad_border(out)
Image.fromarray(out, "RGBA").save(out_path)
やっていることは4つ。(1) BiRefNet で「被写体らしさ」のマスクを作り、(2) 一番大きな塊だけ残して背後の額縁やゴミを捨て、(3) 穴を埋め、境界の背景色のにじみを消し、(4) 余白を詰めてタイトにする。session を一度だけロードして使い回すので、何百枚もバッチで高速処理できる(cut_batch())。
Step 4. 品質ゲート:“見た目”で判断しない
ここが実戦の勘所だ。切り出しの成否を目視の印象で決めると失敗する。2つの機械的チェックを必ず通す。
① 見切れ検出(CLIP)
生成時に被写体が画像の端で切れていると、透過しても“見切れた”ままになる。これを「マスクが画像の枠に接していないか」で自動判定する。cut() は切り出すたびにこの値(CLIP)を返す。
# マスクが上下左右いずれかの枠に接していたら、生成時点で見切れている
fa = out[:, :, 3] # アルファ(透過)チャンネル
clip = max((fa[0, :] > 16).mean(), (fa[-1, :] > 16).mean(),
(fa[:, 0] > 16).mean(), (fa[:, -1] > 16).mean())
# clip が ~0.01 を超えたら、その被写体は見切れ → 引き構図で再生成する
② 透過はアルファ値で確認する
暗い被写体(黒いモンスター等)を画像ビューアで開くと、透過部分が黒く描画されて「背景が残った」と誤判定しやすい。見た目でなくアルファ値で確かめる。隅のアルファが0なら透過OKだ。
a = np.asarray(Image.open(out_path).convert("RGBA"))[:, :, 3]
assert a[0, 0] == 0 and a.min() == 0 # 隅=0・最小=0 なら確実に透過できている
💡 生成プロンプト側の対策:背後に台座・額縁・カードが描かれると、最大連結成分が台座ごと残ってしまう。プロンプトに「単一の浮いた被写体・背後に何も無い・NO panel/plaque/frame/border」を明示し、フルボディは「引き構図・四辺に厚い余白・枠に触れない」を強制する。整える工程の前に、生成を整えるのが一番効く。
Step 5. 正規化と、地形だけは手続き生成
切り出しただけでは、まだ大きさがバラバラ。立ち絵は均一なキャンバスに揃える(正規化)。主人公は9職×男女=18枚を同じ規格に正規化し(characters_norm/)、戦闘でも編成でもサイズが揃って見えるようにする。
一方、海・草・砂のように「繋いで敷く」地形タイルは、AI生成より手続き生成が向く。AIだと継ぎ目ができるが、FFTノイズで生成すればシームレスに繋がる。「被写体=生成」「地形=手続き生成」と使い分けるのが品質の分かれ目だ。AIは万能の絵筆というより、得意分野のはっきりした職人として使う。
Step 6. 配置:データの art パスと一致させる
最後に、採用版を本番ディレクトリへ。ここで第2回が効いてくる。データの art が指すパスと、置くファイル名を一致させるだけでいい。
// 第2回のデータ:敵の art はこのパスを指している
var EA = "../assets_ai/enemies/";
slime: { name: "スライム", art: EA + "enemy_slime.png", /* … */ }
// ↑
// assets_ai/enemies/enemy_slime.png にファイルを置けば、戦闘でも図鑑でも自動表示される
用途別ディレクトリ(enemies/ 93・bosses/ 6・characters_norm/ 18・battle_bg/ 67・items_game/ 290 …)に置けば完了。データが id で参照しているので、正しい名前で置く=即ゲームに反映。第2回「マッドクラブ追加」で残していた最後のピース(立ち絵 enemy_mudcrab.png)も、ここで埋まる。
例えば、敵1体は最初から最後までこう流れる
第2回で追加した「マッドクラブ」の立ち絵を、この量産フローで仕上げる場面を通しで見る。①ai_enemies.py のプロンプト(共通スタイル+「泥まみれの蟹の魔物」)で複数枚生成し、原画は _raw に退避。②asset_review.html の市松模様の上で見比べ、姿勢と輪郭が良い1枚を選ぶ。③cut() で透過を切り出し、返ってきた CLIP 値が閾値以下(見切れ無し)・隅のアルファ0(透過OK)を確認。④apply_cand.py で assets_ai/enemies/enemy_mudcrab.png へ確定。ブラウザを再読み込みすれば、戦闘にも図鑑にもマッドクラブが立っている。
1体あたりの人間の判断は②の「選ぶ」だけで、コンピュータの検査(③)が品質の門番を務める。この分担だから、99体でも品質が揃う。
候補を何枚生成するかにも目安がある。1体につき2〜4枚で十分だ。10枚出すと選択肢は増えるが、見比べる時間と迷いも増え、量産のリズムが崩れる。「4枚見て良いのが無ければ、プロンプトの方を疑って出し直す」と決めておくと、選別が沼にならない。生成のガチャは引き直せるが、選別に溶けた時間は戻らない。枚数の上限は、時間を守るための線になる。量産の敵は、判断の渋滞だ。基準を明文化して、迷う時間を仕組みで削る。

art パスと名前を合わせるだけで、図鑑にも戦闘にも同じ絵が出る。命名規約が、パイプラインの背骨になる
地味だが全工程を貫いているのが、ファイル名の規約だ。enemy_<id>.png・hero_<class>.png のように「データのidから機械的に決まる名前」で統一している。おかげで、生成スクリプトは出力先を計算でき、レビュー画面はidと画像を突き合わせられ、ゲームはデータの art パスで参照できる。もし名前が「slime_final_v2_ok.png」のような人間の気分で付いていたら、この自動化はどこかで破綻する。名前を規約にした瞬間、ファイルシステムがデータベースになる。個人開発で一番安上がりなインフラだ。規約は最初の10体で決めて、以後は絶対に崩さない。途中で変えると、旧規約の残骸が一番厄介なゴミになる。
まとめ:第5回の要点
- 生成≠完成:背景付き・不揃いの生成物を「整える工程」を自動化できるかが量産速度を決める。
- 5工程の型:生成 → 候補選別 → 透過(BiRefNet) → 正規化 → 配置。
- BiRefNet切り出し:最大連結成分・穴埋め・色抜き・余白pad。セッションは一度ロードしてバッチ処理。
- 品質は機械で判定:見切れはCLIP(枠接触)、透過はアルファ値で確認。見た目で判断しない。
- 地形は手続き生成:繋ぐタイルはFFTノイズでシームレスに。
- 配置=名前合わせ:データの
artパスとファイル名を一致させれば即反映。
次回(最終回・第6回)は、ここまで作った全部が本当に動くことを「自動で証明する」方法へ。puppeteer でタイトルから戦闘・リザルトまで通しプレイし、「コンソールエラー0・画像/音声の404ゼロ」を完了条件にするヘッドレス検証と、静的ファイルを置くだけのデプロイを開く。第3回のDOM非依存設計が、ここで効いてくる。
※ AI生成コンテンツの開示:本作の BGM・効果音・キャラクター画像・ボイスは、すべて生成AIの出力物です(BGM=ACE-Step 1.5〈MIT・出力の商用利用可を配布元モデルカードで確認済み〉 / ボイス=Irodori-TTS 系の合成設計音声・実在人物の複製ではありません / 効果音=数値合成 / 画像=画像生成AI〈各サービスの利用条件で出力の商用利用可を確認のうえ使用。透過処理は BiRefNet〈MIT〉を rembg 経由で使用〉)。第三者の著作物は同梱していません。© 2026 Cooliris(本作のコード・構成・データの権利は作者に帰属します)。
👉 ← 第4回:マップ・シーン・セーブ ・ 連載ハブ ・ ▶ 遊ぶ ・ ほかのアプリの作り方