Files
Rainer LeitandClaude Fable 5.1 d64748f76f Fix the sun in L_World and L_Gym: positional unreal.Rotator is (roll, pitch, yaw)
Both scripts passed (pitch, yaw, roll) and pitched the sun upward, so every scene rendered as night and the
viewport's auto exposure sat at EV -8.5, which is what the 'cached lighting is going to be clipped' warning
reports. Keyword arguments now; the gym script re-applies the rotation on an existing sun; the world script
removes the old external actor folder on disk rather than through the asset library, which cannot load
streaming proxies on their own.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 22:03:42 +03:00

89 lines
3.7 KiB
Python

"""Creates /Game/Maps/L_Gym if it does not exist: a flat floor, a player start and daylight. Idempotent.
Step 1 wants an empty gym; step 3 extends this script with the greybox sections from Movement.md (stairs, slopes,
gaps, ledges, doorways, beams, corridor, surfaces, arena). Run headless:
UnrealEditor-Cmd.exe Salty.uproject -run=pythonscript -script=Scripts/Authoring/create_gym.py
or from the editor's Python console. Never hand-edit the .umap; rerun this.
"""
import unreal
LEVEL_PATH = "/Game/Maps/L_Gym"
FLOOR_HALF_SIZE_CM = 5000.0 # a 100 m square; the cube mesh is 100 cm, so scale = size / 100
CUBE = "/Engine/BasicShapes/Cube.Cube"
FLOOR_MATERIAL = "/Game/LevelPrototyping/Materials/MI_PrototypeGrid_Gray.MI_PrototypeGrid_Gray"
level_subsystem = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
asset_lib = unreal.EditorAssetLibrary
def spawn(actor_class, label, location=unreal.Vector(0, 0, 0), rotation=unreal.Rotator(0, 0, 0)):
actor = actor_subsystem.spawn_actor_from_class(actor_class, location, rotation)
actor.set_actor_label(label)
return actor
def find_actor(label):
for actor in actor_subsystem.get_all_level_actors():
if actor.get_actor_label() == label:
return actor
return None
def ensure_floor():
if find_actor("Gym_Floor"):
return
floor = spawn(unreal.StaticMeshActor, "Gym_Floor", unreal.Vector(0, 0, -50))
mesh = unreal.load_asset(CUBE)
component = floor.static_mesh_component
component.set_editor_property("mobility", unreal.ComponentMobility.STATIC)
component.set_static_mesh(mesh)
scale = FLOOR_HALF_SIZE_CM * 2.0 / 100.0
floor.set_actor_scale3d(unreal.Vector(scale, scale, 1.0))
material = unreal.load_asset(FLOOR_MATERIAL) if asset_lib.does_asset_exist(FLOOR_MATERIAL) else None
if material:
component.set_material(0, material)
def ensure_daylight():
sun = find_actor("Gym_Sun")
if not sun:
sun = spawn(unreal.DirectionalLight, "Gym_Sun", unreal.Vector(0, 0, 500))
sun.light_component.set_editor_property("intensity", 10.0)
sun.light_component.set_editor_property("atmosphere_sun_light", True)
# Always re-applied, with keyword arguments: positional unreal.Rotator is (roll, pitch, yaw), and the first
# version of this script pitched the sun 30 degrees upward, which lit the gym as night.
sun.set_actor_rotation(unreal.Rotator(roll=0.0, pitch=-45.0, yaw=30.0), False)
if not find_actor("Gym_Sky"):
spawn(unreal.SkyAtmosphere, "Gym_Sky", unreal.Vector(0, 0, 0))
if not find_actor("Gym_SkyLight"):
sky_light = spawn(unreal.SkyLight, "Gym_SkyLight", unreal.Vector(0, 0, 500))
sky_light.light_component.set_editor_property("real_time_capture", True)
if not find_actor("Gym_Fog"):
spawn(unreal.ExponentialHeightFog, "Gym_Fog", unreal.Vector(0, 0, 0))
def ensure_player_starts():
# Two starts so two PIE clients on a dedicated server both spawn without a collision warning.
for index, y in enumerate((-150, 150)):
label = f"Gym_PlayerStart_{index}"
if not find_actor(label):
spawn(unreal.PlayerStart, label, unreal.Vector(0, y, 100))
def main():
if asset_lib.does_asset_exist(LEVEL_PATH):
unreal.log(f"{LEVEL_PATH} exists; loading it to top up missing actors")
level_subsystem.load_level(LEVEL_PATH)
else:
unreal.log(f"creating {LEVEL_PATH}")
level_subsystem.new_level(LEVEL_PATH, False) # not world-partitioned: the gym is small and hand-laid
ensure_floor()
ensure_daylight()
ensure_player_starts()
level_subsystem.save_current_level()
unreal.log(f"{LEVEL_PATH} saved")
main()