AGigLineCharacter: 1.75 m capsule, 1.60 m eye camera, 4 m/s walk, mouse look scaled in degrees per second (no double DeltaTime), jump. Loaded ahead of a Blueprint so the demo is playable with no assets wired by hand. Tools/ue_create_input_assets.py builds /Game/Input headlessly: five 1D actions and an 11-mapping context. One action per direction instead of per-key modifiers, because modifier objects do not survive the save path here: a mapping's modifier array serialises None, and dimensioning a 2D move with per-key axis masks would need exactly those modifiers. The cost is four action assets rather than two. Also adds a fourth review camera at eye height and documents it; its approval is pending.
135 lines
5.7 KiB
Python
135 lines
5.7 KiB
Python
# Builds /Game/Test/ReviewMap — the visual-review scene — and saves it as a
|
|
# map asset. Run before the screenshot pass (Tools/render_screenshots.sh does
|
|
# this automatically).
|
|
#
|
|
# Scene: metric floor, key light, fill sky, sky atmosphere, MPFB character,
|
|
# the three 1 m scale cubes, and an AGigLineShotActor with the camera list.
|
|
#
|
|
# UnrealEditor-Cmd <uproject> -run=pythonscript -script=<repo>/Tools/ue_build_review_map.py \
|
|
# -nullrhi -unattended -nopause -stdout
|
|
#
|
|
# Do NOT load_map here: after a map load the commandlet's spawn context dies
|
|
# (every spawn_actor call returns None). Everything is spawned into the
|
|
# default world and saved as a new map asset.
|
|
|
|
import os
|
|
|
|
import unreal
|
|
|
|
RESULT = "/tmp/ue-review-map-result.txt"
|
|
MAP_PATH = "/Game/Test/ReviewMap"
|
|
lines = []
|
|
|
|
|
|
def log(msg):
|
|
lines.append(str(msg))
|
|
|
|
|
|
def spawn(cls, loc, label=None, scale=None, rot=None):
|
|
a = unreal.EditorLevelLibrary.spawn_actor_from_class(cls, loc)
|
|
if a is None:
|
|
log(f"spawn {cls.__name__} returned None")
|
|
return None
|
|
if rot is not None:
|
|
a.set_actor_rotation(rot, False)
|
|
if scale is not None:
|
|
a.set_actor_scale3d(scale)
|
|
if label:
|
|
a.set_actor_label(label)
|
|
return a
|
|
|
|
|
|
# --- scene -------------------------------------------------------------------
|
|
|
|
try:
|
|
# Neutral gray floor material (the scale-test mesh ships red)
|
|
floor_mat = None
|
|
if not unreal.EditorAssetLibrary.does_asset_exist("/Game/Test/M_ReviewFloor"):
|
|
floor_mat = unreal.AssetToolsHelpers.get_asset_tools().create_asset(
|
|
"M_ReviewFloor", "/Game/Test", unreal.Material, unreal.MaterialFactoryNew())
|
|
n = unreal.MaterialEditingLibrary.create_material_expression(
|
|
floor_mat, unreal.MaterialExpressionConstant3Vector)
|
|
n.set_editor_property("constant", unreal.LinearColor(0.35, 0.35, 0.35, 1.0))
|
|
unreal.MaterialEditingLibrary.connect_material_property(
|
|
n, "", unreal.MaterialProperty.MP_BASE_COLOR)
|
|
unreal.MaterialEditingLibrary.recompile_material(floor_mat)
|
|
unreal.EditorAssetLibrary.save_loaded_asset(floor_mat)
|
|
else:
|
|
floor_mat = unreal.EditorAssetLibrary.load_asset("/Game/Test/M_ReviewFloor")
|
|
|
|
floor_mesh = unreal.EditorAssetLibrary.load_asset("/Game/Test/ScaleTest/ScaleTest_A")
|
|
if floor_mesh and floor_mat:
|
|
floor = spawn(unreal.StaticMeshActor, unreal.Vector(450, 450, -50.0), "ReviewFloor",
|
|
scale=(45.0, 45.0, 0.1))
|
|
if floor:
|
|
floor.static_mesh_component.set_static_mesh(floor_mesh)
|
|
floor.static_mesh_component.set_material(0, floor_mat)
|
|
log("staged: floor (45 m plane, gray)")
|
|
else:
|
|
log("floor spawn failed")
|
|
|
|
if spawn(unreal.DirectionalLight, unreal.Vector(0, 0, 900), "RenderKeyLight",
|
|
rot=unreal.Rotator(pitch=-38.0, yaw=35.0, roll=0.0)):
|
|
log("staged: DirectionalLight")
|
|
if spawn(unreal.SkyLight, unreal.Vector(0, 0, 1200), "RenderFillSky"):
|
|
log("staged: SkyLight")
|
|
if spawn(unreal.SkyAtmosphere, unreal.Vector(0, 0, 0), "RenderSky"):
|
|
log("staged: SkyAtmosphere")
|
|
|
|
char_mesh = unreal.EditorAssetLibrary.load_asset("/Game/Test/Character/character")
|
|
if char_mesh:
|
|
ch = spawn(unreal.SkeletalMeshActor, unreal.Vector(0, 0, 0), "ReviewCharacter",
|
|
rot=unreal.Rotator(pitch=0.0, yaw=180.0, roll=0.0))
|
|
if ch:
|
|
ch.skeletal_mesh_component.set_skinned_asset_and_update(char_mesh)
|
|
log("staged: MPFB character at origin, facing -Y")
|
|
else:
|
|
log("MISSING: /Game/Test/Character/character")
|
|
|
|
for name, x in (("ScaleTest_A", -400.0), ("ScaleTest_B", 0.0), ("ScaleTest_C", 400.0)):
|
|
m = unreal.EditorAssetLibrary.load_asset(f"/Game/Test/ScaleTest/{name}")
|
|
if m:
|
|
c = spawn(unreal.StaticMeshActor, unreal.Vector(x, 600.0, 50.0), f"Cube_{name}")
|
|
if c:
|
|
c.static_mesh_component.set_static_mesh(m)
|
|
log(f"staged: {name} at ({x}, 600, 50)")
|
|
|
|
shots = spawn(unreal.GigLineShotActor, unreal.Vector(0, 0, 300), "ShotSequencer")
|
|
if shots:
|
|
shots.set_editor_property("shot_locations", [
|
|
unreal.Vector(900, -1100, 500),
|
|
unreal.Vector(200, -300, 160),
|
|
unreal.Vector(200, 100, 200),
|
|
# first-person eye height, just south-east of the character: this is the
|
|
# view a player has at spawn, capsule and camera stack in frame
|
|
unreal.Vector(120, -260, 165),
|
|
])
|
|
shots.set_editor_property("shot_rotations", [
|
|
unreal.MathLibrary.find_look_at_rotation(
|
|
unreal.Vector(900, -1100, 500), unreal.Vector(100, 200, 80)),
|
|
unreal.MathLibrary.find_look_at_rotation(
|
|
unreal.Vector(200, -300, 160), unreal.Vector(0, 0, 85)),
|
|
unreal.MathLibrary.find_look_at_rotation(
|
|
unreal.Vector(200, 100, 200), unreal.Vector(0, 600, 50)),
|
|
unreal.MathLibrary.find_look_at_rotation(
|
|
unreal.Vector(120, -260, 165), unreal.Vector(-200, 600, 90)),
|
|
])
|
|
shots.set_editor_property("shot_names",
|
|
["overview", "character", "scale-cubes", "first-person"])
|
|
shots.set_editor_property("quit_when_done", True)
|
|
log("staged: ShotSequencer with 4 cameras")
|
|
except Exception as exc: # noqa
|
|
log(f"staging failed: {exc!r}")
|
|
|
|
# --- save --------------------------------------------------------------------
|
|
|
|
try:
|
|
world = unreal.EditorLevelLibrary.get_editor_world()
|
|
saved = unreal.EditorLoadingAndSavingUtils.save_map(world, MAP_PATH)
|
|
log(f"save_map {MAP_PATH}: {saved}")
|
|
except Exception as exc: # noqa
|
|
log(f"save_map failed: {exc!r}")
|
|
|
|
with open(RESULT, "w") as f:
|
|
f.write("\n".join(lines) + "\n")
|