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:
@@ -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 布景物
|
||||
|
||||
|
||||
@@ -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] 保存关卡...")
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
@@ -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": [],
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user