Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
166 lines
4.7 KiB
Python
166 lines
4.7 KiB
Python
#!/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()
|