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:
2026-06-16 15:30:58 +08:00
parent cba5105908
commit d393302388
6248 changed files with 17322729 additions and 11036 deletions

View File

@@ -15,6 +15,8 @@ import re
import sys
from pathlib import Path
from export_unity_prefab_maps import parse_level_prefab
DIR = {
"Direction.North": "Direction.North",
"Direction.East": "Direction.East",
@@ -47,13 +49,19 @@ def kind_from_path(path: str) -> str:
return "vehicle"
if "enemy" in p:
return "enemy"
if "nprop" in p:
return "prop_decor"
if "prop" in p:
return "prop"
return "prop_decor"
def prop_placement_from_path(path: str) -> str | None:
if "nprop" in path.lower():
return "ground"
if "prop" in path.lower():
return "block"
return None
def parse_spawns(block: str) -> list[dict]:
spawns = []
for m in SPAWN_RE.finditer(block):
@@ -68,6 +76,9 @@ def parse_spawns(block: str) -> list[dict]:
"y": int(pm.group(2)),
"kind": kind_from_path(path),
}
placement = prop_placement_from_path(path)
if placement and item["kind"] == "prop":
item["propPlacement"] = placement
pdir = PDIR_RE.search(body)
vdir = VDIR_RE.search(body)
if pdir:
@@ -114,33 +125,44 @@ def spawn_to_ts(s: dict, indent: str) -> str:
parts.append(f"playerDirection: {s['playerDirection']}")
if "vehicleDirection" in s:
parts.append(f"vehicleDirection: {s['vehicleDirection']}")
if s.get("propPlacement"):
parts.append(f"propPlacement: '{s['propPlacement']}'")
return indent + "{ " + ", ".join(parts) + " }"
def emit_ts(levels: dict[int, dict], border_cache: dict[str, dict]) -> str:
def emit_ts(levels: dict[int, dict], export_const: str = "LEVELS_600") -> str:
lines = [
"/* AUTO-GENERATED by tools/export_unity_levels.py — DO NOT EDIT */",
"/* spawns 来自 Levels*.csground/border 来自 Assets/Prefabs/Level/LevelN.prefab Tilemap */",
"import { Direction } from '../core/Define';",
"import { LevelConfig, SpawnConfig } from './LevelTypes';",
"import { LevelConfig } from './LevelTypes';",
"",
"const BORDER_CACHE: Record<string, Record<string, boolean>> = " + json.dumps(border_cache, separators=(',', ':')) + ";",
"",
"function withBorder(key: string, cfg: Omit<LevelConfig, 'border'>): LevelConfig {",
" return { ...cfg, border: BORDER_CACHE[key] };",
"}",
"",
"export const LEVELS_600: Record<number, LevelConfig> = {",
f"export const {export_const}: Record<number, LevelConfig> = {{",
]
for lid in sorted(levels.keys()):
L = levels[lid]
lines.append(f" {lid}: withBorder('{L['borderKey']}', {{")
lines.append(f" {lid}: {{")
lines.append(f" levelID: {lid},")
lines.append(f" boundary: {{ x: {L['boundary']['x']}, y: {L['boundary']['y']} }},")
lines.append(
f" boundary: {{ x: {L['boundary']['x']}, y: {L['boundary']['y']} }},"
)
if L.get("ground"):
lines.append(
" ground: "
+ json.dumps(L["ground"], ensure_ascii=False, separators=(",", ": "))
+ ","
)
if L.get("border"):
lines.append(
" border: "
+ json.dumps(L["border"], ensure_ascii=False, separators=(",", ": "))
+ ","
)
lines.append(" spawns: [")
for s in L["spawns"]:
lines.append(spawn_to_ts(s, " ") + ",")
lines.append(" ],")
lines.append(" }),")
lines.append(" },")
lines.append("};")
lines.append("")
return "\n".join(lines)
@@ -167,21 +189,49 @@ def main():
ap.add_argument("--input", required=True, help="Unity Levels*.cs path")
ap.add_argument("--output", required=True, help="Output .ts path")
ap.add_argument("--border-cache", default="", help="Optional border cache json")
ap.add_argument("--limit", type=int, default=0, help="Max levels to export (0 = all)")
ap.add_argument("--export-const", default="LEVELS_600", help="TS export const name")
ap.add_argument(
"--prefab-dir",
default="",
help="Unity Assets/Prefabs/Level 目录,合并 LevelN.prefab 的 Tilemap",
)
args = ap.parse_args()
text = Path(args.input).read_text(encoding="utf-8")
levels = parse_file(text)
if args.limit > 0:
sorted_ids = sorted(levels.keys())[: args.limit]
levels = {k: levels[k] for k in sorted_ids}
if not levels:
print("No levels parsed", file=sys.stderr)
sys.exit(1)
border_cache = build_border_cache(levels)
prefab_dir = Path(args.prefab_dir) if args.prefab_dir else None
merged_prefab = 0
if prefab_dir:
for lid, L in levels.items():
maps = parse_level_prefab(prefab_dir / f"Level{lid}.prefab")
if maps:
L["ground"] = maps.get("ground", {})
L["border"] = maps.get("border", {})
merged_prefab += 1
else:
ring = build_border_cache({lid: L})[L["borderKey"]]
L["border"] = ring
print(f"Merged Tilemap from {merged_prefab} prefabs")
else:
border_cache = build_border_cache(levels)
for L in levels.values():
L["border"] = border_cache[L["borderKey"]]
out = Path(args.output)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(emit_ts(levels, border_cache), encoding="utf-8")
out.write_text(emit_ts(levels, args.export_const), encoding="utf-8")
if args.border_cache:
Path(args.border_cache).write_text(json.dumps(border_cache, indent=2), encoding="utf-8")
cache = {str(lid): L.get("border", {}) for lid, L in levels.items()}
Path(args.border_cache).write_text(json.dumps(cache, indent=2), encoding="utf-8")
print(f"Exported {len(levels)} levels -> {out}")