Files
mapTools/make_prefab.py
2025-12-08 14:23:57 +08:00

203 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pandas as pd
from pathlib import Path
# ============= 配置 =============
# Excel 路径
EXCEL_PATH = "found_方块.xlsx" # 根据你的实际文件名改
# 作为模板的 prefab就是你贴出来那份包含 Ground / Border / Grid
TEMPLATE_PREFAB = "Level90116.prefab" # 根据实际文件名改
OLD_LEVEL_NAME = "Level90116" # 模板 prefab 中的旧关卡名
# 输出目录
OUTPUT_DIR = Path("GeneratedPrefabs")
OUTPUT_DIR.mkdir(exist_ok=True)
# ============= Tile 行模板 =============
# 使用 Ground 里那种 tilem_TileIndex = 0, m_TileSpriteIndex = 0
# 注意:花括号 {{ }} 是为了在 format 之后输出字面上的 { }
TILE_LINE = """ - first: {{x: {x}, y: {y}, z: 0}}
second:
serializedVersion: 2
m_TileIndex: {tile_index}
m_TileSpriteIndex: {sprite_index}
m_TileMatrixIndex: 0
m_TileColorIndex: 0
m_TileObjectToInstantiateIndex: 65535
dummyAlignment: 0
m_AllTileFlags: 1073741825
"""
# ============= 工具函数 =============
def load_template_text() -> str:
"""读取整份 prefab 模板文本"""
return Path(TEMPLATE_PREFAB).read_text(encoding="utf-8")
def replace_level_name(text: str, map_no) -> str:
"""
把根节点名字从 Level80317 改为 Level{map_no}
(只替换这一行)
"""
# 注意:只替换一次,避免误伤别的地方
old = f"m_Name: {OLD_LEVEL_NAME}"
new = f"m_Name: Level{int(map_no)}"
return text.replace(old, new, 1)
def generate_tiles_block(coords):
"""
根据坐标列表生成 m_Tiles: 区块(只包含 m_Tiles 本身,不包括 m_AnimatedTiles 之后的内容)
coords: [(x, y, tile_index, sprite_index), ...]
"""
block = " m_Tiles:\n"
for item in coords:
if len(item) == 2:
x, y = item
tile_index = 1
sprite_index = 0
else:
x, y, tile_index, sprite_index = item
block += TILE_LINE.format(x=int(x), y=int(y), tile_index=int(tile_index), sprite_index=int(sprite_index))
return block
def replace_ground_tiles(text: str, coords) -> str:
"""
只修改 Ground Tilemap 的 m_Tiles: 列表
Border 的 m_Tiles: {} 不动
"""
# 找到第一个 m_Tiles:(就是 Ground 的)
# 本函数保持向后兼容性:如果模板中有多个 Tilemap默认替换第一个 m_Tiles 区块(通常是 Ground
pos = text.find(" m_Tiles:")
if pos == -1:
raise RuntimeError("在模板 prefab 中找不到 'm_Tiles:'")
pos2 = text.find(" m_AnimatedTiles:", pos)
if pos2 == -1:
raise RuntimeError("在模板 prefab 中找不到 'm_AnimatedTiles:' 用来截断。")
before = text[:pos]
after = text[pos2:]
tiles_block = generate_tiles_block(coords)
return before + tiles_block + after
def replace_tilemap_by_gameobject_name(text: str, gameobject_name: str, tiles_coords) -> str:
"""
根据 GameObject 名称m_Name找到紧随其后的第一个 m_Tiles: 块并替换内容。
tiles_coords: list of (x,y,tile_index,sprite_index) 或 (x,y)
"""
lower_text = text.lower()
target_name = f"m_name: {gameobject_name.lower()}"
name_pos = lower_text.find(target_name)
if name_pos == -1:
raise RuntimeError(f"在 prefab 中找不到 GameObject 名称: {gameobject_name}")
tiles_pos = lower_text.find(" m_tiles:", name_pos)
if tiles_pos == -1:
raise RuntimeError(f"在 prefab 中找不到 {gameobject_name} 对应的 m_Tiles: 区块")
animated_pos = lower_text.find(" m_animatedtiles:", tiles_pos)
if animated_pos == -1:
raise RuntimeError("找不到 m_AnimatedTiles 来截断。")
before = text[:tiles_pos]
after = text[animated_pos:]
if tiles_coords:
new_tiles_block = generate_tiles_block(tiles_coords)
else:
new_tiles_block = " m_Tiles: {}\n"
return before + new_tiles_block + after
# ============= 主逻辑 =============
def main():
# 读取 Excel
df = pd.read_excel(EXCEL_PATH)
# 根据你的表头改名(如果不完全一样,请把左边字符串改成实际表头)
df = df.rename(columns={
"地图号 (Map No)": "map",
"X坐标 (X No)": "x",
"Y坐标 (Y No)": "y",
})
template_text = load_template_text()
# 尝试读取 border 相关的 found 文件(可选)
water_df = None
wall_df = None
jump_df = None
if Path("found_water.xlsx").exists():
water_df = pd.read_excel("found_water.xlsx")
water_df = water_df.rename(columns={"地图号 (Map No)": "map", "X坐标 (X No)": "x", "Y坐标 (Y No)": "y"})
if Path("found_wall.xlsx").exists():
wall_df = pd.read_excel("found_wall.xlsx")
wall_df = wall_df.rename(columns={"地图号 (Map No)": "map", "X坐标 (X No)": "x", "Y坐标 (Y No)": "y"})
if Path("found_jump.xlsx").exists():
jump_df = pd.read_excel("found_jump.xlsx")
jump_df = jump_df.rename(columns={"地图号 (Map No)": "map", "X坐标 (X No)": "x", "Y坐标 (Y No)": "y"})
# 按地图号分组,一个 Map 生成一个 prefab
for map_no, group in df.groupby("map"):
ground_coords = list(zip(group["x"], group["y"]))
# 1) 换根节点名字 LevelXXXX
text_with_name = replace_level_name(template_text, map_no)
new_text = text_with_name
# 2) 用当前地图的坐标替换 Ground 的 m_Tiles 区块
# 构建 (x,y,tile_index,sprite_index) 列表Ground 使用索引 0/0和原模板一致
ground_tiles = [(int(x), int(y), 1, 0) for x, y in ground_coords]
if ground_tiles:
try:
new_text = replace_tilemap_by_gameobject_name(new_text, "Ground", ground_tiles)
except Exception as e:
# 回退到旧的替换方式
new_text = replace_ground_tiles(new_text, ground_coords)
# 3) 对 Border 图层,合并 water/wall/jump如果存在并替换
border_tiles = []
# water -> m_TileIndex:2, m_TileSpriteIndex:2
if water_df is not None:
wg = water_df[water_df['map'] == map_no]
border_tiles += [(int(x), int(y), 5, 3) for x, y in zip(wg['x'], wg['y'])]
# wall -> m_TileIndex:0, m_TileSpriteIndex:0
if wall_df is not None:
wg = wall_df[wall_df['map'] == map_no]
border_tiles += [(int(x), int(y), 4, 2) for x, y in zip(wg['x'], wg['y'])]
# jump -> m_TileIndex:1, m_TileSpriteIndex:1
if jump_df is not None:
wg = jump_df[jump_df['map'] == map_no]
border_tiles += [(int(x), int(y), 3, 1) for x, y in zip(wg['x'], wg['y'])]
if border_tiles:
try:
new_text = replace_tilemap_by_gameobject_name(new_text, "Border", border_tiles)
except Exception as e:
print(f"警告:替换 Border 时出错: {e}")
else:
# 没有 border 坐标,清空 Border 图层
try:
new_text = replace_tilemap_by_gameobject_name(new_text, "Border", [])
except Exception as e:
print(f"警告:清空 Border 时出错: {e}")
# 4) 写出新 prefab
out_path = OUTPUT_DIR / f"Level{int(map_no)}.prefab"
out_path.write_text(new_text, encoding="utf-8")
print(f"生成 {out_path} ,共 Ground {len(ground_tiles)} 个 tileBorder {len(border_tiles)} 个 tile")
if __name__ == "__main__":
main()