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
+95 -9
View File
@@ -28,7 +28,7 @@ _LB_DIR = _SCRIPT_DIR.parent # tools/level-builder/
if str(_LB_DIR) not in sys.path:
sys.path.insert(0, str(_LB_DIR))
from config.asset_mapping import MESH_ASSETS
from config.asset_mapping import MESH_ASSETS, get_pivot_compensation
from config.material_mapping import MATERIAL_PATHS
logger = logging.getLogger("level_builder.builder")
@@ -87,6 +87,8 @@ def build_level(json_path: str, clear_existing: bool = True, zones: list[str] =
actor = _spawn_sky_light(editor_subsystem, actor_def)
elif asset_type == "point_light":
actor = _spawn_point_light(editor_subsystem, actor_def)
elif asset_type == "exp_height_fog":
actor = _spawn_exp_height_fog(editor_subsystem, actor_def)
else:
actor = _spawn_mesh_actor(editor_subsystem, actor_def)
@@ -148,20 +150,35 @@ def _spawn_mesh_actor(editor_subsystem, actor_def: dict):
"""生成 StaticMesh Actor。"""
asset_key = actor_def.get("asset_key", "")
if not asset_key or asset_key not in MESH_ASSETS:
print(f" [警告] {actor_def['actor_name']}: 资产 '{asset_key}' 未找到,使用默认 Cube")
print(f" [警告] {actor_def['actor_name']}: 资产 '{asset_key}' 未在映射表中找到,使用默认 Cube")
asset_key = "Cube"
mesh_path = MESH_ASSETS[asset_key]["path"]
static_mesh = unreal.load_asset(mesh_path)
if not static_mesh:
print(f" [警告] {actor_def['actor_name']}: 无法加载资产 '{mesh_path}'")
return None
print(f" [警告] {actor_def['actor_name']}: 无法加载资产 '{mesh_path}',使用 Cube 占位")
# 使用 Cube 作为占位,但名称加 [MISSING] 前缀以醒目标记
cube_path = MESH_ASSETS["Cube"]["path"]
static_mesh = unreal.load_asset(cube_path)
if not static_mesh:
print(f" [错误] {actor_def['actor_name']}: 连 Cube 都无法加载")
return None
# 标记为缺失资产
actor_def = dict(actor_def) # 不修改原始 dict
actor_def["actor_name"] = f"[MISSING]{actor_def['actor_name']}"
actor_def["material"] = "MI_DevPink" # 醒目的粉色标记
# 生成
# 生成 — 应用锚点补偿
loc = actor_def.get("location", [0, 0, 0])
scale = actor_def.get("scale", [1, 1, 1])
rot = actor_def.get("rotation", [0, 0, 0])
dx, dy = get_pivot_compensation(asset_key, scale, rot)
spawn_x = loc[0] - dx
spawn_y = loc[1] - dy
actor = editor_subsystem.spawn_actor_from_object(
static_mesh,
unreal.Vector(loc[0], loc[1], loc[2])
unreal.Vector(spawn_x, spawn_y, loc[2])
)
if not actor:
@@ -169,7 +186,10 @@ def _spawn_mesh_actor(editor_subsystem, actor_def: dict):
# 设置 Transform
rot = actor_def.get("rotation", [0, 0, 0])
actor.set_actor_rotation(unreal.Rotator(rot[0], rot[1], rot[2]), False)
# 使用关键字参数避免位置参数顺序歧义(UE5.7 Rotator 位置参数非 P,Y,R 顺序)
actor.set_actor_rotation(
unreal.Rotator(pitch=rot[0], yaw=rot[1], roll=rot[2]), False
)
scale = actor_def.get("scale", [1, 1, 1])
actor.set_actor_scale3d(unreal.Vector(scale[0], scale[1], scale[2]))
@@ -202,7 +222,9 @@ def _spawn_player_start(editor_subsystem, actor_def: dict):
)
if actor:
rot = actor_def.get("rotation", [0, 0, 0])
actor.set_actor_rotation(unreal.Rotator(rot[0], rot[1], rot[2]), False)
actor.set_actor_rotation(
unreal.Rotator(pitch=rot[0], yaw=rot[1], roll=rot[2]), False
)
actor.set_actor_label(actor_def["actor_name"])
folder = actor_def.get("folder", "")
if folder:
@@ -219,7 +241,9 @@ def _spawn_directional_light(editor_subsystem, actor_def: dict):
)
if actor:
rot = actor_def.get("rotation", [-45, 315, 0])
actor.set_actor_rotation(unreal.Rotator(rot[0], rot[1], rot[2]), False)
actor.set_actor_rotation(
unreal.Rotator(pitch=rot[0], yaw=rot[1], roll=rot[2]), False
)
actor.set_actor_label(actor_def["actor_name"])
folder = actor_def.get("folder", "")
if folder:
@@ -255,3 +279,65 @@ def _spawn_point_light(editor_subsystem, actor_def: dict):
if folder:
actor.set_folder_path(folder)
return actor
def _spawn_exp_height_fog(editor_subsystem, actor_def: dict):
"""生成 ExponentialHeightFog Actor。"""
loc = actor_def.get("location", [0, 0, 0])
actor = editor_subsystem.spawn_actor_from_class(
unreal.ExponentialHeightFog,
unreal.Vector(loc[0], loc[1], loc[2])
)
if actor:
actor.set_actor_label(actor_def["actor_name"])
folder = actor_def.get("folder", "")
if folder:
actor.set_folder_path(folder)
# 设置雾密度
fog_comp = actor.get_component_by_class(unreal.ExponentialHeightFogComponent)
if fog_comp:
fog_density = actor_def.get("fog_density", 0.02)
fog_comp.set_editor_property("fog_density", fog_density)
return actor
def setup_post_build():
"""关卡后处理:设置 GameMode、启用大气雾等。
在 build_level 之后调用。
"""
if not HAS_UNREAL:
return
# 1. 设置关卡 GameMode — 使用 BP_ThirdPersonGameMode
try:
# load_asset 用不带 _C 的资产路径
gm_asset_path = "/Game/ThirdPerson/Blueprints/BP_ThirdPersonGameMode.BP_ThirdPersonGameMode"
gm_class_path = gm_asset_path + "_C"
gm_asset = unreal.load_asset(gm_asset_path)
if gm_asset:
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
for a in all_actors:
if a.get_class().get_name() == "WorldSettings":
# game_mode_override 接受 UClass,通过 SoftClassPath 设置
soft_ref = unreal.SoftClassPath(gm_class_path)
a.set_editor_property("game_mode_override", soft_ref)
print(f"[LevelBuilder] ✅ GameMode 已设置: {gm_class_path}")
break
else:
print(f"[LevelBuilder] ⚠️ GameMode 资产未找到: {gm_asset_path}")
except Exception as e:
print(f"[LevelBuilder] ⚠️ 设置 GameMode 失败: {e}")
# 2. 启用 DirectionalLight 的大气效果
try:
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
for a in all_actors:
if "DirectionalLight" in a.get_class().get_name():
dl_comp = a.get_component_by_class(unreal.DirectionalLightComponent)
if dl_comp:
dl_comp.set_editor_property("atmosphere_sun_light", True)
print(f"[LevelBuilder] ✅ DirectionalLight 大气效果已启用")
break
except Exception as e:
print(f"[LevelBuilder] ⚠️ DirectionalLight 设置失败: {e}")