0f96b79dba
两阶段流水线:搭建指南Markdown → JSON → UE5 Actor - 解析器:4种表格变体+范围展开+坐标表达式计算 - 配置:37种BSP资产映射+13种材质+7区域定义 - 验证:265个Actor全部通过,0错误 - Builder:UE5 Python API自动放置Actor+材质+Outliner - 报告:自动生成搭建报告Markdown Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
"""离线资产验证模块。
|
||
|
||
检查解析数据中的资产引用是否有效:
|
||
1. asset_key 是否在 asset_mapping 中定义
|
||
2. 材质名是否在 material_mapping 中定义
|
||
3. Content/ 目录下 .uasset 文件是否存在(可选)
|
||
"""
|
||
|
||
import logging
|
||
from pathlib import Path
|
||
from dataclasses import dataclass
|
||
|
||
from config.asset_mapping import MESH_ASSETS
|
||
from config.material_mapping import MATERIAL_PATHS
|
||
|
||
logger = logging.getLogger("level_builder.validator")
|
||
|
||
|
||
@dataclass
|
||
class ValidationResult:
|
||
"""验证结果。"""
|
||
total_actors: int = 0
|
||
valid_actors: int = 0
|
||
warnings: list[str] = None
|
||
errors: list[str] = None
|
||
missing_assets: set[str] = None
|
||
missing_materials: set[str] = None
|
||
|
||
def __post_init__(self):
|
||
if self.warnings is None:
|
||
self.warnings = []
|
||
if self.errors is None:
|
||
self.errors = []
|
||
if self.missing_assets is None:
|
||
self.missing_assets = set()
|
||
if self.missing_materials is None:
|
||
self.missing_materials = set()
|
||
|
||
@property
|
||
def is_valid(self) -> bool:
|
||
return len(self.errors) == 0
|
||
|
||
|
||
def validate_actors(actors: list[dict], project_root: str = "") -> ValidationResult:
|
||
"""验证 Actor 列表中的资产引用。
|
||
|
||
Args:
|
||
actors: Actor 数据列表(来自解析器的 JSON 输出)。
|
||
project_root: 项目根目录路径(用于文件存在性检查)。
|
||
|
||
Returns:
|
||
ValidationResult 包含所有验证信息。
|
||
"""
|
||
result = ValidationResult(total_actors=len(actors))
|
||
|
||
for i, actor in enumerate(actors):
|
||
name = actor.get("actor_name", f"<unnamed #{i}>")
|
||
asset_key = actor.get("asset_key", "")
|
||
material = actor.get("material", "")
|
||
asset_type = actor.get("asset_type", "static_mesh")
|
||
|
||
# 跳过非 mesh 类型(光照、PlayerStart 等)
|
||
if asset_type != "static_mesh":
|
||
result.valid_actors += 1
|
||
continue
|
||
|
||
# 检查 asset_key
|
||
if not asset_key:
|
||
result.warnings.append(f"{name}: 无 asset_key(可能是光照/特殊 Actor)")
|
||
result.valid_actors += 1
|
||
continue
|
||
|
||
if asset_key not in MESH_ASSETS:
|
||
result.errors.append(f"{name}: 未知资产 '{asset_key}'")
|
||
result.missing_assets.add(asset_key)
|
||
continue
|
||
|
||
# 检查材质
|
||
if material and material not in MATERIAL_PATHS:
|
||
result.warnings.append(f"{name}: 未知材质 '{material}'")
|
||
result.missing_materials.add(material)
|
||
|
||
# 检查 .uasset 文件存在性(如果提供了项目根目录)
|
||
if project_root:
|
||
mesh_info = MESH_ASSETS[asset_key]
|
||
asset_path = mesh_info["path"]
|
||
# /Game/BlockingStarterPack/Meshes/.../SM_Xxx.SM_Xxx → Content/BlockingStarterPack/Meshes/.../SM_Xxx.uasset
|
||
fs_path = asset_path.replace("/Game/", "Content/").split(".")[0] + ".uasset"
|
||
full_path = Path(project_root) / fs_path
|
||
if not full_path.exists():
|
||
result.warnings.append(f"{name}: 资产文件不存在 '{fs_path}'")
|
||
|
||
result.valid_actors += 1
|
||
|
||
# 汇总日志
|
||
if result.missing_assets:
|
||
logger.error(f"缺失资产定义: {sorted(result.missing_assets)}")
|
||
if result.missing_materials:
|
||
logger.warning(f"缺失材质定义: {sorted(result.missing_materials)}")
|
||
logger.info(f"验证完成: {result.valid_actors}/{result.total_actors} 有效, "
|
||
f"{len(result.errors)} 错误, {len(result.warnings)} 警告")
|
||
|
||
return result
|