fix(level-builder): 锚点补偿+旋转修复+天空盒+解析器去重

- asset_mapping: 38种BSP资产实测锚点+pivot_offset_xy自动补偿
- scene_builder: Rotator改关键字参数修正旋转轴映射
- scene_builder: spawn前XY锚点补偿(支持Yaw旋转)
- scene_builder: 新增ExponentialHeightFog+大气效果后处理
- build_cmdline: 构建后自动添加雾+GameMode+DirectionalLight大气
- build_guide_parser: 去重+全局地面+旋转列名兼容+边界段范围修正
- BarrierWall实际尺寸修正(60×210非20×100)
- 搭建指南: 全局地面中心坐标+西边界旋转统一+院门改双开门
This commit is contained in:
2026-06-08 19:05:50 +08:00
parent c3c10a9fcb
commit a3c38aa92a
13 changed files with 1284 additions and 1879 deletions
@@ -138,6 +138,9 @@ class BuildGuideParser:
# 添加 Player Start
self._add_player_start()
# 去重:检测同名称 Actor,保留后出现的
self._deduplicate_actors()
# 构建输出
result = {
"metadata": self.metadata,
@@ -163,11 +166,15 @@ class BuildGuideParser:
# Phase B — 地面
# ----------------------------------------------------------
def _parse_phase_ground(self, lines: list[str]):
"""解析 Phase B 地面铺设表格。"""
text = "\n".join(lines)
# 找到 Phase B 区域
b_start = self._find_section_start(lines, "Phase B")
b_end = self._find_section_start(lines, "Phase C")
"""解析 Phase B 地面铺设表格。仅处理 B1 铺设地面段,不处理 B2 边界段。"""
# 找到地面铺设段起始
b_start = self._find_section_start(lines, "铺设地面")
if b_start < 0:
b_start = self._find_section_start(lines, "Phase B")
# 地面段在边界段前结束
b_end = self._find_section_start(lines, "放置边界围挡")
if b_end < 0:
b_end = self._find_section_start(lines, "Phase C")
if b_start < 0:
return
if b_end < 0:
@@ -175,10 +182,13 @@ class BuildGuideParser:
section_text = "\n".join(lines[b_start:b_end])
# 地面子区域标题:#### Z1 家 地面(MI_DevGray)
# 地面子区域标题:#### Z1 家 地面(MI_DevGray)或 #### 全局地面(MI_DevGray)
floor_pattern = re.compile(
r'####\s+Z(\d+)\s+\S+\s+地面(?:((.+?)))?'
)
global_floor_pattern = re.compile(
r'####\s+全局地面(?:((.+?)))?'
)
current_zone = ""
current_material = ""
@@ -196,6 +206,14 @@ class BuildGuideParser:
i += 1
continue
# 检测全局地面
gm = global_floor_pattern.match(line)
if gm:
current_zone = "GLOBAL"
current_material = gm.group(1) or "MI_DevGray"
i += 1
continue
# 检测水面区域
if '水面' in line and current_zone:
current_material = "MI_DevSkyBlue"
@@ -289,7 +307,11 @@ class BuildGuideParser:
asset_key = clean_cell(row.get("资产", ""))
location = parse_location(row.get("位置 (X,Y,Z)", row.get("位置", "")))
rotation = parse_rotation(row.get("旋转 (Pitch,Yaw,Roll)", row.get("旋转", "(0,0,0)")))
rotation = parse_rotation(
row.get("旋转 (Pitch,Yaw,Roll)")
or row.get("旋转 (P,Y,R)")
or row.get("旋转", "(0,0,0)")
)
scale = parse_scale(row.get("缩放", "—"))
material = clean_cell(row.get("材质", ""))
@@ -309,7 +331,11 @@ class BuildGuideParser:
def _expand_range_row(self, row: dict, name: str) -> list[ActorData]:
"""展开范围格式行。如 'LVL_BND_East_01~15' → 15 个 Actor。"""
asset_key = clean_cell(row.get("资产", ""))
rotation = parse_rotation(row.get("旋转 (Pitch,Yaw,Roll)", row.get("旋转", "(0,0,0)")))
rotation = parse_rotation(
row.get("旋转 (Pitch,Yaw,Roll)")
or row.get("旋转 (P,Y,R)")
or row.get("旋转", "(0,0,0)")
)
scale = parse_scale(row.get("缩放", "—"))
material = clean_cell(row.get("材质", ""))
@@ -478,7 +504,10 @@ class BuildGuideParser:
)
# 旋转:结构体表可能有,布景表可能没有
rot_col = row.get("旋转 (Pitch,Yaw,Roll)", row.get("旋转", ""))
# 兼容多种列名格式:(Pitch,Yaw,Roll)、(P,Y,R)、无后缀
rot_col = (row.get("旋转 (Pitch,Yaw,Roll)")
or row.get("旋转 (P,Y,R)")
or row.get("旋转", ""))
rotation = parse_rotation(rot_col) if rot_col else (0.0, 0.0, 0.0)
# 缩放
@@ -724,6 +753,33 @@ class BuildGuideParser:
note="Z1 院内院门内侧约60cm,面向+Y(北)",
))
# ----------------------------------------------------------
# 去重
# ----------------------------------------------------------
def _deduplicate_actors(self):
"""去除同名称的重复 Actor,保留后出现的(后出现的通常是更完整的定义)。"""
seen = {}
unique = []
for actor in self.actors:
name = actor.actor_name
if name in seen:
# 同名但位置不同——可能是合法的同名(如 Z3 墙段),保留
prev = seen[name]
if prev.location == actor.location and prev.asset_key == actor.asset_key:
logger.warning(f"去重:移除重复 Actor '{name}' (loc={actor.location})")
continue # 跳过完全重复的
else:
# 同名但不同位置/资产——合法,保留
unique.append(actor)
else:
seen[name] = actor
unique.append(actor)
removed = len(self.actors) - len(unique)
if removed > 0:
logger.info(f"去重完成:移除 {removed} 个重复 Actor")
self.actors = unique
# ----------------------------------------------------------
# 辅助方法
# ----------------------------------------------------------