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
+46 -41
View File
@@ -69,6 +69,7 @@ LVL_SuburbanV01/
| Wall_100x300 | `Architecture/Walls/SM_Wall_100x300` | 100×20×300 |
| Wall_400x100 | `Architecture/Walls/SM_Wall_400x100` | 400×20×100 |
| WallDoor_200x300 | `Architecture/Walls/SM_WallDoor_200x300` | 200×20×300 |
| WallDoorDouble_400x300 | `Architecture/Walls/SM_WallDoorDouble_400x300` | 400×20×300(双开门洞) |
| WallDoorGarage_400x300 | `Architecture/Walls/SM_WallDoorGarage_400x300` | 400×20×300 |
| WallWindow_400x300 | `Architecture/Walls/SM_WallWindow_400x300` | 400×20×300 |
| WallWindowSmall_200x300 | `Architecture/Walls/SM_WallWindowSmall_200x300` | 200×20×300 |
@@ -197,15 +198,19 @@ LVL_SuburbanV01/
### B1 铺设地面
按区域铺设地面 Floor,使用 Floor_1000x1000 平铺。
先铺设全局地面,再按区域铺设地面 Floor。
#### Z1 家 地面(MI_DevGray)
#### 全局地面(MI_DevGray)
| # | Actor 名称 | 资产 | 位置 (X,Y,Z) | 缩放 |
|---|-----------|------|-------------|------|
| 1 | LVL_Z1_STR_Floor | Floor_1000x1000 | (500, 400, 0) | (1, 0.8, 1) |
| 1 | LVL_Global_GroundFloor | Floor_1000x1000 | (4000, 3000, -5) | (8, 6, 1) |
> Z1 范围 (0,0)→(1000,800),Floor_1000x1000 中心点 (500,400),Y 方向缩放 0.8 覆盖 800cm。
> 全局地面覆盖整个场景 8000×6000cm,Z=-5 略低于区域地面,作为兜底防穿模。
#### Z1 家 地面(MI_DevGray)
> Z1 院子地面在 C1 结构体表中定义(LVL_Z1_STR_CourtyardFloor),此处不重复。
#### Z2 小卖部 地面(MI_DevGray)
@@ -265,7 +270,7 @@ LVL_SuburbanV01/
| # | Actor 名称 | 资产 | 位置 (X,Y,Z) | 旋转 (Pitch,Yaw,Roll) | 材质 |
|---|-----------|------|-------------|----------------------|------|
| 1 | LVL_BND_West_01 | Wall_400x300 | (0, 200, 0) | (0,0,0) → Yaw=90° | MI_DevRed |
| 1 | LVL_BND_West_01 | Wall_400x300 | (0, 200, 0) | (0,90,0) | MI_DevRed |
| 2 | LVL_BND_West_02 | Wall_400x300 | (0, 600, 0) | (0,90,0) | MI_DevRed |
| 3 | LVL_BND_West_03 | Wall_400x300 | (0, 1000, 0) | (0,90,0) | MI_DevRed |
| 4 | LVL_BND_West_04 | Wall_400x300 | (0, 1400, 0) | (0,90,0) | MI_DevRed |
@@ -281,19 +286,15 @@ LVL_SuburbanV01/
| 14 | LVL_BND_West_14 | Wall_400x300 | (0, 5400, 0) | (0,90,0) | MI_DevRed |
| 15 | LVL_BND_West_15 | Wall_400x300 | (0, 5800, 0) | (0,90,0) | MI_DevRed |
> Wall_400x300 旋转 90° 后沿 Y 方向排列,每块 400cm 高 × 300cm 长(需纵向排列覆盖 250cm 墙高)。
> **实际操作**:Wall_400x300 放置时默认长轴沿 X。Yaw=90° 旋转后长轴沿 Y。但墙高只有 300cm,而围挡只需 250cm,300cm 足够。
> **修正**:Wall 默认高度 300cm(Z轴),宽度 400cm(X轴),厚度 20cm(Y轴)。放置在 X=0 处,Yaw=90° 使 400cm 宽度沿 Y 方向。每块覆盖 Y 方向 400cm,15 块覆盖 6000cm。Z 方向 300cm > 250cm 要求,足够。
> Wall_400x300 默认 400×20×300。Yaw=90° 后 400cm 宽度沿 Y 方向。15 块 × 400cm = 6000cm 覆盖全线。
#### 东边界(X=8000, Y=0-6000)
| # | Actor 名称 | 资产 | 位置 (X,Y,Z) | 旋转 | 材质 |
|---|-----------|------|-------------|------|------|
| 1-15 | LVL_BND_East_01~15 | BarrierWall_600 | (8000, 200+400n, 0) n=0~14 | (0,90,0) | MI_DevBlue |
| 1-15 | LVL_BND_East_01~15 | Wall_400x300 | (8000, 200+400n, 0) n=0~14 | (0,90,0) | MI_DevBlue |
> BarrierWall_600 默认 600cm 长。旋转 90° 后沿 Y 方向。10 块覆盖 6000cm。
> **修正**:BarrierWall_600 高度 100cm,需要堆叠 3 层达到 300cm。改为使用 Wall_400x300 更经济。
> 实际建议:使用 Wall_400x300 替代,与西边界相同方式,15 块。
> 改用 Wall_400x300(300cm 高度一次到位),与西边界相同方式。15 块 × 400cm = 6000cm。
#### 北边界(Y=6000, X=0-8000)
@@ -307,26 +308,29 @@ LVL_SuburbanV01/
南边界分段处理:
- X=0-1000:Z1 南院墙(已含在 Z1 结构体中)
- X=1000-1600:Z2 后墙区域
- X=1600-5000:施工围挡
- X=5000-8000:补齐围挡
- X=1000-1600:Z2 后墙区域(已含在 Z2 结构体中)
- X=1600-5500:施工围挡(Wall_400x300)
- X=5500-7000:**Z7 碰撞围墙已覆盖**(见 C7 结构体),此处不重复放置
- X=7000-8000:补齐围挡
| # | Actor 名称 | 资产 | 位置 (X,Y,Z) | 材质 |
|---|-----------|------|-------------|------|
| 1 | LVL_BND_South_01 | BarrierWall_600 | (1900, 0, 0) | MI_DevBlue |
| 2 | LVL_BND_South_02 | BarrierWall_600 | (2500, 0, 0) | MI_DevBlue |
| 3 | LVL_BND_South_03 | BarrierWall_600 | (3100, 0, 0) | MI_DevBlue |
| 4 | LVL_BND_South_04 | BarrierWall_600 | (3700, 0, 0) | MI_DevBlue |
| 5 | LVL_BND_South_05 | BarrierWall_600 | (4300, 0, 0) | MI_DevBlue |
| 6 | LVL_BND_South_06 | BarrierWall_600 | (4900, 0, 0) | MI_DevBlue |
| 7 | LVL_BND_South_07 | BarrierWall_600 | (5500, 0, 0) | MI_DevBlue |
| 8 | LVL_BND_South_08 | BarrierWall_600 | (6100, 0, 0) | MI_DevBlue |
| 9 | LVL_BND_South_09 | BarrierWall_600 | (6700, 0, 0) | MI_DevBlue |
| 10 | LVL_BND_South_10 | BarrierWall_600 | (7300, 0, 0) | MI_DevBlue |
| 11 | LVL_BND_South_11 | BarrierWall_600 | (7900, 0, 0) | MI_DevBlue |
| 1 | LVL_BND_South_01 | Wall_400x300 | (1800, 0, 0) | MI_DevBlue |
| 2 | LVL_BND_South_02 | Wall_400x300 | (2200, 0, 0) | MI_DevBlue |
| 3 | LVL_BND_South_03 | Wall_400x300 | (2600, 0, 0) | MI_DevBlue |
| 4 | LVL_BND_South_04 | Wall_400x300 | (3000, 0, 0) | MI_DevBlue |
| 5 | LVL_BND_South_05 | Wall_400x300 | (3400, 0, 0) | MI_DevBlue |
| 6 | LVL_BND_South_06 | Wall_400x300 | (3800, 0, 0) | MI_DevBlue |
| 7 | LVL_BND_South_07 | Wall_400x300 | (4200, 0, 0) | MI_DevBlue |
| 8 | LVL_BND_South_08 | Wall_400x300 | (4600, 0, 0) | MI_DevBlue |
| 9 | LVL_BND_South_09 | Wall_400x300 | (5000, 0, 0) | MI_DevBlue |
| 10 | LVL_BND_South_10 | Wall_400x300 | (5400, 0, 0) | MI_DevBlue |
| 11 | LVL_BND_South_11 | Wall_400x300 | (7200, 0, 0) | MI_DevBlue |
| 12 | LVL_BND_South_12 | Wall_400x300 | (7600, 0, 0) | MI_DevBlue |
> BarrierWall_600 需注意高度仅 100cm。建议堆叠 2-3 层达到 200-300cm 视觉高度。
> **替代方案**:改用 Wall_400x300 更高效(300cm 高度一次到位)。
> X=5500-7000 区段由 Z7 碰撞围墙(CollisionFront/2/3)覆盖,不重复放置。
> 全部改用 Wall_400x300(300cm 高度一次到位),间距 400cm。
> X=1600-5500 共 10 块,X=7000-8000 共 2 块。
---
@@ -351,7 +355,7 @@ LVL_SuburbanV01/
| 5 | LVL_Z1_STR_EastWall_01 | Wall_400x300 | (980, 200, 0) | (0,90,0) | — | MI_DevRed |
| 6 | LVL_Z1_STR_EastWall_02 | Wall_400x300 | (980, 600, 0) | (0,90,0) | — | MI_DevRed |
| 7 | LVL_Z1_STR_NorthWall_West | Wall_400x300 | (200, 780, 0) | (0,0,0) | — | MI_DevRed |
| 8 | LVL_Z1_STR_NorthWall_Gate | WallDoor_200x300 | (500, 780, 0) | (0,0,0) | — | MI_DevRed |
| 8 | LVL_Z1_STR_NorthWall_Gate | WallDoorDouble_400x300 | (500, 780, 0) | (0,0,0) | — | MI_DevRed |
| 9 | LVL_Z1_STR_NorthWall_East | Wall_400x300 | (800, 780, 0) | (0,0,0) | — | MI_DevRed |
| 10 | LVL_Z1_STR_GateDoor | RotativeDoor_01 | (500, 780, 0) | (0,0,0) | — | MI_DevGreen |
| 11 | LVL_Z1_STR_HouseSouthWall | WallWindow_400x300 | (400, 30, 0) | (0,0,0) | — | MI_DevWhite |
@@ -366,9 +370,9 @@ LVL_SuburbanV01/
| 20 | LVL_Z1_STR_HouseRoof_02 | Floor_300x400 | (650, 230, 280) | (0,0,0) | — | MI_DevBrown |
| 21 | LVL_Z1_STR_CourtyardFloor | Floor_1000x1000 | (500, 400, 0) | (0,0,0) | (1, 0.8, 1) | MI_DevGray |
> **Z1 房屋内空间**:约 (200,30,0)→(800,410,280),即 600×380×280cm。
> **门洞高度问题**:WallDoor_200x300 门洞 200cm 宽,300cm 高,但设计要求院门高度 220cm。300cm 资产足够高,门洞实际开口约 200cm 高(标准 BSP 门洞),对于 120cm 身高儿童足够通过。
> **房屋北墙上方补齐**:WallDoor_200x300 上方需要补齐至 280cm。可用 Wall_200x100 放在 Z=200 处,或者直接用 Wall_400x300 整块(门洞在下方,上方自然有墙体)。
> **院门修改**:改用 WallDoorDouble_400x300(双开门洞墙),门洞更宽(~200cm),匹配执行案院门 200cm 宽要求。
> **Z1 房屋**:西墙/东墙旋转 (0,90,0) 使其沿 Y 轴展开,正确围合房屋。
> **房屋北墙**:WallDoor_200x300 门洞用于室内木门(100cm 宽足够),上方用 Wall_400x300 自然覆盖。
#### C1.2 布景物(Props)
@@ -679,17 +683,18 @@ LVL_SuburbanV01/
#### C7.1 结构体
| # | Actor 名称 | 资产 | 位置 (X,Y,Z) | 缩放 | 材质 |
|---|-----------|------|-------------|------|------|
| 1 | LVL_Z7_STR_Building | Building_01 | (6400, 800, 0) | — | MI_DevGray |
| 2 | LVL_Z7_STR_CollisionFront | Wall_400x300 | (6200, 150, 0) | — | MI_DevGray |
| 3 | LVL_Z7_STR_CollisionFront2 | Wall_400x300 | (6600, 150, 0) | — | MI_DevGray |
| 4 | LVL_Z7_STR_CollisionFront3 | Wall_400x300 | (7000, 150, 0) | — | MI_DevGray |
| 5 | LVL_Z7_STR_CollisionSide | Wall_400x300 | (5750, 400, 0) | (0,90,0) | MI_DevGray |
| 6 | LVL_Z7_STR_CollisionSide2 | Wall_400x300 | (5750, 800, 0) | (0,90,0) | MI_DevGray |
| # | Actor 名称 | 资产 | 位置 (X,Y,Z) | 旋转 (P,Y,R) | 缩放 | 材质 |
|---|-----------|------|-------------|-------------|------|------|
| 1 | LVL_Z7_STR_Building | Building_01 | (6400, 800, 0) | (0,0,0) | — | MI_DevGray |
| 2 | LVL_Z7_STR_CollisionFront | Wall_400x300 | (6200, 150, 0) | (0,0,0) | — | MI_DevGray |
| 3 | LVL_Z7_STR_CollisionFront2 | Wall_400x300 | (6600, 150, 0) | (0,0,0) | — | MI_DevGray |
| 4 | LVL_Z7_STR_CollisionFront3 | Wall_400x300 | (7000, 150, 0) | (0,0,0) | — | MI_DevGray |
| 5 | LVL_Z7_STR_CollisionSide | Wall_400x300 | (5750, 400, 0) | (0,90,0) | — | MI_DevGray |
| 6 | LVL_Z7_STR_CollisionSide2 | Wall_400x300 | (5750, 800, 0) | (0,90,0) | — | MI_DevGray |
> Building_01 放置在 (6400, 800) — 即区域中心偏南。
> 碰撞围墙阻挡玩家靠近。Building_01 的默认尺寸可能与设计 1200×600 有出入,需在编辑器中调整。
> CollisionFront 沿 X 轴展开(不旋转),CollisionSide 沿 Y 轴展开(Yaw=90°)。
> 碰撞围墙同时作为全局南边界的一部分(X=5800-7000 区段),南边界不再重复放置围挡。
#### C7.2 布景物
+25 -1
View File
@@ -145,11 +145,35 @@ if not json_path.exists():
# ---- 导入并执行构建 ----
from builder.scene_builder import build_level
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] 保存关卡...")
+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}")
+48
View File
@@ -0,0 +1,48 @@
@echo off
setlocal enabledelayedexpansion
set "SCRIPT_DIR=%~dp0"
set "PROJECT_DIR=%SCRIPT_DIR%..\.."
for %%i in ("%PROJECT_DIR%\.") do set "PROJECT_ROOT=%%~fi"
set "UPROJECT=%PROJECT_ROOT%\ChildHood.uproject"
set "UE_EDITOR="
if defined UE5_ROOT (
if exist "%UE5_ROOT%\Engine\Binaries\Win64\UnrealEditor.exe" (
set "UE_EDITOR=%UE5_ROOT%\Engine\Binaries\Win64\UnrealEditor.exe"
)
)
if "%UE_EDITOR%"=="" (
for %%p in (
"C:\Program Files\Epic Games\UE_5.7"
"C:\Program Files (x86)\Epic Games\UE_5.7"
"D:\Program Files\Epic Games\UE_5.7"
) do (
if not defined UE_EDITOR (
if exist "%%~p\Engine\Binaries\Win64\UnrealEditor.exe" (
set "UE_EDITOR=%%~p\Engine\Binaries\Win64\UnrealEditor.exe"
)
)
)
)
if "%UE_EDITOR%"=="" (
echo [ERROR] UE5 not found
exit /b 1
)
set "SCRIPT_PATH=%SCRIPT_DIR%check_pivots.py"
set "SCRIPT_PATH=!SCRIPT_PATH:\=/!"
set "UPROJECT_FWD=!UPROJECT:\=/!"
set "LB_PROJECT_ROOT=!PROJECT_ROOT!"
echo.
echo === Pivot Check ===
echo UE5: !UE_EDITOR!
echo Script: !SCRIPT_PATH!
echo.
"!UE_EDITOR!" "!UPROJECT_FWD!" -unattended -NoSound -NullRHI -ExecutePythonScript=!SCRIPT_PATH!
echo.
echo Done. Check output\pivot_check.txt
+123
View File
@@ -0,0 +1,123 @@
"""BSP 资产锚点检测脚本。
在 UE5 编辑器 Python 环境中运行,检测每个资产的 Bounds Origin,
判断锚点是中心、角落还是底部中心。
用法:
方式1 — 命令行: python main.py check-pivots
方式2 — 编辑器内: exec(open(r"check_pivots.py").read())
"""
import sys
import os
from pathlib import Path
# 将 level-builder 根目录加入 sys.path
_SCRIPT_DIR = Path(__file__).resolve().parent
_LB_DIR = _SCRIPT_DIR
if str(_LB_DIR) not in sys.path:
sys.path.insert(0, str(_LB_DIR))
from config.asset_mapping import MESH_ASSETS
try:
import unreal
HAS_UNREAL = True
except ImportError:
HAS_UNREAL = False
def check_pivots():
"""检测所有 BSP 资产的锚点类型。"""
if not HAS_UNREAL:
print("[错误] 需要在 UE5 编辑器 Python 环境中运行")
return
results = []
for name, info in MESH_ASSETS.items():
path = info["path"]
default_size = info.get("default_size")
mesh = unreal.load_asset(path)
if not mesh:
results.append(f"{name}: ⚠️ 无法加载")
continue
# 获取 Bounding Box (UE5 Box 对象有 min/max,没有 origin/extent)
bb = mesh.get_bounding_box()
bmin = bb.min
bmax = bb.max
mn_x, mn_y, mn_z = bmin.x, bmin.y, bmin.z
mx_x, mx_y, mx_z = bmax.x, bmax.y, bmax.z
size_x = mx_x - mn_x
size_y = mx_y - mn_y
size_z = mx_z - mn_z
# 判断锚点类型:
# CENTER: min ≈ (-half, -half, -half),对称包围盒
# CORNER: min ≈ (0, 0, 0),锚点在角落
# CENTER_BOTTOM: min ≈ (-half, -half, 0),锚点在底部中心
pivot_type = "UNKNOWN"
offset_desc = ""
if default_size:
dw, dh, dd = default_size
half_w, half_h, half_d = dw / 2, dh / 2, dd / 2
tol = 5 # cm 容差
is_corner = (abs(mn_x) < tol and abs(mn_y) < tol and abs(mn_z) < tol)
is_center = (abs(mn_x + half_w) < tol and abs(mn_y + half_h) < tol
and abs(mn_z + half_d) < tol)
is_center_bot = (abs(mn_x + half_w) < tol and abs(mn_y + half_h) < tol
and abs(mn_z) < tol)
if is_corner:
pivot_type = "CORNER"
elif is_center:
pivot_type = "CENTER"
elif is_center_bot:
pivot_type = "CENTER_BOTTOM"
else:
pivot_type = "OTHER"
offset_desc = f" (min=({mn_x:.0f},{mn_y:.0f},{mn_z:.0f}), expected corner=0/center=-{half_w:.0f})"
else:
# 无 default_size,用 min 值推断
tol = 5
if abs(mn_x) < tol and abs(mn_y) < tol and abs(mn_z) < tol:
pivot_type = "CORNER?"
elif mn_z < -tol:
pivot_type = "CENTER?"
else:
pivot_type = "CENTER_BOTTOM?"
offset_desc = f" (no default_size, inferred)"
results.append(
f"{name}:\n"
f" min=({mn_x:.1f}, {mn_y:.1f}, {mn_z:.1f}) "
f"max=({mx_x:.1f}, {mx_y:.1f}, {mx_z:.1f}) "
f"size=({size_x:.0f}, {size_y:.0f}, {size_z:.0f})\n"
f" default_size={default_size} → pivot={pivot_type}{offset_desc}"
)
print("=" * 60)
print("BSP 资产锚点检测结果")
print("=" * 60)
for r in results:
print(r)
print("=" * 60)
# 保存结果
out_path = Path(__file__).parent / "output" / "pivot_check.txt"
out_path.parent.mkdir(exist_ok=True)
with open(out_path, "w", encoding="utf-8") as f:
f.write("BSP 资产锚点检测结果\n")
f.write("=" * 60 + "\n")
for r in results:
f.write(r + "\n")
print(f"\n结果已保存: {out_path}")
# 供命令行调用
if __name__ == "__main__":
check_pivots()
+133 -2
View File
@@ -4,208 +4,339 @@
资产根路径: Content/BlockingStarterPack/Meshes/
路径格式: /Game/<ContentSubPath>/<AssetName>.<AssetName>
pivot_offset_xy: 锚点偏离视觉中心的 XY 偏移量(局部坐标系)。
- 搭建指南中所有位置统一为「视觉中心」
- builder 生成 Actor 时减去此偏移 × 缩放 × 旋转,得到实际 spawn 位置
- (0, 0) 表示锚点已在中心(如底面中心类资产),无需补偿
"""
import math
# 资产路径前缀
MESH_ROOT = "/Game/BlockingStarterPack/Meshes"
MATERIAL_ROOT = "/Game/BlockingStarterPack/Materials"
MESH_ASSETS = {
# ---- 建筑 — 墙体 ----
# 实测: min=(0, -thickness/2, 0), max=(width, thickness/2, height)
# 锚点在底边一端,X方向偏移半个宽度
"Wall_400x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_Wall_400x300.SM_Wall_400x300",
"default_size": (400, 20, 300),
"actual_bounds": ((0, -10, 0), (400, 10, 300)),
"pivot_offset_xy": (200, 0),
"category": "wall",
},
"Wall_200x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_Wall_200x300.SM_Wall_200x300",
"default_size": (200, 20, 300),
"actual_bounds": ((0, -10, 0), (200, 10, 300)),
"pivot_offset_xy": (100, 0),
"category": "wall",
},
"Wall_100x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_Wall_100x300.SM_Wall_100x300",
"default_size": (100, 20, 300),
"actual_bounds": ((0, -10, 0), (100, 10, 300)),
"pivot_offset_xy": (50, 0),
"category": "wall",
},
"Wall_400x100": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_Wall_400x100.SM_Wall_400x100",
"default_size": (400, 20, 100),
"actual_bounds": ((0, -10, 0), (400, 10, 100)),
"pivot_offset_xy": (200, 0),
"category": "wall",
},
"WallDoor_200x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_WallDoor_200x300.SM_WallDoor_200x300",
"default_size": (200, 20, 300),
"actual_bounds": ((0, -10, 0), (200, 10, 300)),
"pivot_offset_xy": (100, 0),
"category": "wall_door",
},
"WallDoorGarage_400x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_WallDoorGarage_400x300.SM_WallDoorGarage_400x300",
"default_size": (400, 20, 300),
"actual_bounds": ((0, -10, 0), (400, 10, 300)),
"pivot_offset_xy": (200, 0),
"category": "wall_door",
},
"WallDoorDouble_400x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_WallDoorDouble_400x300.SM_WallDoorDouble_400x300",
"default_size": (400, 20, 300),
"actual_bounds": ((0, -10, 0), (400, 10, 300)),
"pivot_offset_xy": (200, 0),
"category": "wall_door",
},
"WallWindow_400x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_WallWindow_400x300.SM_WallWindow_400x300",
"default_size": (400, 20, 300),
"actual_bounds": ((0, -10, 0), (400, 10, 300)),
"pivot_offset_xy": (200, 0),
"category": "wall_window",
},
"WallWindowSmall_200x300": {
"path": f"{MESH_ROOT}/Architecture/Walls/SM_WallWindowSmall_200x300.SM_WallWindowSmall_200x300",
"default_size": (200, 20, 300),
"actual_bounds": ((0, -10, 0), (200, 10, 300)),
"pivot_offset_xy": (100, 0),
"category": "wall_window",
},
# ---- 建筑 — 地板 ----
# 实测: min=(0, 0, -20), max=(width, height, 2)
# 锚点在角落,XY 都偏移半尺寸
"Floor_1000x1000": {
"path": f"{MESH_ROOT}/Architecture/Floors/SM_Floor_1000x1000.SM_Floor_1000x1000",
"default_size": (1000, 1000, 10),
"actual_bounds": ((0, 0, -20), (1000, 1000, 2)),
"pivot_offset_xy": (500, 500),
"category": "floor",
},
"Floor_300x400": {
"path": f"{MESH_ROOT}/Architecture/Floors/SM_Floor_300x400.SM_Floor_300x400",
"default_size": (300, 400, 10),
"actual_bounds": ((0, 0, -20), (300, 400, 2)),
"pivot_offset_xy": (150, 200),
"category": "floor",
},
"Floor_200x300": {
"path": f"{MESH_ROOT}/Architecture/Floors/SM_Floor_200x300.SM_Floor_200x300",
"default_size": (200, 300, 10),
"actual_bounds": ((0, 0, -20), (200, 300, 2)),
"pivot_offset_xy": (100, 150),
"category": "floor",
},
# ---- 建筑 — 门 ----
"RotativeDoor_01": {
"path": f"{MESH_ROOT}/Architecture/Doors/SM_RotativeDoor_01.SM_RotativeDoor_01",
"default_size": None,
"actual_bounds": ((-100, -12, 0), (0, 12, 216)),
"pivot_offset_xy": (50, 0), # off-center, pivot at hinge side
"category": "door",
},
"GarageDoorOpen_01": {
"path": f"{MESH_ROOT}/Architecture/Doors/SM_GarageDoorOpen_01.SM_GarageDoorOpen_01",
"default_size": None,
"actual_bounds": ((-150, -224.5, 220), (150, 5.9, 296.4)),
"pivot_offset_xy": (0, 109), # X centered, Y offset
"category": "door",
},
# ---- 建筑 — 窗户 ----
"FakeWindowsFrame": {
"path": f"{MESH_ROOT}/Architecture/WindowsSet/SM_FakeWindowsFrame.SM_FakeWindowsFrame",
"default_size": None,
"actual_bounds": ((-70, 0, 96), (70, 14, 260)),
"pivot_offset_xy": (0, 7),
"category": "window",
},
# ---- 建筑 — 围栏/围挡 ----
# 实测: min=(0, -30, 0), max=(600, 30, 210) — 实际60cm厚, 210cm高!
"BarrierWall_600": {
"path": f"{MESH_ROOT}/Architecture/Barriers/SM_BarrierWall_600.SM_BarrierWall_600",
"default_size": (600, 20, 100),
"default_size": (600, 60, 210),
"actual_bounds": ((0, -30, 0), (600, 30, 210)),
"pivot_offset_xy": (300, 0),
"category": "barrier",
},
"BarrierWall_200": {
"path": f"{MESH_ROOT}/Architecture/Barriers/SM_BarrierWall_200.SM_BarrierWall_200",
"default_size": (200, 20, 100),
"default_size": (200, 60, 210),
"actual_bounds": ((0, -30, 0), (200, 30, 210)),
"pivot_offset_xy": (100, 0),
"category": "barrier",
},
"Fence_Small_400": {
"path": f"{MESH_ROOT}/Industrials/SM_Fence_Small_400.SM_Fence_Small_400",
"default_size": (400, 5, 100),
"actual_bounds": ((0, 0, 0), (404, 6, 112)),
"pivot_offset_xy": (200, 3),
"category": "fence",
},
# ---- 建筑 — 横梁 ----
"Beam_300": {
"path": f"{MESH_ROOT}/Architecture/Beams/SM_Beam_300.SM_Beam_300",
"default_size": None,
"actual_bounds": ((-20, -20, 0), (20, 20, 300)),
"pivot_offset_xy": (0, 0), # X/Y centered
"category": "beam",
},
# ---- 建筑 — 大型建筑 ----
"Building_01": {
"path": f"{MESH_ROOT}/BigBuildings/SM_Building_01.SM_Building_01",
"default_size": None,
"actual_bounds": ((-925, -725, 0), (1025, 725, 4010)),
"pivot_offset_xy": (50, 0), # nearly centered, slight X offset
"category": "building",
},
# ---- 家具 ----
# 所有家具锚点在底面中心,无需补偿
"SimpleTable_01": {
"path": f"{MESH_ROOT}/Furnitures/SM_SimpleTable_01.SM_SimpleTable_01",
"default_size": None,
"actual_bounds": ((-80.6, -43.8, 0), (80.6, 43.8, 74)),
"pivot_offset_xy": (0, 0),
"category": "furniture",
},
"SimpleTable_02": {
"path": f"{MESH_ROOT}/Furnitures/SM_SimpleTable_02.SM_SimpleTable_02",
"default_size": None,
"actual_bounds": ((-119.6, -49.8, 0), (119.6, 49.8, 74)),
"pivot_offset_xy": (0, 0),
"category": "furniture",
},
"BedSingle": {
"path": f"{MESH_ROOT}/Bedroom/SM_BedSingle.SM_BedSingle",
"default_size": None,
"actual_bounds": ((-57.2, -1.2, 0), (59, 214.8, 68)),
"pivot_offset_xy": (1, 107), # nearly corner in Y
"category": "furniture",
},
"Monitor_01": {
"path": f"{MESH_ROOT}/Furnitures/SM_Monitor_01.SM_Monitor_01",
"default_size": None,
"actual_bounds": ((-27.4, -14.3, 0), (27.4, 4.5, 37.9)),
"pivot_offset_xy": (0, 5), # slight Y offset
"category": "furniture",
},
# ---- 工业/装饰 ----
"Barrel": {
"path": f"{MESH_ROOT}/Industrials/SM_Barrel.SM_Barrel",
"default_size": None,
"actual_bounds": ((-36.6, -36.6, 0), (36.6, 36.6, 111.8)),
"pivot_offset_xy": (0, 0),
"category": "industrial",
},
"WoodenBox_Small": {
"path": f"{MESH_ROOT}/Industrials/SM_WoodenBox_Small.SM_WoodenBox_Small",
"default_size": None,
"actual_bounds": ((-35, -35, 0), (35, 35, 70)),
"pivot_offset_xy": (0, 0),
"category": "industrial",
},
"WoodenBox_Normal": {
"path": f"{MESH_ROOT}/Industrials/SM_WoodenBox_Normal.SM_WoodenBox_Normal",
"default_size": None,
"actual_bounds": ((-45, -45, 0), (45, 45, 90)),
"pivot_offset_xy": (0, 0),
"category": "industrial",
},
"WoodenPallet_01": {
"path": f"{MESH_ROOT}/Industrials/SM_WoodenPallet_01.SM_WoodenPallet_01",
"default_size": None,
"actual_bounds": ((-94.2, -94.7, 0), (93.9, 94.1, 20)),
"pivot_offset_xy": (0, 0),
"category": "industrial",
},
"Bucket_01": {
"path": f"{MESH_ROOT}/Decorations/SM_Bucket_01.SM_Bucket_01",
"default_size": None,
"actual_bounds": ((-15.3, -13.9, 0), (15.3, 13.9, 26.5)),
"pivot_offset_xy": (0, 0),
"category": "industrial",
},
"TrashCans_01": {
"path": f"{MESH_ROOT}/Trashs/SM_TrashCans_01.SM_TrashCans_01",
"default_size": None,
"actual_bounds": ((-26.8, -41.3, 0), (26.8, 30.1, 118.1)),
"pivot_offset_xy": (0, 6), # slight Y offset
"category": "industrial",
},
"Container_Normal": {
"path": f"{MESH_ROOT}/Industrials/SM_Container_Normal.SM_Container_Normal",
"default_size": None,
"actual_bounds": ((-122.4, -292.7, 0), (122.4, 292.7, 260)),
"pivot_offset_xy": (0, 0),
"category": "industrial",
},
# ---- 载具 ----
"Car01": {
"path": f"{MESH_ROOT}/Vehicles/SM_Car01.SM_Car01",
"default_size": None,
"actual_bounds": ((-80, -220, 0), (80, 200, 180)),
"pivot_offset_xy": (0, 10), # slight Y offset
"category": "vehicle",
},
# ---- 基础体 ----
# Cube: 角落锚点! min=(0,0,0), max=(100,100,100)
"Cube": {
"path": f"{MESH_ROOT}/Primitives/SM_Cube.SM_Cube",
"default_size": (100, 100, 100),
"actual_bounds": ((0, 0, 0), (100, 100, 100)),
"pivot_offset_xy": (50, 50),
"category": "primitive",
},
# Cylinder: 底面中心,无需补偿
"Cylinder": {
"path": f"{MESH_ROOT}/Primitives/SM_Cylinder.SM_Cylinder",
"default_size": (100, 100, 100),
"actual_bounds": ((-50, -50, 0), (50, 50, 100)),
"pivot_offset_xy": (0, 0),
"category": "primitive",
},
"CirclePlatform": {
"path": f"{MESH_ROOT}/Extra/SM_CirclePlatform.SM_CirclePlatform",
"default_size": None,
"actual_bounds": ((-48, -48, 0), (48, 48, 4)),
"pivot_offset_xy": (0, 0),
"category": "primitive",
},
# ---- 其他 ----
"Tree_01": {
"path": f"{MESH_ROOT}/Vegetations/SM_Tree_01.SM_Tree_01",
"default_size": None,
"actual_bounds": ((-352, -349, 0), (410, 454, 845)),
"pivot_offset_xy": (29, 53), # slightly off-center
"category": "vegetation",
},
"RoadLamp_01": {
"path": f"{MESH_ROOT}/Lights/SM_RoadLamp_01.SM_RoadLamp_01",
"default_size": None,
"actual_bounds": ((-20, -14.2, 0), (20, 230, 850)),
"pivot_offset_xy": (0, 108), # off-center in Y
"category": "light_fixture",
},
"DisplayPanels_400x200": {
"path": f"{MESH_ROOT}/DisplayPanels/SM_DisplayPanels_400x200.SM_DisplayPanels_400x200",
"default_size": None,
"actual_bounds": ((-200, 0, -100), (200, 8, 100)),
"pivot_offset_xy": (0, 4),
"category": "decoration",
},
}
def get_pivot_compensation(asset_key: str, scale: list, rotation: list) -> tuple:
"""计算锚点偏移补偿。
搭建指南位置 = 视觉中心。实际 spawn 位置 = 视觉中心 - offset(缩放后旋转后)。
Args:
asset_key: 资产映射 key
scale: [sx, sy, sz] 缩放
rotation: [pitch, yaw, roll] 度数
Returns:
(dx, dy) 需要从位置中减去的 XY 偏移
"""
info = MESH_ASSETS.get(asset_key)
if not info:
return (0.0, 0.0)
offset = info.get("pivot_offset_xy", (0, 0))
if offset == (0, 0):
return (0.0, 0.0)
# 缩放偏移
ox = offset[0] * scale[0]
oy = offset[1] * scale[1]
# 应用 Yaw 旋转(绕 Z 轴)
yaw_rad = math.radians(rotation[1]) # rotation[1] = Yaw
cos_y = math.cos(yaw_rad)
sin_y = math.sin(yaw_rad)
rx = ox * cos_y - oy * sin_y
ry = ox * sin_y + oy * cos_y
return (rx, ry)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+116
View File
@@ -0,0 +1,116 @@
BSP 资产锚点检测结果
============================================================
Wall_400x300:
min=(-0.0, -10.0, -0.0) max=(400.0, 10.0, 300.0) size=(400, 20, 300)
default_size=(400, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-200)
Wall_200x300:
min=(-0.0, -10.0, -0.0) max=(200.0, 10.0, 300.0) size=(200, 20, 300)
default_size=(200, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-100)
Wall_100x300:
min=(-0.0, -10.0, -0.0) max=(100.0, 10.0, 300.0) size=(100, 20, 300)
default_size=(100, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-50)
Wall_400x100:
min=(-0.0, -10.0, -0.0) max=(400.0, 10.0, 100.0) size=(400, 20, 100)
default_size=(400, 20, 100) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-200)
WallDoor_200x300:
min=(-0.0, -10.0, -0.0) max=(200.0, 10.0, 300.0) size=(200, 20, 300)
default_size=(200, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-100)
WallDoorGarage_400x300:
min=(-0.0, -10.0, -0.0) max=(400.0, 10.0, 300.0) size=(400, 20, 300)
default_size=(400, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-200)
WallDoorDouble_400x300:
min=(-0.0, -10.0, -0.0) max=(400.0, 10.0, 300.0) size=(400, 20, 300)
default_size=(400, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-200)
WallWindow_400x300:
min=(-0.0, -10.0, -0.0) max=(400.0, 10.0, 300.0) size=(400, 20, 300)
default_size=(400, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-200)
WallWindowSmall_200x300:
min=(-0.0, -10.0, -0.0) max=(200.0, 10.0, 300.0) size=(200, 20, 300)
default_size=(200, 20, 300) → pivot=OTHER (min=(-0,-10,-0), expected corner=0/center=-100)
Floor_1000x1000:
min=(-0.0, -0.0, -20.0) max=(1000.0, 1000.0, 2.0) size=(1000, 1000, 22)
default_size=(1000, 1000, 10) → pivot=OTHER (min=(-0,-0,-20), expected corner=0/center=-500)
Floor_300x400:
min=(-0.0, -0.0, -20.0) max=(300.0, 400.0, 2.0) size=(300, 400, 22)
default_size=(300, 400, 10) → pivot=OTHER (min=(-0,-0,-20), expected corner=0/center=-150)
Floor_200x300:
min=(-0.0, -0.0, -20.0) max=(200.0, 300.0, 2.0) size=(200, 300, 22)
default_size=(200, 300, 10) → pivot=OTHER (min=(-0,-0,-20), expected corner=0/center=-100)
RotativeDoor_01:
min=(-100.0, -12.0, -0.0) max=(0.0, 12.0, 216.0) size=(100, 24, 216)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
GarageDoorOpen_01:
min=(-150.0, -224.5, 220.0) max=(150.0, 5.9, 296.4) size=(300, 230, 76)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
FakeWindowsFrame:
min=(-70.0, 0.0, 96.0) max=(70.0, 14.0, 260.0) size=(140, 14, 164)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
BarrierWall_600:
min=(0.0, -30.0, 0.0) max=(600.0, 30.0, 210.0) size=(600, 60, 210)
default_size=(600, 20, 100) → pivot=OTHER (min=(0,-30,0), expected corner=0/center=-300)
BarrierWall_200:
min=(0.0, -30.0, 0.0) max=(200.0, 30.0, 210.0) size=(200, 60, 210)
default_size=(200, 20, 100) → pivot=OTHER (min=(0,-30,0), expected corner=0/center=-100)
Fence_Small_400:
min=(-3.6, -3.0, -0.0) max=(403.6, 3.0, 111.6) size=(407, 6, 112)
default_size=(400, 5, 100) → pivot=CORNER
Beam_300:
min=(-20.0, -20.0, 0.0) max=(20.0, 20.0, 300.0) size=(40, 40, 300)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Building_01:
min=(-925.0, -725.0, -0.0) max=(1025.0, 725.0, 4009.7) size=(1950, 1450, 4010)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
SimpleTable_01:
min=(-80.6, -43.8, 0.0) max=(80.6, 43.8, 74.0) size=(161, 88, 74)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
SimpleTable_02:
min=(-119.6, -49.8, 0.0) max=(119.6, 49.8, 74.0) size=(239, 100, 74)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
BedSingle:
min=(-57.2, -1.2, 0.0) max=(59.0, 214.8, 68.0) size=(116, 216, 68)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Monitor_01:
min=(-27.4, -14.3, -0.0) max=(27.4, 4.5, 37.9) size=(55, 19, 38)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Barrel:
min=(-36.6, -36.6, -0.0) max=(36.6, 36.6, 111.8) size=(73, 73, 112)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
WoodenBox_Small:
min=(-35.0, -35.0, -0.0) max=(35.0, 35.0, 70.0) size=(70, 70, 70)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
WoodenBox_Normal:
min=(-45.0, -45.0, -0.0) max=(45.0, 45.0, 90.0) size=(90, 90, 90)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
WoodenPallet_01:
min=(-94.2, -94.7, -0.0) max=(93.9, 94.1, 20.0) size=(188, 189, 20)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Bucket_01:
min=(-15.3, -13.9, -0.0) max=(15.3, 13.9, 26.5) size=(31, 28, 26)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
TrashCans_01:
min=(-26.8, -41.3, -0.0) max=(26.8, 30.1, 118.1) size=(54, 71, 118)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Container_Normal:
min=(-122.4, -292.7, -0.0) max=(122.4, 292.7, 260.0) size=(245, 585, 260)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Car01:
min=(-80.0, -220.0, 0.0) max=(80.0, 200.0, 180.0) size=(160, 420, 180)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Cube:
min=(0.0, -0.0, 0.0) max=(100.0, 100.0, 100.0) size=(100, 100, 100)
default_size=(100, 100, 100) → pivot=CORNER
Cylinder:
min=(-50.0, -50.0, -0.0) max=(50.0, 50.0, 100.0) size=(100, 100, 100)
default_size=(100, 100, 100) → pivot=CENTER_BOTTOM
CirclePlatform:
min=(-48.1, -48.1, -0.0) max=(48.1, 48.1, 4.1) size=(96, 96, 4)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
Tree_01:
min=(-352.3, -349.3, -0.0) max=(409.7, 454.0, 844.8) size=(762, 803, 845)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
RoadLamp_01:
min=(-20.0, -14.2, -0.0) max=(20.0, 230.0, 850.0) size=(40, 244, 850)
default_size=None → pivot=CENTER_BOTTOM? (no default_size, inferred)
DisplayPanels_400x200:
min=(-200.0, -0.0, -100.0) max=(200.0, 8.0, 100.0) size=(400, 8, 200)
default_size=None → pivot=CENTER? (no default_size, inferred)
@@ -1,7 +1,7 @@
{
"is_valid": true,
"total": 265,
"valid": 265,
"total": 238,
"valid": 238,
"errors": [],
"warnings": [],
"missing_assets": [],
+429
View File
@@ -0,0 +1,429 @@
# 白模搭建报告 — 自动生成
> 生成时间: 2026-06-08 17:52
> 数据来源: 关卡-v01-搭建指南.md
> 场景尺寸: 8000cm × 6000cm
## 一、搭建概要
- **总 Actor 数**: 238
- 结构体 (STR): 95
- 布景 (PROP): 53
- 交互物 (INTR): 10
- 收集品 (COLL): 9
- 边界 (BND): 62
- 光源 (LGT): 8
- PlayerStart (PLAY): 1
- 构建成功: 238
- 构建失败: 0
## 二、逐区域对照
### Z1 家
共 39 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z1_STR_SouthWall_01 | Wall_400x300 | (200,0,0) | — | MI_DevRed | ✅ |
| 2 | LVL_Z1_STR_SouthWall_02 | Wall_400x300 | (600,0,0) | — | MI_DevRed | ✅ |
| 3 | LVL_Z1_STR_WestWall_01 | Wall_400x300 | (0,200,0) | — | MI_DevRed | ✅ |
| 4 | LVL_Z1_STR_WestWall_02 | Wall_400x300 | (0,600,0) | — | MI_DevRed | ✅ |
| 5 | LVL_Z1_STR_EastWall_01 | Wall_400x300 | (980,200,0) | — | MI_DevRed | ✅ |
| 6 | LVL_Z1_STR_EastWall_02 | Wall_400x300 | (980,600,0) | — | MI_DevRed | ✅ |
| 7 | LVL_Z1_STR_NorthWall_West | Wall_400x300 | (200,780,0) | — | MI_DevRed | ✅ |
| 8 | LVL_Z1_STR_NorthWall_Gate | WallDoorDouble_400x300 | (500,780,0) | — | MI_DevRed | ✅ |
| 9 | LVL_Z1_STR_NorthWall_East | Wall_400x300 | (800,780,0) | — | MI_DevRed | ✅ |
| 10 | LVL_Z1_STR_GateDoor | RotativeDoor_01 | (500,780,0) | — | MI_DevGreen | ✅ |
| 11 | LVL_Z1_STR_HouseSouthWall | WallWindow_400x300 | (400,30,0) | — | MI_DevWhite | ✅ |
| 12 | LVL_Z1_STR_HouseSouthWin | WallWindowSmall_200x300 | (600,30,0) | — | MI_DevWhite | ✅ |
| 13 | LVL_Z1_STR_HouseWestWall | Wall_400x300 | (200,220,0) | — | MI_DevWhite | ✅ |
| 14 | LVL_Z1_STR_HouseWestTop | Wall_400x100 | (200,220,300) | — | MI_DevWhite | ✅ |
| 15 | LVL_Z1_STR_HouseEastWall | Wall_400x300 | (780,220,0) | — | MI_DevWhite | ✅ |
| 16 | LVL_Z1_STR_HouseEastTop | Wall_400x100 | (780,220,300) | — | MI_DevWhite | ✅ |
| 17 | LVL_Z1_STR_HouseNorthWall | WallDoor_200x300 | (400,410,0) | — | MI_DevWhite | ✅ |
| 18 | LVL_Z1_STR_HouseNorthFill | Wall_400x300 | (600,410,0) | — | MI_DevWhite | ✅ |
| 19 | LVL_Z1_STR_HouseRoof_01 | Floor_300x400 | (350,230,280) | — | MI_DevBrown | ✅ |
| 20 | LVL_Z1_STR_HouseRoof_02 | Floor_300x400 | (650,230,280) | — | MI_DevBrown | ✅ |
| 21 | LVL_Z1_STR_CourtyardFloor | Floor_1000x1000 | (500,400,0) | (1.00,0.80,1.00) | MI_DevGray | ✅ |
| 22 | LVL_Z1_PROP_OldBicycle | Car01 | (100,650,0) | (0.20,0.20,0.20) | — | ✅ |
| 23 | LVL_Z1_PROP_ClothesPole_Beam | Beam_300 | (500,600,180) | — | MI_DevWhite | ✅ |
| 24 | LVL_Z1_PROP_ClothesPole_Pole1 | Cylinder | (380,600,0) | (0.05,0.05,3.60) | MI_DevGray | ✅ |
| 25 | LVL_Z1_PROP_ClothesPole_Pole2 | Cylinder | (620,600,0) | (0.05,0.05,3.60) | MI_DevGray | ✅ |
| 26 | LVL_Z1_PROP_JunkPile_Box1 | WoodenBox_Small | (850,100,0) | — | — | ✅ |
| 27 | LVL_Z1_PROP_JunkPile_Box2 | WoodenBox_Small | (870,120,35) | — | — | ✅ |
| 28 | LVL_Z1_PROP_JunkPile_Bucket | Bucket_01 | (890,80,0) | — | — | ✅ |
| 29 | LVL_Z1_PROP_DoorMat | Cube | (480,740,0) | (0.60,0.40,0.02) | MI_DevGray | ✅ |
| 30 | LVL_Z1_PROP_Fridge | Barrel | (720,80,0) | (0.30,0.30,1.60) | MI_DevWhite | ✅ |
| 31 | LVL_Z1_PROP_TV | Monitor_01 | (320,80,45) | — | MI_DevGray | ✅ |
| 32 | LVL_Z1_PROP_Desk | SimpleTable_01 | (300,70,0) | — | — | ✅ |
| 33 | LVL_Z1_PROP_Bed | BedSingle | (350,250,0) | — | — | ✅ |
| 34 | LVL_Z1_PROP_Calendar | Cube | (500,40,180) | (0.30,0.02,0.40) | MI_DevRed | ✅ |
| 35 | LVL_Z1_PROP_TVStand | SimpleTable_02 | (320,55,0) | (0.85,0.75,0.67) | — | ✅ |
| 36 | LVL_Z1_INTR_01_Fridge | Cube | (720,80,80) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 37 | LVL_Z1_INTR_02_Gate | Cube | (500,780,0) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 38 | LVL_Z1_INTR_06_TV | Cube | (320,80,70) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 39 | LVL_Z1_COLL_10_CatEyeMarble | CirclePlatform | (870,140,0) | (0.03,0.03,0.03) | MI_DevYellow | ✅ |
### Z2 小卖部
共 26 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z2_STR_Floor | Floor_1000x1000 | (1300,500,0) | (0.60,0.60,1.00) | MI_DevGray | ✅ |
| 2 | LVL_Z2_STR_SouthWall | Wall_400x300 | (1200,200,0) | — | MI_DevOrange | ✅ |
| 3 | LVL_Z2_STR_SouthWall2 | Wall_200x300 | (1500,200,0) | — | MI_DevOrange | ✅ |
| 4 | LVL_Z2_STR_WestWall | Wall_400x300 | (1000,400,0) | — | MI_DevOrange | ✅ |
| 5 | LVL_Z2_STR_WestWallTop | Wall_400x100 | (1000,400,300) | — | MI_DevOrange | ✅ |
| 6 | LVL_Z2_STR_EastWall | Wall_400x300 | (1580,400,0) | — | MI_DevOrange | ✅ |
| 7 | LVL_Z2_STR_EastWallTop | Wall_400x100 | (1580,400,300) | — | MI_DevOrange | ✅ |
| 8 | LVL_Z2_STR_NorthWall_West | Wall_200x300 | (1100,580,0) | — | MI_DevOrange | ✅ |
| 9 | LVL_Z2_STR_NorthWall_Garage | WallDoorGarage_400x300 | (1300,580,0) | — | MI_DevOrange | ✅ |
| 10 | LVL_Z2_STR_NorthWall_East | Wall_200x300 | (1500,580,0) | — | MI_DevOrange | ✅ |
| 11 | LVL_Z2_STR_GarageDoor | GarageDoorOpen_01 | (1300,580,0) | — | MI_DevGray | ✅ |
| 12 | LVL_Z2_STR_Roof_01 | Floor_300x400 | (1250,400,280) | — | MI_DevBrown | ✅ |
| 13 | LVL_Z2_STR_Roof_02 | Floor_300x400 | (1350,400,280) | — | MI_DevBrown | ✅ |
| 14 | LVL_Z2_STR_Awning_01 | Floor_200x300 | (1100,700,260) | — | MI_DevWhite | ✅ |
| 15 | LVL_Z2_STR_Awning_02 | Floor_200x300 | (1400,700,260) | — | MI_DevWhite | ✅ |
| 16 | LVL_Z2_PROP_Counter | Cube | (1200,500,0) | (2.00,0.60,0.90) | MI_DevGlassWhite | ✅ |
| 17 | LVL_Z2_PROP_ShelfEast_01 | Fence_Small_400 | (1520,300,0) | (1.00,1.00,1.80) | MI_DevGray | ✅ |
| 18 | LVL_Z2_PROP_ShelfEast_02 | Fence_Small_400 | (1520,300,180) | (1.00,1.00,1.80) | MI_DevGray | ✅ |
| 19 | LVL_Z2_PROP_ShelfSouth | Fence_Small_400 | (1100,230,0) | (1.00,1.00,1.80) | MI_DevGray | ✅ |
| 20 | LVL_Z2_PROP_Freezer | Container_Normal | (1050,400,0) | (0.25,0.15,0.40) | MI_DevWhite | ✅ |
| 21 | LVL_Z2_PROP_SmallTV | Monitor_01 | (1350,540,90) | (0.60,0.60,0.60) | MI_DevGray | ✅ |
| 22 | LVL_Z2_PROP_Signboard | Cube | (1150,610,280) | (3.00,0.05,0.40) | MI_DevBlue | ✅ |
| 23 | LVL_Z2_PROP_CardRack | Cube | (1400,550,0) | (0.40,0.20,0.30) | MI_DevGray | ✅ |
| 24 | LVL_Z2_INTR_05_ShopOwner | Cube | (1300,480,0) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 25 | LVL_Z2_INTR_07_SnackShelf | Cube | (1520,350,90) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 26 | LVL_Z2_COLL_09_BouncyBall | CirclePlatform | (1300,700,0) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
### Z3 主街道
共 42 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z3_STR_Floor_01 | Floor_1000x1000 | (500,1050,0) | (1.00,0.50,1.00) | MI_DevGray | ✅ |
| 2 | LVL_Z3_STR_Floor_02 | Floor_1000x1000 | (1500,1050,0) | (1.00,0.50,1.00) | MI_DevGray | ✅ |
| 3 | LVL_Z3_STR_Floor_03 | Floor_1000x1000 | (2500,1050,0) | (1.00,0.50,1.00) | MI_DevGray | ✅ |
| 4 | LVL_Z3_STR_Floor_04 | Floor_1000x1000 | (3500,1050,0) | (1.00,0.50,1.00) | MI_DevGray | ✅ |
| 5 | LVL_Z3_STR_Floor_05 | Floor_1000x1000 | (4500,1050,0) | (1.00,0.50,1.00) | MI_DevGray | ✅ |
| 6 | LVL_Z3_STR_SouthWall_C1 | Wall_400x300 | (1800,800,0) | — | MI_DevGray | ✅ |
| 7 | LVL_Z3_STR_SouthWall_C2 | Wall_400x300 | (2200,800,0) | — | MI_DevGray | ✅ |
| 8 | LVL_Z3_STR_SouthWall_C3 | Wall_400x300 | (2600,800,0) | — | MI_DevGray | ✅ |
| 9 | LVL_Z3_STR_SouthWall_C4 | Wall_400x300 | (3000,800,0) | — | MI_DevGray | ✅ |
| 10 | LVL_Z3_STR_SouthWall_C5 | Wall_400x300 | (3400,800,0) | — | MI_DevGray | ✅ |
| 11 | LVL_Z3_STR_SouthWall_C6 | Wall_400x300 | (3800,800,0) | — | MI_DevGray | ✅ |
| 12 | LVL_Z3_STR_SouthWall_D1 | Wall_400x300 | (4200,800,0) | — | MI_DevGray | ✅ |
| 13 | LVL_Z3_STR_SouthWall_D2 | Wall_400x300 | (4600,800,0) | — | MI_DevGray | ✅ |
| 14 | LVL_Z3_STR_NorthWall_A1 | Wall_400x300 | (200,1300,0) | — | MI_DevGray | ✅ |
| 15 | LVL_Z3_STR_NorthWall_A2 | Wall_400x300 | (600,1300,0) | — | MI_DevGray | ✅ |
| 16 | LVL_Z3_STR_NorthWall_A3 | Wall_400x300 | (1000,1300,0) | — | MI_DevGray | ✅ |
| 17 | LVL_Z3_STR_NorthWall_A4 | Wall_400x300 | (1400,1300,0) | — | MI_DevGray | ✅ |
| 18 | LVL_Z3_STR_NorthWall_A5 | Wall_400x300 | (1800,1300,0) | — | MI_DevGray | ✅ |
| 19 | LVL_Z3_STR_NorthWall_A6 | Wall_200x300 | (2100,1300,0) | — | MI_DevGray | ✅ |
| 20 | LVL_Z3_STR_NorthWall_C1 | Wall_400x300 | (2800,1300,0) | — | MI_DevGray | ✅ |
| 21 | LVL_Z3_STR_NorthWall_C2 | Wall_400x300 | (3200,1300,0) | — | MI_DevGray | ✅ |
| 22 | LVL_Z3_STR_NorthWall_C3 | Wall_100x300 | (3450,1300,0) | — | MI_DevGray | ✅ |
| 23 | LVL_Z3_PROP_PhoneBooth | Cube | (1800,380,0) | (0.80,0.80,2.20) | MI_DevGray | ✅ |
| 24 | LVL_Z3_PROP_Bicycle | Car01 | (2800,880,0) | (0.20,0.20,0.20) | — | ✅ |
| 25 | LVL_Z3_PROP_ClothesPole | Beam_300 | (1200,1250,180) | — | MI_DevWhite | ✅ |
| 26 | LVL_Z3_PROP_ClothesPolePole | Cylinder | (1200,1250,0) | (0.05,0.05,3.60) | MI_DevGray | ✅ |
| 27 | LVL_Z3_PROP_SewerCover | CirclePlatform | (800,1050,0) | (0.12,0.12,0.02) | MI_DevGray | ✅ |
| 28 | LVL_Z3_PROP_Graffiti | Cube | (3200,1270,100) | (1.20,0.05,0.80) | MI_DevYellow | ✅ |
| 29 | LVL_Z3 PROP_AdWall | DisplayPanels_400x200 | (3800,1280,0) | — | MI_DevGray | ✅ |
| 30 | LVL_Z3_PROP_TrashCan | TrashCans_01 | (4200,850,0) | — | MI_DevGreen | ✅ |
| 31 | LVL_Z3_PROP_PoleWest | Cylinder | (1000,1050,0) | (0.04,0.04,12.00) | MI_DevGray | ✅ |
| 32 | LVL_Z3_PROP_PoleEast | Cylinder | (3500,1050,0) | (0.04,0.04,12.00) | MI_DevGray | ✅ |
| 33 | LVL_Z3_INTR_04_PhoneBooth | Cube | (1800,380,110) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 34 | LVL_Z3_INTR_09_Graffiti | Cube | (3200,1270,140) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 35 | LVL_Z3_COLL_08_IceFlowerMarble | CirclePlatform | (820,1060,0) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
| 36 | LVL_Z3_LGT_StreetLight_01 | RoadLamp_01 | (1500,1050,0) | — | — | ✅ |
| 37 | LVL_Z3_LGT_StreetLight_02 | RoadLamp_01 | (3000,1050,0) | — | — | ✅ |
| 38 | LVL_Z3_LGT_StreetLight_03 | RoadLamp_01 | (4500,1050,0) | — | — | ✅ |
| 39 | LVL_Z3_PROP_TrashCan_02 | TrashCans_01 | (2000,850,0) | — | MI_DevGreen | ✅ |
| 40 | LVL_Z3_LGT_PointLight_01 | | (1500,1050,400) | — | — | ✅ |
| 41 | LVL_Z3_LGT_PointLight_02 | | (3000,1050,400) | — | — | ✅ |
| 42 | LVL_Z3_LGT_PointLight_03 | | (4500,1050,400) | — | — | ✅ |
### Z4 空地
共 20 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z4_STR_Floor_01 | Floor_1000x1000 | (4000,1800,10) | (1.50,1.20,1.00) | MI_DevBrown | ✅ |
| 2 | LVL_Z4_STR_EastBound_01 | BarrierWall_600 | (4980,1600,0) | — | MI_DevBlue | ✅ |
| 3 | LVL_Z4_STR_EastBound_02 | BarrierWall_600 | (4980,2200,0) | — | MI_DevBlue | ✅ |
| 4 | LVL_Z4_STR_NorthBound_01 | BarrierWall_600 | (3800,2480,0) | — | MI_DevBlue | ✅ |
| 5 | LVL_Z4_STR_NorthBound_02 | BarrierWall_600 | (4400,2480,0) | — | MI_DevBlue | ✅ |
| 6 | LVL_Z4_STR_NorthBound_03 | BarrierWall_200 | (4700,2480,0) | — | MI_DevBlue | ✅ |
| 7 | LVL_Z4_STR_WestLowWall_01 | BarrierWall_200 | (3500,1500,0) | — | MI_DevGray | ✅ |
| 8 | LVL_Z4_STR_WestLowWall_02 | BarrierWall_200 | (3500,1900,0) | — | MI_DevGray | ✅ |
| 9 | LVL_Z4_STR_WestLowWall_03 | BarrierWall_200 | (3500,2300,0) | — | MI_DevGray | ✅ |
| 10 | LVL_Z4_PROP_BigTree | Tree_01 | (4250,1900,10) | (1.20,1.20,1.20) | MI_DevFoliage | ✅ |
| 11 | LVL_Z4_PROP_Bricks_01~05 | WoodenBox_Small | (0,0,0) | — | — | ✅ |
| 12 | LVL_Z4_PROP_Puddle | Cube | (4600,1500,5) | (1.20,0.80,0.01) | MI_DevGlassBlue | ✅ |
| 13 | LVL_Z4_PROP_MarbleCircle | CirclePlatform | (4100,1800,12) | (0.16,0.16,0.01) | MI_DevGray | ✅ |
| 14 | LVL_Z4_PROP_Debris_01 | WoodenPallet_01 | (4800,2300,10) | — | — | ✅ |
| 15 | LVL_Z4_PROP_Debris_02 | WoodenBox_Normal | (4850,2250,10) | — | — | ✅ |
| 16 | LVL_Z4_INTR_03_BlueMarble | Cube | (3900,1700,10) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 17 | LVL_Z4_INTR_08_SmallStone | Cube | (4550,1550,10) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 18 | LVL_Z4_COLL_01_BlueMarble | CirclePlatform | (3900,1700,10) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
| 19 | LVL_Z4_COLL_03_SmallStone | CirclePlatform | (4550,1550,10) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
| 20 | LVL_Z4_COLL_04_LuZhishenCard | CirclePlatform | (3720,1610,10) | (0.03,0.03,0.03) | MI_DevYellow | ✅ |
### Z5 巷子
共 19 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z5_STR_Floor_01 | Floor_1000x1000 | (2450,1800,0) | (0.30,1.00,1.00) | MI_DevGray | ✅ |
| 2 | LVL_Z5_STR_Floor_02 | Floor_1000x1000 | (2450,2800,0) | (0.30,1.00,1.00) | MI_DevGray | ✅ |
| 3 | LVL_Z5_STR_WestWall_01 | Wall_400x300 | (2300,1500,0) | — | MI_DevWhite | ✅ |
| 4 | LVL_Z5_STR_WestWall_02 | Wall_400x300 | (2300,1900,0) | — | MI_DevWhite | ✅ |
| 5 | LVL_Z5_STR_WestWall_03 | Wall_400x300 | (2300,2300,0) | — | MI_DevWhite | ✅ |
| 6 | LVL_Z5_STR_WestWall_04 | Wall_400x300 | (2300,2700,0) | — | MI_DevWhite | ✅ |
| 7 | LVL_Z5_STR_WestWall_05 | Wall_400x300 | (2300,3100,0) | — | MI_DevWhite | ✅ |
| 8 | LVL_Z5_STR_EastWall_01 | Wall_400x300 | (2580,1500,0) | — | MI_DevWhite | ✅ |
| 9 | LVL_Z5_STR_EastWall_02 | Wall_400x300 | (2580,1900,0) | — | MI_DevWhite | ✅ |
| 10 | LVL_Z5_STR_EastWall_03 | Wall_400x300 | (2580,2300,0) | — | MI_DevWhite | ✅ |
| 11 | LVL_Z5_STR_EastWall_04 | Wall_400x300 | (2580,2700,0) | — | MI_DevWhite | ✅ |
| 12 | LVL_Z5_STR_EastWall_05 | Wall_400x300 | (2580,3100,0) | — | MI_DevWhite | ✅ |
| 13 | LVL_Z5_PROP_WasteBox | WoodenBox_Small | (2380,2900,0) | — | — | ✅ |
| 14 | LVL_Z5_PROP_SpiderWeb | Cube | (2305,2100,250) | (0.30,0.30,0.01) | MI_DevWhite | ✅ |
| 15 | LVL_Z5_PROP_OldWindow | FakeWindowsFrame | (2310,2100,150) | — | MI_DevGray | ✅ |
| 16 | LVL_Z5_PROP_FlowerPot | Cylinder | (2340,2100,150) | (0.20,0.20,0.25) | MI_DevGreen | ✅ |
| 17 | LVL_Z5_PROP_Moss | Cube | (2400,2700,0) | (0.60,0.40,0.01) | MI_DevFoliage | ✅ |
| 18 | LVL_Z5_INTR_10_WasteBox | Cube | (2380,2900,25) | (0.30,0.30,0.30) | MI_DevYellow | ✅ |
| 19 | LVL_Z5_COLL_02_RedMarble | CirclePlatform | (2500,3000,0) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
### Z6 水塘边
共 16 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z6_STR_GroundSouth | Floor_1000x1000 | (2350,3475,0) | (1.50,0.35,1.00) | MI_DevBrown | ✅ |
| 2 | LVL_Z6_STR_GroundEast | Floor_1000x1000 | (3075,4000,0) | (0.15,0.80,1.00) | MI_DevBrown | ✅ |
| 3 | LVL_Z6_STR_GroundWest | Floor_1000x1000 | (1675,4000,0) | (0.15,0.80,1.00) | MI_DevBrown | ✅ |
| 4 | LVL_Z6_STR_Water | Floor_1000x1000 | (2350,4000,-20) | (1.20,0.80,0.50) | MI_DevSkyBlue | ✅ |
| 5 | LVL_Z6_STR_EastBound_01 | BarrierWall_600 | (3080,3600,0) | — | MI_DevBlue | ✅ |
| 6 | LVL_Z6_STR_EastBound_02 | BarrierWall_600 | (3080,4200,0) | — | MI_DevBlue | ✅ |
| 7 | LVL_Z6_STR_NorthBound_01 | BarrierWall_600 | (1900,4680,0) | — | MI_DevBlue | ✅ |
| 8 | LVL_Z6_STR_NorthBound_02 | BarrierWall_600 | (2500,4680,0) | — | MI_DevBlue | ✅ |
| 9 | LVL_Z6_STR_NorthBound_03 | BarrierWall_200 | (2800,4680,0) | — | MI_DevBlue | ✅ |
| 10 | LVL_Z6_PROP_Reeds_S1~5 | Cylinder | (1800,3600,0) | (0.08,0.08,2.00) | MI_DevFoliage | ✅ |
| 11 | LVL_Z6_PROP_Reeds_S2_1~5 | Cylinder | (2700,3600,0) | (0.08,0.08,2.00) | MI_DevFoliage | ✅ |
| 12 | LVL_Z6_PROP_Reeds_E_1~3 | Cylinder | (2900,4100,0) | (0.08,0.08,1.60) | MI_DevFoliage | ✅ |
| 13 | LVL_Z6_PROP_LilyPad_1~4 | CirclePlatform | (2200,3900,-18) | (0.06,0.06,0.01) | MI_DevGreen | ✅ |
| 14 | LVL_Z6_PROP_Frog | Cube | (2100,3680,0) | (0.05,0.05,0.04) | MI_DevGreen | ✅ |
| 15 | LVL_Z6_COLL_05_CandyWrapper | CirclePlatform | (2000,3500,0) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
| 16 | LVL_Z6_COLL_06_Eraser | CirclePlatform | (2500,3450,0) | (0.03,0.03,0.03) | MI_DevWhite | ✅ |
### Z7 老居民楼
共 10 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Z7_STR_Building | Building_01 | (6400,800,0) | — | MI_DevGray | ✅ |
| 2 | LVL_Z7_STR_CollisionFront | Wall_400x300 | (6200,150,0) | — | MI_DevGray | ✅ |
| 3 | LVL_Z7_STR_CollisionFront2 | Wall_400x300 | (6600,150,0) | — | MI_DevGray | ✅ |
| 4 | LVL_Z7_STR_CollisionFront3 | Wall_400x300 | (7000,150,0) | — | MI_DevGray | ✅ |
| 5 | LVL_Z7_STR_CollisionSide | Wall_400x300 | (5750,400,0) | — | MI_DevGray | ✅ |
| 6 | LVL_Z7_STR_CollisionSide2 | Wall_400x300 | (5750,800,0) | — | MI_DevGray | ✅ |
| 7 | LVL_Z7_PROP_ClothesPole_01 | Beam_300 | (6100,190,800) | — | MI_DevWhite | ✅ |
| 8 | LVL_Z7_PROP_ClothesPole_02 | Beam_300 | (6500,190,1200) | — | MI_DevWhite | ✅ |
| 9 | LVL_Z7_PROP_ClothesPole_03 | Beam_300 | (6900,190,1600) | — | MI_DevWhite | ✅ |
| 10 | LVL_Z7_PROP_TrashCan | TrashCans_01 | (6000,140,0) | — | MI_DevGreen | ✅ |
### GLOBAL 全局
共 66 个 Actor
| # | Actor 名称 | 资产 | 位置 | 缩放 | 材质 | 状态 |
|---|-----------|------|------|------|------|------|
| 1 | LVL_Global_GroundFloor | Floor_1000x1000 | (4000,3000,-5) | (8.00,6.00,1.00) | MI_DevGray | ✅ |
| 2 | LVL_BND_West_01 | Wall_400x300 | (0,200,0) | — | MI_DevRed | ✅ |
| 3 | LVL_BND_West_02 | Wall_400x300 | (0,600,0) | — | MI_DevRed | ✅ |
| 4 | LVL_BND_West_03 | Wall_400x300 | (0,1000,0) | — | MI_DevRed | ✅ |
| 5 | LVL_BND_West_04 | Wall_400x300 | (0,1400,0) | — | MI_DevRed | ✅ |
| 6 | LVL_BND_West_05 | Wall_400x300 | (0,1800,0) | — | MI_DevRed | ✅ |
| 7 | LVL_BND_West_06 | Wall_400x300 | (0,2200,0) | — | MI_DevRed | ✅ |
| 8 | LVL_BND_West_07 | Wall_400x300 | (0,2600,0) | — | MI_DevRed | ✅ |
| 9 | LVL_BND_West_08 | Wall_400x300 | (0,3000,0) | — | MI_DevRed | ✅ |
| 10 | LVL_BND_West_09 | Wall_400x300 | (0,3400,0) | — | MI_DevRed | ✅ |
| 11 | LVL_BND_West_10 | Wall_400x300 | (0,3800,0) | — | MI_DevRed | ✅ |
| 12 | LVL_BND_West_11 | Wall_400x300 | (0,4200,0) | — | MI_DevRed | ✅ |
| 13 | LVL_BND_West_12 | Wall_400x300 | (0,4600,0) | — | MI_DevRed | ✅ |
| 14 | LVL_BND_West_13 | Wall_400x300 | (0,5000,0) | — | MI_DevRed | ✅ |
| 15 | LVL_BND_West_14 | Wall_400x300 | (0,5400,0) | — | MI_DevRed | ✅ |
| 16 | LVL_BND_West_15 | Wall_400x300 | (0,5800,0) | — | MI_DevRed | ✅ |
| 17 | LVL_BND_East_01 | Wall_400x300 | (8000,200,0) | — | MI_DevBlue | ✅ |
| 18 | LVL_BND_East_02 | Wall_400x300 | (8000,600,0) | — | MI_DevBlue | ✅ |
| 19 | LVL_BND_East_03 | Wall_400x300 | (8000,1000,0) | — | MI_DevBlue | ✅ |
| 20 | LVL_BND_East_04 | Wall_400x300 | (8000,1400,0) | — | MI_DevBlue | ✅ |
| 21 | LVL_BND_East_05 | Wall_400x300 | (8000,1800,0) | — | MI_DevBlue | ✅ |
| 22 | LVL_BND_East_06 | Wall_400x300 | (8000,2200,0) | — | MI_DevBlue | ✅ |
| 23 | LVL_BND_East_07 | Wall_400x300 | (8000,2600,0) | — | MI_DevBlue | ✅ |
| 24 | LVL_BND_East_08 | Wall_400x300 | (8000,3000,0) | — | MI_DevBlue | ✅ |
| 25 | LVL_BND_East_09 | Wall_400x300 | (8000,3400,0) | — | MI_DevBlue | ✅ |
| 26 | LVL_BND_East_10 | Wall_400x300 | (8000,3800,0) | — | MI_DevBlue | ✅ |
| 27 | LVL_BND_East_11 | Wall_400x300 | (8000,4200,0) | — | MI_DevBlue | ✅ |
| 28 | LVL_BND_East_12 | Wall_400x300 | (8000,4600,0) | — | MI_DevBlue | ✅ |
| 29 | LVL_BND_East_13 | Wall_400x300 | (8000,5000,0) | — | MI_DevBlue | ✅ |
| 30 | LVL_BND_East_14 | Wall_400x300 | (8000,5400,0) | — | MI_DevBlue | ✅ |
| 31 | LVL_BND_East_15 | Wall_400x300 | (8000,5800,0) | — | MI_DevBlue | ✅ |
| 32 | LVL_BND_North_01 | Wall_400x300 | (200,6000,0) | — | MI_DevBlue | ✅ |
| 33 | LVL_BND_North_02 | Wall_400x300 | (600,6000,0) | — | MI_DevBlue | ✅ |
| 34 | LVL_BND_North_03 | Wall_400x300 | (1000,6000,0) | — | MI_DevBlue | ✅ |
| 35 | LVL_BND_North_04 | Wall_400x300 | (1400,6000,0) | — | MI_DevBlue | ✅ |
| 36 | LVL_BND_North_05 | Wall_400x300 | (1800,6000,0) | — | MI_DevBlue | ✅ |
| 37 | LVL_BND_North_06 | Wall_400x300 | (2200,6000,0) | — | MI_DevBlue | ✅ |
| 38 | LVL_BND_North_07 | Wall_400x300 | (2600,6000,0) | — | MI_DevBlue | ✅ |
| 39 | LVL_BND_North_08 | Wall_400x300 | (3000,6000,0) | — | MI_DevBlue | ✅ |
| 40 | LVL_BND_North_09 | Wall_400x300 | (3400,6000,0) | — | MI_DevBlue | ✅ |
| 41 | LVL_BND_North_10 | Wall_400x300 | (3800,6000,0) | — | MI_DevBlue | ✅ |
| 42 | LVL_BND_North_11 | Wall_400x300 | (4200,6000,0) | — | MI_DevBlue | ✅ |
| 43 | LVL_BND_North_12 | Wall_400x300 | (4600,6000,0) | — | MI_DevBlue | ✅ |
| 44 | LVL_BND_North_13 | Wall_400x300 | (5000,6000,0) | — | MI_DevBlue | ✅ |
| 45 | LVL_BND_North_14 | Wall_400x300 | (5400,6000,0) | — | MI_DevBlue | ✅ |
| 46 | LVL_BND_North_15 | Wall_400x300 | (5800,6000,0) | — | MI_DevBlue | ✅ |
| 47 | LVL_BND_North_16 | Wall_400x300 | (6200,6000,0) | — | MI_DevBlue | ✅ |
| 48 | LVL_BND_North_17 | Wall_400x300 | (6600,6000,0) | — | MI_DevBlue | ✅ |
| 49 | LVL_BND_North_18 | Wall_400x300 | (7000,6000,0) | — | MI_DevBlue | ✅ |
| 50 | LVL_BND_North_19 | Wall_400x300 | (7400,6000,0) | — | MI_DevBlue | ✅ |
| 51 | LVL_BND_North_20 | Wall_400x300 | (7800,6000,0) | — | MI_DevBlue | ✅ |
| 52 | LVL_BND_South_01 | Wall_400x300 | (1800,0,0) | — | MI_DevBlue | ✅ |
| 53 | LVL_BND_South_02 | Wall_400x300 | (2200,0,0) | — | MI_DevBlue | ✅ |
| 54 | LVL_BND_South_03 | Wall_400x300 | (2600,0,0) | — | MI_DevBlue | ✅ |
| 55 | LVL_BND_South_04 | Wall_400x300 | (3000,0,0) | — | MI_DevBlue | ✅ |
| 56 | LVL_BND_South_05 | Wall_400x300 | (3400,0,0) | — | MI_DevBlue | ✅ |
| 57 | LVL_BND_South_06 | Wall_400x300 | (3800,0,0) | — | MI_DevBlue | ✅ |
| 58 | LVL_BND_South_07 | Wall_400x300 | (4200,0,0) | — | MI_DevBlue | ✅ |
| 59 | LVL_BND_South_08 | Wall_400x300 | (4600,0,0) | — | MI_DevBlue | ✅ |
| 60 | LVL_BND_South_09 | Wall_400x300 | (5000,0,0) | — | MI_DevBlue | ✅ |
| 61 | LVL_BND_South_10 | Wall_400x300 | (5400,0,0) | — | MI_DevBlue | ✅ |
| 62 | LVL_BND_South_11 | Wall_400x300 | (7200,0,0) | — | MI_DevBlue | ✅ |
| 63 | LVL_BND_South_12 | Wall_400x300 | (7600,0,0) | — | MI_DevBlue | ✅ |
| 64 | LVL_Global_DirectionalLight | | (0,0,500) | — | — | ✅ |
| 65 | LVL_Global_SkyLight | | (0,0,0) | — | — | ✅ |
| 66 | LVL_PlayerStart | | (500,720,0) | — | — | ✅ |
## 三、资产使用统计
| 资产名 | 使用次数 |
|--------|---------|
| Wall_400x300 | 106 |
| Cube | 21 |
| Floor_1000x1000 | 15 |
| CirclePlatform | 12 |
| Cylinder | 9 |
| BarrierWall_600 | 8 |
| Beam_300 | 5 |
| BarrierWall_200 | 5 |
| Wall_400x100 | 4 |
| Floor_300x400 | 4 |
| WoodenBox_Small | 4 |
| Wall_200x300 | 4 |
| TrashCans_01 | 3 |
| Fence_Small_400 | 3 |
| RoadLamp_01 | 3 |
| Car01 | 2 |
| Monitor_01 | 2 |
| Floor_200x300 | 2 |
| WallDoorDouble_400x300 | 1 |
| RotativeDoor_01 | 1 |
| WallWindow_400x300 | 1 |
| WallWindowSmall_200x300 | 1 |
| WallDoor_200x300 | 1 |
| Bucket_01 | 1 |
| Barrel | 1 |
| SimpleTable_01 | 1 |
| BedSingle | 1 |
| SimpleTable_02 | 1 |
| Wall_100x300 | 1 |
| DisplayPanels_400x200 | 1 |
| WallDoorGarage_400x300 | 1 |
| GarageDoorOpen_01 | 1 |
| Container_Normal | 1 |
| Tree_01 | 1 |
| WoodenPallet_01 | 1 |
| WoodenBox_Normal | 1 |
| FakeWindowsFrame | 1 |
| Building_01 | 1 |
## 四、缩放记录
| Actor 名称 | 资产 | 缩放 | 实际尺寸 (cm) |
|-----------|------|------|-------------|
| LVL_Global_GroundFloor | Floor_1000x1000 | (8.00,6.00,1.00) | 8000×6000×10 |
| LVL_Z2_STR_Floor | Floor_1000x1000 | (0.60,0.60,1.00) | 600×600×10 |
| LVL_Z3_STR_Floor_01 | Floor_1000x1000 | (1.00,0.50,1.00) | 1000×500×10 |
| LVL_Z3_STR_Floor_02 | Floor_1000x1000 | (1.00,0.50,1.00) | 1000×500×10 |
| LVL_Z3_STR_Floor_03 | Floor_1000x1000 | (1.00,0.50,1.00) | 1000×500×10 |
| LVL_Z3_STR_Floor_04 | Floor_1000x1000 | (1.00,0.50,1.00) | 1000×500×10 |
| LVL_Z3_STR_Floor_05 | Floor_1000x1000 | (1.00,0.50,1.00) | 1000×500×10 |
| LVL_Z4_STR_Floor_01 | Floor_1000x1000 | (1.50,1.20,1.00) | 1500×1200×10 |
| LVL_Z5_STR_Floor_01 | Floor_1000x1000 | (0.30,1.00,1.00) | 300×1000×10 |
| LVL_Z5_STR_Floor_02 | Floor_1000x1000 | (0.30,1.00,1.00) | 300×1000×10 |
| LVL_Z6_STR_GroundSouth | Floor_1000x1000 | (1.50,0.35,1.00) | 1500×350×10 |
| LVL_Z6_STR_GroundEast | Floor_1000x1000 | (0.15,0.80,1.00) | 150×800×10 |
| LVL_Z6_STR_GroundWest | Floor_1000x1000 | (0.15,0.80,1.00) | 150×800×10 |
| LVL_Z6_STR_Water | Floor_1000x1000 | (1.20,0.80,0.50) | 1200×800×5 |
| LVL_Z1_STR_CourtyardFloor | Floor_1000x1000 | (1.00,0.80,1.00) | 1000×800×10 |
| LVL_Z1_PROP_OldBicycle | Car01 | (0.20,0.20,0.20) | (默认尺寸未知) |
| LVL_Z1_PROP_ClothesPole_Pole1 | Cylinder | (0.05,0.05,3.60) | 5×5×360 |
| LVL_Z1_PROP_ClothesPole_Pole2 | Cylinder | (0.05,0.05,3.60) | 5×5×360 |
| LVL_Z1_PROP_DoorMat | Cube | (0.60,0.40,0.02) | 60×40×2 |
| LVL_Z1_PROP_Fridge | Barrel | (0.30,0.30,1.60) | (默认尺寸未知) |
| LVL_Z1_PROP_Calendar | Cube | (0.30,0.02,0.40) | 30×2×40 |
| LVL_Z1_PROP_TVStand | SimpleTable_02 | (0.85,0.75,0.67) | (默认尺寸未知) |
| LVL_Z1_INTR_01_Fridge | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z1_INTR_02_Gate | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z1_INTR_06_TV | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z1_COLL_10_CatEyeMarble | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z3_PROP_PhoneBooth | Cube | (0.80,0.80,2.20) | 80×80×220 |
| LVL_Z3_PROP_Bicycle | Car01 | (0.20,0.20,0.20) | (默认尺寸未知) |
| LVL_Z3_PROP_ClothesPolePole | Cylinder | (0.05,0.05,3.60) | 5×5×360 |
| LVL_Z3_PROP_SewerCover | CirclePlatform | (0.12,0.12,0.02) | (默认尺寸未知) |
| LVL_Z3_PROP_Graffiti | Cube | (1.20,0.05,0.80) | 120×5×80 |
| LVL_Z3_PROP_PoleWest | Cylinder | (0.04,0.04,12.00) | 4×4×1200 |
| LVL_Z3_PROP_PoleEast | Cylinder | (0.04,0.04,12.00) | 4×4×1200 |
| LVL_Z3_INTR_04_PhoneBooth | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z3_INTR_09_Graffiti | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z3_COLL_08_IceFlowerMarble | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z2_PROP_Counter | Cube | (2.00,0.60,0.90) | 200×60×90 |
| LVL_Z2_PROP_ShelfEast_01 | Fence_Small_400 | (1.00,1.00,1.80) | 400×5×180 |
| LVL_Z2_PROP_ShelfEast_02 | Fence_Small_400 | (1.00,1.00,1.80) | 400×5×180 |
| LVL_Z2_PROP_ShelfSouth | Fence_Small_400 | (1.00,1.00,1.80) | 400×5×180 |
| LVL_Z2_PROP_Freezer | Container_Normal | (0.25,0.15,0.40) | (默认尺寸未知) |
| LVL_Z2_PROP_SmallTV | Monitor_01 | (0.60,0.60,0.60) | (默认尺寸未知) |
| LVL_Z2_PROP_Signboard | Cube | (3.00,0.05,0.40) | 300×5×40 |
| LVL_Z2_PROP_CardRack | Cube | (0.40,0.20,0.30) | 40×20×30 |
| LVL_Z2_INTR_05_ShopOwner | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z2_INTR_07_SnackShelf | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z2_COLL_09_BouncyBall | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z4_PROP_BigTree | Tree_01 | (1.20,1.20,1.20) | (默认尺寸未知) |
| LVL_Z4_PROP_Puddle | Cube | (1.20,0.80,0.01) | 120×80×1 |
| LVL_Z4_PROP_MarbleCircle | CirclePlatform | (0.16,0.16,0.01) | (默认尺寸未知) |
| LVL_Z4_INTR_03_BlueMarble | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z4_INTR_08_SmallStone | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z4_COLL_01_BlueMarble | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z4_COLL_03_SmallStone | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z4_COLL_04_LuZhishenCard | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z5_PROP_SpiderWeb | Cube | (0.30,0.30,0.01) | 30×30×1 |
| LVL_Z5_PROP_FlowerPot | Cylinder | (0.20,0.20,0.25) | 20×20×25 |
| LVL_Z5_PROP_Moss | Cube | (0.60,0.40,0.01) | 60×40×1 |
| LVL_Z5_INTR_10_WasteBox | Cube | (0.30,0.30,0.30) | 30×30×30 |
| LVL_Z5_COLL_02_RedMarble | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z6_PROP_Reeds_S1~5 | Cylinder | (0.08,0.08,2.00) | 8×8×200 |
| LVL_Z6_PROP_Reeds_S2_1~5 | Cylinder | (0.08,0.08,2.00) | 8×8×200 |
| LVL_Z6_PROP_Reeds_E_1~3 | Cylinder | (0.08,0.08,1.60) | 8×8×160 |
| LVL_Z6_PROP_LilyPad_1~4 | CirclePlatform | (0.06,0.06,0.01) | (默认尺寸未知) |
| LVL_Z6_PROP_Frog | Cube | (0.05,0.05,0.04) | 5×5×4 |
| LVL_Z6_COLL_05_CandyWrapper | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
| LVL_Z6_COLL_06_Eraser | CirclePlatform | (0.03,0.03,0.03) | (默认尺寸未知) |
@@ -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
# ----------------------------------------------------------
# 辅助方法
# ----------------------------------------------------------
@@ -164,7 +164,13 @@ def parse_location(text: str) -> tuple[float, float, float]:
def parse_rotation(text: str) -> tuple[float, float, float]:
"""解析旋转字符串 '(P,Y,R)' 为三元组(度数)。"""
"""解析旋转字符串 '(P,Y,R)' 为三元组(度数)。
处理箭头注释格式:'(0,0,0) → Yaw=90°' 只取括号内部分。
"""
if not text:
return (0.0, 0.0, 0.0)
# 先尝试提取括号内的旋转值,忽略箭头后面的注释
return parse_location(text)