Step 1: the Salty project, two modules, tests and the gym
Salty.uproject (UE 5.8) from the Third Person template with class redirects, the SaltyCore and Salty modules, the three build targets, USaltyAssetManager calling InitGlobalData, Config/Tags with the 23 root namespaces, Scripts/run-tests.sh, build.sh and Authoring/create_gym.py, L_Gym, Git LFS attributes and the placeholder test. Template variants kept as reference (D-41). The four Fab packs stay out of the repository for now (~10 GB). The editor serves the engine MCP plugin on 127.0.0.1:8000 through DefaultEditorPerProjectUserSettings.ini. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
0e61a77346
commit
4f2c55cd2a
@@ -0,0 +1,84 @@
|
||||
"""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():
|
||||
if not find_actor("Gym_Sun"):
|
||||
sun = spawn(unreal.DirectionalLight, "Gym_Sun", unreal.Vector(0, 0, 500), unreal.Rotator(-45, 30, 0))
|
||||
sun.light_component.set_editor_property("intensity", 8.0)
|
||||
sun.light_component.set_editor_property("atmosphere_sun_light", True)
|
||||
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()
|
||||
Reference in New Issue
Block a user