Complete Cocos Creator port with level bundles, themes, and tooling.
Adds level prefabs, theme assets, audio, extensions, and deployment scripts for the Unity WebGL migration. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
165
tools/build_theme_tile_meta.py
Normal file
165
tools/build_theme_tile_meta.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user