AI Game Asset Generation: A Working Pipeline (Sprites to UI)
AI Game Asset Generation: A Working Pipeline (Sprites to UI)
Honesty note: this is the exact pipeline I run for my game (Merge Fish 2048) — AI generates the art, then a small Python post-processing script turns it into game-ready assets. No proprietary tricks, just reproducible steps with code you can adapt.
TL;DR
- AI art needs a post-processing step before it’s game-ready. Generated sprites arrive with white backgrounds, mixed sizes, and no text on buttons — every one of those is fixable with a small Python script.
- Never let the AI “remove the background” on white-background art. It cuts holes in white fish bellies and white button edges. A pixel-threshold flood approach (below) keeps the art intact.
- Standardize sizes in the pipeline, not in the engine. Resize once to your target dimensions (buttons 320×96, board 720×720, bubbles 48/72/96) so the game never does layout math at runtime.
- Build the pipeline as a re-runnable script, not a one-off. When the AI generates a v2 of your title art, you re-run one file and get consistent output — that’s what makes AI iteration cheap.
The pipeline at a glance
AI generates art (white background, any size)
↓
1. download_assets() → local copies (re-runnable, no re-download)
↓
2. remove_white_bg() → transparent background, edge-gradient preserved
↓
3. trim_to_content() → crop away transparent margins
↓
4. resize / canvas-pad → exact target sizes (buttons, board, bubbles)
↓
5. add_button_text() → centered text with shadow + outline
↓
6. crop particles from clusters → small/medium/large bubbles
Each step is deterministic — run the script again, get the same output. That’s the property that makes AI iteration cheap.
Step 1 — Download with a re-runnable pattern
ASSETS = {
"title": "https://aka.doubaocdn.com/s/QOeyPeHUkB",
"btn_green": "https://aka.doubaocdn.com/s/wUv6VJ79ll",
"btn_blue": "https://aka.doubaocdn.com/s/aBSZJnk7N4",
"board_bg": "https://aka.doubaocdn.com/s/Gu5aY9yVuK",
"bubble_single": "https://aka.doubaocdn.com/s/zZAVmnj0ug",
"bubble_cluster": "https://aka.doubaocdn.com/s/0bs0qpC6cu",
}
def download_assets():
for name, url in ASSETS.items():
path = os.path.join(TMP_DIR, name + ".png")
if not os.path.exists(path):
urllib.request.urlretrieve(url, path)
Key idea: skip downloads that already exist. Re-running the script doesn’t re-fetch anything, so you can regenerate downstream steps freely.
Step 2 — Remove white backgrounds without destroying art
This is the step that matters most. A naive “make white transparent” turns fish bellies and button highlights into holes. The fix is a two-threshold gradient:
def remove_white_bg(img, threshold=235, edge_threshold=200):
img = img.convert("RGBA")
pixels = img.load()
w, h = img.size
for y in range(h):
for x in range(w):
r, g, b, a = pixels[x, y]
min_val = min(r, g, b)
if min_val >= threshold:
pixels[x, y] = (r, g, b, 0) # clearly white → transparent
elif min_val >= edge_threshold:
# near-white → partially transparent, proportional to whiteness
alpha = int((threshold - min_val) / (threshold - edge_threshold) * 255)
pixels[x, y] = (r, g, b, alpha)
return img
Why this works: pure white (min_val ≥ 235) goes fully transparent; near-white edge pixels (200–235) keep a soft alpha gradient, so anti-aliased edges and subtle highlights survive. A single hard cut at 235 would fringe every curve.
Tune per asset: title art is forgiving (240/210), particle bubbles need gentler edges (240/215). Keep the thresholds as parameters.
Step 3 — Trim to content
def trim_to_content(img):
bbox = img.getbbox()
if bbox:
return img.crop(bbox)
return img
AI generations have unpredictable margins. Cropping to the alpha bounding box removes that variance before resizing, so your target-size math is exact.
Step 4 — Standardize sizes
Resize or canvas-pad to exact targets so the game engine never scales at runtime:
target_w, target_h = 320, 96
img = img.resize((target_w, target_h), Image.LANCZOS)
# canvas-pad to a square (keeps aspect, centers content):
canvas = Image.new("RGBA", (target, target), (0,0,0,0))
canvas.paste(img, ((target-new_w)//2, (target-new_h)//2), img)
My actual targets: buttons 320×96 and 380×96, board background 720×720, single bubble 256×256, particles 48/72/96.
Step 5 — Add text to buttons
Buttons generated by AI rarely include the text you need. Draw it on with a shadow and a rounded outline so it reads on any background:
draw.text((tx+2, ty+3), text, font=font, fill=(0,0,0,100)) # shadow
for dx in range(-outline_width, outline_width+1): # outline
for dy in range(-outline_width, outline_width+1):
if dx*dx + dy*dy <= outline_width*outline_width:
draw.text((tx+dx, ty+dy), text, font=font, fill=outline_color)
draw.text((tx, ty), text, font=font, fill=text_color) # main
Use a bold system font (Impact or Arial Bold) — game UI text needs weight at small sizes. The outline is drawn as a filled circle sweep, which keeps it round instead of square.
Step 6 — Crop particles from a cluster
Instead of asking the AI for three separate bubble sizes, generate one cluster and crop:
small = img.crop((int(w*0.05), int(h*0.1), int(w*0.25), int(h*0.3)))
medium = img.crop((int(w*0.3), int(h*0.3), int(w*0.55), int(h*0.55)))
large = img.crop((int(w*0.6), int(h*0.25), int(w*0.9), int(h*0.55)))
Then trim + resize each to 48/72/96. This gives you a consistent particle family from one source image.
Common pitfalls
- Single-threshold background removal → holes and fringes in light-colored art. Always use the gradient version.
- Not trimming before resize → aspect-ratio math is off, content gets scaled into wrong margins.
- Letting the engine scale at runtime → blurry sprites and layout drift across screen sizes. Bake the target size into the asset.
- Hand-editing generated files → the next AI iteration overwrites your fixes. Put every transformation in the script, so re-runs reproduce everything.
- Ignoring file size → 2048×2048 AI exports bloat your build. Resize down to what the game actually shows (see the size guide).
Bottom line
AI generates art fast; a deterministic post-processing script makes it consistent. The two-threshold background removal is the single highest-value step — it’s the difference between professional-looking sprites and art that looks AI-made. Once the pipeline exists, regenerating assets after an AI iteration takes minutes, not hours. This is one piece of the full AI-assisted workflow that turned my solo timeline into two prototypes.