Files
ChildHood/tools/level-builder/build_cmdline.py
T
starmen1265 a3c38aa92a 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)
- 搭建指南: 全局地面中心坐标+西边界旋转统一+院门改双开门
2026-06-08 19:05:50 +08:00

208 lines
7.0 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.
"""UE5 命令行白模关卡构建脚本。
由 build_level.bat 通过 UE5 的 -ExecutePythonScript 参数调用。
在 UE5 编辑器 Python 环境中运行,无需手动打开编辑器。
用法:
build_level.bat # 默认配置
build_level.bat --json output/v02.json # 指定 JSON
build_level.bat --no-quit # 构建后不退出编辑器
环境变量(由 build_level.bat 设置):
LB_JSON_PATH — JSON 数据文件路径(相对于项目根目录)
LB_MAP_NAME — 目标关卡资产名
LB_MAP_PACKAGE — 关卡包路径(如 /Game/Maps)
LB_NO_QUIT — 设为 "1" 不退出编辑器
"""
import json
import os
import sys
import time
from pathlib import Path
# ---- 从环境变量读取配置 ----
json_rel = os.environ.get("LB_JSON_PATH", "tools/level-builder/output/level_v01.json")
map_name = os.environ.get("LB_MAP_NAME", "LVL_SuburbanV01")
map_package = os.environ.get("LB_MAP_PACKAGE", "/Game/Maps")
no_quit = os.environ.get("LB_NO_QUIT", "0") == "1"
map_asset_path = f"{map_package}/{map_name}"
# ---- 检查 unreal 模块 ----
try:
import unreal
except ImportError:
print("[LevelBuilder] FATAL: unreal 模块不可用,必须在 UE5 环境中运行")
sys.exit(1)
# ---- 项目根目录 ----
# UE5 -ExecutePythonScript 模式下 CWD 是引擎目录,不是项目目录
# 所以必须通过环境变量传入项目路径
_project_root_str = os.environ.get("LB_PROJECT_ROOT", "")
if _project_root_str:
PROJECT_ROOT = Path(_project_root_str)
else:
# Fallback: 尝试从 .uproject 文件定位
_cwd = Path(os.getcwd())
if (_cwd / "ChildHood.uproject").exists():
PROJECT_ROOT = _cwd
else:
# 最后尝试 unreal API
try:
PROJECT_ROOT = Path(unreal.Paths.project_dir()).parent
except Exception:
PROJECT_ROOT = _cwd
json_path = PROJECT_ROOT / json_rel
print(f"[LevelBuilder] ==========================================")
print(f"[LevelBuilder] ChildHood Level Builder — Command Line")
print(f"[LevelBuilder] ==========================================")
print(f"[LevelBuilder] Project: {PROJECT_ROOT}")
print(f"[LevelBuilder] JSON: {json_rel}")
print(f"[LevelBuilder] Map: {map_asset_path}")
print(f"[LevelBuilder]")
# ---- 等待编辑器子系统就绪 ----
def wait_for_editor(timeout=90):
"""轮询等待 EditorActorSubsystem 可用。"""
print("[LevelBuilder] 等待编辑器初始化...")
start = time.time()
while time.time() - start < timeout:
try:
subsys = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
if subsys is not None:
elapsed = time.time() - start
print(f"[LevelBuilder] 编辑器就绪 ({elapsed:.1f}s)")
return True
except Exception:
pass
time.sleep(1)
print(f"[LevelBuilder] FATAL: 编辑器初始化超时 ({timeout}s)")
return False
if not wait_for_editor():
sys.exit(1)
# ---- 设置 sys.path ----
lb_dir = str(PROJECT_ROOT / "tools" / "level-builder")
for p in [str(PROJECT_ROOT), lb_dir]:
if p not in sys.path:
sys.path.insert(0, p)
# ---- 确保目标关卡存在 ----
def ensure_map(asset_path):
"""创建或加载目标关卡。成功返回 True。"""
# 已存在 → 加载
if unreal.EditorAssetLibrary.does_asset_exist(asset_path):
print(f"[LevelBuilder] 加载已有关卡: {asset_path}")
unreal.EditorLevelLibrary.load_level(asset_path)
return True
print(f"[LevelBuilder] 创建新关卡: {asset_path}")
# 方法 1: new_level(asset_path) — UE5.7 EditorLevelLibrary 直接创建
try:
unreal.EditorLevelLibrary.new_level(asset_path)
print("[LevelBuilder] ✅ 关卡创建成功 (new_level)")
return True
except Exception as e:
print(f"[LevelBuilder] new_level 失败: {e}")
# 方法 2: AssetTools + WorldFactory(某些版本可用)
try:
asset_tools = unreal.get_editor_subsystem(unreal.AssetToolsSubsystem)
factory = unreal.WorldFactory()
new_asset = asset_tools.create_asset(
asset_name=map_name,
package_path=map_package,
asset_class=None,
factory=factory,
)
if new_asset:
unreal.EditorLevelLibrary.load_level(asset_path)
print("[LevelBuilder] ✅ 关卡创建成功 (WorldFactory)")
return True
except Exception as e:
print(f"[LevelBuilder] WorldFactory 失败: {e}")
print("[LevelBuilder] FATAL: 无法创建关卡")
return False
if not ensure_map(map_asset_path):
sys.exit(1)
# ---- 验证 JSON ----
if not json_path.exists():
print(f"[LevelBuilder] FATAL: JSON 文件不存在: {json_path}")
sys.exit(1)
# ---- 导入并执行构建 ----
from builder.scene_builder import build_level, setup_post_build
print(f"[LevelBuilder] 开始构建...")
build_log = build_level(str(json_path), clear_existing=True)
# ---- 后处理:GameMode + 大气 + ExponentialHeightFog ----
print("[LevelBuilder] 执行关卡后处理...")
try:
# 添加 ExponentialHeightFog(如果场景中不存在)
all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
has_fog = any("ExponentialHeightFog" in a.get_class().get_name() for a in all_actors)
if not has_fog:
editor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
fog_actor = editor_subsystem.spawn_actor_from_class(
unreal.ExponentialHeightFog,
unreal.Vector(0, 0, 0)
)
if fog_actor:
fog_actor.set_actor_label("LVL_Global_ExpHeightFog")
fog_actor.set_folder_path("LVL_SuburbanV01/Global/Lighting")
fog_comp = fog_actor.get_component_by_class(unreal.ExponentialHeightFogComponent)
if fog_comp:
fog_comp.set_editor_property("fog_density", 0.015)
print("[LevelBuilder] ✅ ExponentialHeightFog 已添加")
except Exception as e:
print(f"[LevelBuilder] ⚠️ ExponentialHeightFog 添加失败: {e}")
setup_post_build()
# ---- 保存关卡 ----
print("[LevelBuilder] 保存关卡...")
try:
unreal.EditorLevelLibrary.save_current_level()
print(f"[LevelBuilder] ✅ 关卡已保存: {map_asset_path}")
except Exception as e:
print(f"[LevelBuilder] ⚠️ 保存失败: {e}")
# ---- 构建结果汇总 ----
if build_log:
ok = sum(1 for b in build_log if b.get("status") == "ok")
fail = len(build_log) - ok
print(f"")
print(f"[LevelBuilder] {'=' * 40}")
print(f"[LevelBuilder] SUCCESS: {ok} | FAILED: {fail} | TOTAL: {len(build_log)}")
print(f"[LevelBuilder] {'=' * 40}")
if fail > 0:
print(f"[LevelBuilder] 失败列表:")
for b in build_log:
if b.get("status") != "ok":
print(f" ❌ {b.get('name', '?')}: {b.get('reason', '未知')}")
# ---- 退出编辑器 ----
if not no_quit:
print("[LevelBuilder] 退出编辑器...")
unreal.SystemLibrary.execute_console_command(None, "quit")
print("[LevelBuilder] Done.")