#!/usr/bin/env python3 """从 Cocos 贴图 + Unity pivot 生成 tile-display-meta.json(裁剪后可见尺寸)。""" from __future__ import annotations import json import re import struct from pathlib import Path UNITY_THEMES = { "silu": "silu", "snow": "snow", "sanxing": "sanxing", "chinese": "Chinese", "numMan": "numMan", "redarmy": "redArmy", "default": "Level", } TILE_NAMES = [ "Baseblock", "JumpBlock", "WallBlock", "kuai11", "Decor23", "素材切图-23", "素材切图2-23", "小游戏素材红色_03", ] def png_size(path: Path) -> tuple[int, int]: with path.open("rb") as f: f.read(16) return struct.unpack(">II", f.read(8)) def alpha_bbox(png_path: Path) -> tuple[int, int, int, int] | None: try: from PIL import Image except ImportError: return None try: img = Image.open(png_path).convert("RGBA") return img.getbbox() except Exception: return None def read_unity_sprite_meta(meta_path: Path) -> dict | None: if not meta_path.is_file(): return None text = meta_path.read_text(encoding="utf-8") pivot = re.search(r"spritePivot:\s*\{x:\s*([^,]+),\s*y:\s*([^}]+)\}", text) ppu = re.search(r"spritePixelsToUnits:\s*(\d+(?:\.\d+)?)", text) if not pivot: return None return { "pivotX": float(pivot.group(1)), "pivotY": float(pivot.group(2)), "ppu": float(ppu.group(1)) if ppu else 100.0, } def resolve_draw_rect( png_path: Path, full_w: int, full_h: int, pivot_x: float, pivot_y: float, tile_name: str, ) -> dict: bbox = alpha_bbox(png_path) if not bbox: return { "width": full_w, "height": full_h, "pivotX": pivot_x, "pivotY": pivot_y, "fitMul": 1.0, } left, top, right, bottom = bbox tw, th = right - left, bottom - top if tw <= 0 or th <= 0 or (tw >= full_w and th >= full_h): return { "width": full_w, "height": full_h, "pivotX": pivot_x, "pivotY": pivot_y, "fitMul": 1.0, } pivot_bottom = pivot_y * full_h trim_bottom = full_h - bottom pivot_bottom_trim = pivot_bottom - trim_bottom meta_pivot_y = max(0.0, min(1.0, pivot_bottom_trim / th)) meta_pivot_x = max(0.0, min(1.0, (pivot_x * full_w - left) / tw)) fit_mul = 1.0 out_pivot_y = meta_pivot_y # kuai11 篝火:底座贴齐地面格,略放大消除缝隙 if tile_name == "kuai11": fit_mul = 1.12 # 底座在裁剪图底部,pivot 放低使底座对齐格子 out_pivot_y = max(0.28, min(0.42, meta_pivot_y * 0.45)) return { "width": tw, "height": th, "pivotX": round(meta_pivot_x, 4), "pivotY": round(out_pivot_y, 4), "fitMul": fit_mul, } def build_theme(cocos_tex: Path, unity_tex: Path, unity_folder: str, cocos_key: str) -> dict: theme_dir = unity_tex if unity_folder == "Level" else unity_tex / unity_folder cocos_dir = cocos_tex if cocos_key == "default" else cocos_tex / cocos_key tiles: dict[str, dict] = {} for name in TILE_NAMES: png = cocos_dir / f"{name}.png" if not png.is_file(): if name == "Decor23": png = cocos_dir / "素材切图-23.png" elif name == "素材切图-23": png = cocos_dir / "Decor23.png" if not png.is_file(): continue unity_png = theme_dir / png.name meta = read_unity_sprite_meta(unity_png.with_suffix(".png.meta")) if not meta: continue w, h = png_size(png) draw = resolve_draw_rect(png, w, h, meta["pivotX"], meta["pivotY"], name) tiles[name] = {**draw, "ppu": meta["ppu"]} return tiles def main(): import argparse ap = argparse.ArgumentParser() ap.add_argument("--unity-root", required=True) ap.add_argument("--out", default="assets/resources/theme/tile-display-meta.json") args = ap.parse_args() project = Path(__file__).resolve().parent.parent unity_tex = Path(args.unity_root) / "Assets" / "Texture" cocos_tex = project / "assets/resources/textures" themes: dict[str, dict] = {} for cocos_key, unity_folder in UNITY_THEMES.items(): tiles = build_theme(cocos_tex, unity_tex, unity_folder, cocos_key) if tiles: themes[cocos_key] = tiles print(f" {cocos_key}: {len(tiles)} tiles") out = project / args.out out.parent.mkdir(parents=True, exist_ok=True) payload = {"version": 1, "themes": themes} out.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8") print(f"Wrote -> {out}") if __name__ == "__main__": main()