"""Add one piece of demo content to ReviewMap and save it as a vehicle probe level. GIGLINE_PROBE_ASSET=Terrain_DEM [GIGLINE_PROBE_NOCOLLISION=1] /UnrealEditor-Cmd \ -run=pythonscript -script=/Tools/unreal/ue_vehicle_probe_map.py -nullrhi -unattended -nopause Why: the Chaos pawn falls correctly on ReviewMap's empty floor and is thrown upward everywhere in DemoMap. Rather than guess which of DemoMap's 593 staged meshes is responsible, add them one group at a time to the map that works and watch for the moment the pawn stops falling. With no GIGLINE_PROBE_ASSET set, the level is a plain copy of ReviewMap - the control. Assets are looked up as /Game/Hollywood/Site/ first, then /Game/Hollywood/Buildings/. Result: /tmp/gigline/vehicle-probe.txt """ import os import unreal RESULT = "/tmp/gigline/vehicle-probe.txt" TARGET = "/Game/Test/VehicleProbe" SOURCE = "/Game/Test/ReviewMap" SEARCH = ("/Game/Hollywood/Site", "/Game/Hollywood/Buildings", "/Game/Hollywood/Streets", "/Game/Hollywood/Props") lines = [] def log(message): lines.append(str(message)) unreal.log(str(message)) def find_asset(name): for folder in SEARCH: path = f"{folder}/{name}" if unreal.EditorAssetLibrary.does_asset_exist(path): return path return None def main(): wanted = os.environ.get("GIGLINE_PROBE_ASSET", "").strip() if not unreal.EditorAssetLibrary.does_asset_exist(SOURCE): log(f"missing source level {SOURCE}") lines.append("VERDICT: FAIL") return target = TARGET # A level that the previous run left behind cannot always be deleted while the editor holds it, # so fall back to a suffixed name rather than failing the probe. for suffix in ("", "_b", "_c", "_d"): target = TARGET + suffix if not unreal.EditorAssetLibrary.does_asset_exist(target): break unreal.EditorAssetLibrary.delete_asset(target) if not unreal.EditorAssetLibrary.does_asset_exist(target): break if not unreal.EditorAssetLibrary.duplicate_asset(SOURCE, target): log(f"could not duplicate {SOURCE} -> {TARGET}") lines.append("VERDICT: FAIL") return log(f"duplicated {SOURCE} -> {target}") unreal.EditorLevelLibrary.load_level(target) if wanted: path = find_asset(wanted) if path is None: log(f"MISSING asset {wanted} in {SEARCH}") lines.append("VERDICT: FAIL") return mesh = unreal.EditorAssetLibrary.load_asset(path) actor = unreal.EditorLevelLibrary.spawn_actor_from_class( unreal.StaticMeshActor, unreal.Vector(0.0, 0.0, 0.0)) comp = actor.get_component_by_class(unreal.StaticMeshComponent) comp.set_static_mesh(mesh) actor.set_actor_label(f"Probe_{wanted}") log(f"staged probe mesh {path} at the origin") # GIGLINE_PROBE_NOCOLLISION=1 stages the same mesh with no collision at all, which separates # "this mesh's collision ejects physics bodies" from "this mesh does something else". if os.environ.get("GIGLINE_PROBE_NOCOLLISION", "").strip() == "1": comp.set_collision_enabled(unreal.CollisionEnabled.NO_COLLISION) log("probe mesh collision disabled") else: log("control run: no extra content") vehicle_class = unreal.load_class(None, "/Script/GigLine.GigLineVehicle") if vehicle_class: pawn = unreal.EditorLevelLibrary.spawn_actor_from_class( vehicle_class, unreal.Vector(600.0, 0.0, 300.0)) pawn.set_actor_label("Probe_Vehicle") log("staged the Chaos pawn at (600, 0, 300)") else: log("vehicle class not found") if unreal.EditorLevelLibrary.save_current_level(): log(f"saved {target}") lines.append(f"LEVEL: {target}") lines.append("VERDICT: PASS") else: log(f"save failed for {target}") lines.append("VERDICT: FAIL") try: main() except Exception as exc: # noqa: BLE001 import traceback log(f"FAILED: {type(exc).__name__}: {exc}") log(traceback.format_exc()) lines.append("VERDICT: FAIL") with open(RESULT, "w") as f: f.write("\n".join(lines) + "\n")