Prechádzať zdrojové kódy

Complete asset plans before generation

bang 2 týždňov pred
rodič
commit
879855d813
3 zmenil súbory, kde vykonal 67 pridanie a 1 odobranie
  1. 21 1
      pipeline.py
  2. 1 0
      server.py
  3. 45 0
      slot_workflow.py

+ 21 - 1
pipeline.py

@@ -51,6 +51,23 @@ def run(manifest, out_root, creds=None, log=print):
             "输出 PNG,背景必须是真实 Alpha 透明通道,不是白底,不是棋盘格。主体居中,边缘干净,适合用于 App、网页和海报叠加",
         ])
 
+    def alpha_report(img):
+        img = img.convert("RGBA")
+        alpha = img.getchannel("A")
+        mn, mx = alpha.getextrema()
+        transparent = sum(1 for v in alpha.getdata() if v == 0)
+        ratio = transparent / max(1, img.width * img.height)
+        return mn, mx, ratio
+
+    def log_alpha(label, img, required):
+        if not required:
+            return
+        mn, mx, ratio = alpha_report(img)
+        if mn == 0:
+            log(f"✅ [{label}] Alpha 透明通道有效:透明像素 {ratio:.1%}")
+        else:
+            log(f"⚠️ [{label}] 模型返回 PNG 但没有透明 Alpha:alpha={mn}-{mx},请重新生成或换支持透明输出的图像模型")
+
     # ---- A. 角色(Spine)----
     for i, c in enumerate(manifest.get("characters", [])):
         cid = c.get("id", f"char_{i}")
@@ -75,6 +92,7 @@ def run(manifest, out_root, creds=None, log=print):
                                              creds.get("base_url", "https://api.openai.com/v1"),
                                              creds.get("model", "gpt-image-2"),
                                              part.get("size", c.get("size", creds.get("size", "1024x1024"))))
+                    log_alpha(f"{cid}/{part_id}", pimg, True)
                     part_images[part_id] = pimg
                 spine_builder.build_parts_character(cid, part_images, chars_out, anims, parts)
                 w, h = 1000, 1000
@@ -82,13 +100,14 @@ def run(manifest, out_root, creds=None, log=print):
             else:
                 full_prompt = ", ".join(x for x in [
                     c.get("prompt", ""), style,
-                    transparent_prompt("single game icon character, centered, full body in frame, no text"),
+                    transparent_prompt("single game icon character or slot symbol, centered, full body in frame, no text, not a boss, not a demon lord, not dark armor"),
                 ] if x)
                 log(f"🎨 [{cid}] 生成角色图…")
                 img = providers.generate(creds["provider"], full_prompt, creds["api_key"],
                                          creds.get("base_url", "https://api.openai.com/v1"),
                                          creds.get("model", "gpt-image-2"),
                                          c.get("size", creds.get("size", "1024x1024")))
+                log_alpha(cid, img, True)
                 spine_builder.build_character(cid, img, chars_out, anims)
                 w, h = spine_builder.trim_to_content(img).size
                 files = [f"characters/{cid}.json", f"characters/{cid}.atlas", f"characters/{cid}.png"]
@@ -125,6 +144,7 @@ def run(manifest, out_root, creds=None, log=print):
                                      creds.get("base_url", "https://api.openai.com/v1"),
                                      creds.get("model", "gpt-image-2"),
                                      a.get("size", creds.get("size", "1024x1024")))
+            log_alpha(aid, img, transparent)
             os.makedirs(ui_art_out, exist_ok=True)
             img.save(os.path.join(ui_art_out, f"{aid}.png"))
             library["ui_art"].append({"id": aid, "file": f"ui_art/{aid}.png",

+ 1 - 0
server.py

@@ -412,6 +412,7 @@ class Handler(BaseHTTPRequestHandler):
         try:
             manifest = json.loads(data["manifest"]) if isinstance(data.get("manifest"), str) \
                 else data.get("manifest")
+            manifest = slot_workflow.complete_manifest(manifest)
         except Exception as e:
             return self._send(400, {"ok": False, "error": f"manifest 非法 JSON: {e}"})
         creds = {

+ 45 - 0
slot_workflow.py

@@ -109,6 +109,18 @@ BASE_SYMBOLS = {
         ("symbol_coin", "a shiny golden coin fruit slot symbol"),
         ("symbol_seven", "a red lucky seven fruit slot symbol with gold trim"),
     ],
+    "pirate": [
+        ("symbol_treasure_chest", "a glossy pirate treasure chest overflowing with bright gold coins, premium mobile slot icon"),
+        ("symbol_pirate_hat", "a black pirate captain hat with gold trim and skull emblem, polished 3D slot symbol"),
+        ("symbol_parrot", "a cute colorful pirate parrot, bright tropical mobile slot symbol, clean edges"),
+        ("symbol_compass", "a golden pirate compass with blue glass face, polished 3D slot icon"),
+        ("symbol_map_scatter", "a rolled treasure map with red X mark, parchment and gold trim, glossy slot symbol, no text"),
+        ("symbol_anchor", "a shiny gold anchor wrapped with rope, pirate treasure slot icon"),
+        ("symbol_ship", "a cute miniature pirate ship with black sails on blue waves, polished 3D icon"),
+        ("symbol_gem", "a bright blue jewel gemstone on gold coins, premium pirate slot icon"),
+        ("symbol_cannon", "a cute stylized pirate cannon with gold details, mobile slot symbol"),
+        ("symbol_coin", "a shiny golden pirate coin with skull emboss, glossy slot icon"),
+    ],
     "pirate_jelly": [
         ("jelly_pirate_red", "a red translucent jelly pirate mascot with a black skull pirate hat, winking, glossy gummy texture, cute mobile slot symbol"),
         ("jelly_pirate_pink", "a pink jelly candy pirate mascot holding a tiny gold coin, cheerful face, glossy 3D gummy texture"),
@@ -125,12 +137,22 @@ BASE_SYMBOLS = {
 
 
 UI_ART = [
+    ("cover", False, "1024x1536", "vertical mobile game cover key art, title-safe top area, hero mascot and theme world, premium app store style, no small text"),
+    ("loading_screen", False, "1024x1536", "vertical mobile game loading screen background, theme world, clear center area for progress bar, no characters, no text"),
     ("bg_main", False, "1024x1536", "vertical mobile slot game background, clear top area for logo, central reel area, bright themed world, no characters, no text"),
     ("logo", True, "1024x1024", "glossy mobile slot game logo lettering, playful premium game title, thick outline, sparkles, transparent background"),
     ("reel_frame", True, "1024x1024", "rounded rectangle slot machine reel frame, glowing border, hollow transparent center, clean mobile game UI element"),
     ("btn_spin", True, "1024x1024", "large round glossy spin button with circular arrows icon, premium 3D mobile game button, transparent background"),
     ("btn_round", True, "1024x1024", "small round glossy secondary mobile game UI button, blank center, transparent background"),
+    ("btn_minus", True, "1024x1024", "small glossy circular minus button with a clear minus icon, mobile game UI, transparent background"),
+    ("btn_plus", True, "1024x1024", "small glossy circular plus button with a clear plus icon, mobile game UI, transparent background"),
+    ("btn_turbo", True, "1024x1024", "small glossy turbo lightning button icon, mobile slot game UI, transparent background"),
+    ("btn_auto", True, "1024x1024", "small glossy auto spin button icon with circular arrow, mobile slot game UI, transparent background"),
     ("hud_pill", True, "1024x1024", "horizontal rounded pill shaped mobile game HUD panel, glossy dark translucent material, blank, transparent background"),
+    ("coin", True, "1024x1024", "single shiny golden coin particle sprite, round clean edges, mobile game VFX asset, transparent background"),
+    ("menu_panel", True, "1024x1024", "large rounded mobile game menu panel frame, glossy themed material, blank center, transparent background"),
+    ("settings_panel", True, "1024x1024", "mobile game settings popup panel frame, glossy themed material, blank center, transparent background"),
+    ("paytable_panel", True, "1024x1024", "mobile slot paytable popup panel frame, glossy themed material, blank center, transparent background"),
     ("win_popup", True, "1024x1024", "big win popup frame, glossy gold and candy highlights, blank center, transparent background"),
     ("free_spin_badge", True, "1024x1024", "free spins bonus badge, glossy premium mobile slot UI, blank center, transparent background"),
 ]
@@ -423,6 +445,29 @@ def build_manifest(slot_config):
     }
 
 
+def complete_manifest(manifest):
+    """Fill missing generated sections before running the image pipeline.
+
+    Older or hand-edited manifests may only contain characters. When a
+    slot_config exists, it is the source of truth for the complete asset plan.
+    """
+    manifest = copy.deepcopy(manifest or {})
+    if manifest.get("slot_config"):
+        full = build_manifest(manifest["slot_config"])
+        for key in ("game", "style", "characters", "ui_art", "vfx", "ui"):
+            manifest[key] = full.get(key)
+        manifest["slot_config"] = full.get("slot_config", manifest["slot_config"])
+        return manifest
+    if not manifest.get("ui_art"):
+        manifest["ui_art"] = [
+            {"id": aid, "transparent": transparent, "size": size, "prompt": prompt}
+            for aid, transparent, size, prompt in UI_ART
+        ]
+    manifest.setdefault("vfx", [])
+    manifest.setdefault("ui", [])
+    return manifest
+
+
 def build_workflow(data):
     slot_config = build_slot_config(data or {})
     manifest = build_manifest(slot_config)