Batch Modify Landscape Grass Type Properties
Tested on Unreal Engine 5.3
Script Overview
This script enables batch modification of specific properties for all LandscapeGrassType assets’ GrassVarieties array elements in a given folder within a UE5.3 project.
Because Unreal Engine’s set_editor_property does not work reliably for struct array elements,
the script creates a new struct, deep-copies all fields, overrides selected properties, writes the array back, and saves the asset to ensure all data is retained and changes take effect.
Usage Instructions
1. Asset Scope Selection
-
Option 1: Folder Scan
- Set
use_folder_scan = Trueand specifysearch_folder(Unreal path, exclude ‘Content’). - This will batch-process all assets in that folder and subfolders.
- Set
-
Option 2: Manual Asset Paths
- Set
use_folder_scan = Falseand add paths inmanual_asset_pathsto process only the assets you specify.
- Set
2. Parameters to Modify
-
start_cull_distanceandend_cull_distance
Set these to the values you want to batch apply. -
fields_to_copy
Must include every field you want preserved forGrassVariety.
Use lowercase, underscore-separated field names as shown below.
3. Process
For each asset, and for each GrassVariety element:
- Create a new struct
- Copy all fields
- Override the specified parameters
- Set the array back on the asset and save
Important Notes
- fields_to_copy:
You must maintain a complete list (including any custom or blueprint-exposed fields).
Omitting a field will cause it to be lost (reset to default) for affected varieties.
import unreal
# == USER SETTINGS ==
# Option 1: Scan assets in a folder
use_folder_scan = True # True = scan folder, False = use manual asset list
search_folder = "/Game/Brushify/Materials/Landscape/GrassTypes" # Unreal path (exclude 'Content')
# Option 2: Manual asset paths
manual_asset_paths = [
"/Game/Brushify/Materials/Landscape/GrassTypes/LG_Beach",
# "/Game/Brushify/Materials/Landscape/GrassTypes/OtherAsset",
]
# == PARAMETERS TO MODIFY ==
start_cull_distance = 666
end_cull_distance = 888
fields_to_copy = [
"grass_mesh",
"override_materials",
"grass_density",
"use_grid",
"placement_jitter",
"start_cull_distance",
"end_cull_distance",
"min_lod",
"scaling",
"scale_x",
"scale_y",
"scale_z",
"random_rotation",
"align_to_surface",
"use_landscape_lightmap",
"lighting_channels",
"receives_decals",
"affect_distance_field_lighting",
"cast_dynamic_shadow",
"cast_contact_shadow",
"keep_instance_buffer_cpucopy",
"instance_world_position_offset_disable_distance",
"shadow_cache_invalidation_behavior",
]
# == ASSET PATHS TO PROCESS ==
if use_folder_scan:
print(f"Scanning folder: {search_folder}")
asset_paths = unreal.EditorAssetLibrary.list_assets(search_folder, recursive=True, include_folder=False)
else:
print("Using manual asset path list")
asset_paths = manual_asset_paths
#MAIN PROCESS
for asset_path in asset_paths:
print(f"Processing: {asset_path}")
asset = unreal.EditorAssetLibrary.load_asset(asset_path)
if not asset:
print(f"Failed to load asset: {asset_path}")
continue
# Retrieve grass_varieties property (array of structs)
try:
grass_varieties = asset.get_editor_property("grass_varieties")
except Exception:
try:
grass_varieties = asset.get_editor_property("GrassVarieties")
except Exception:
print(f"Cannot find GrassVarieties property: {asset_path}")
continue
new_grass_varieties = []
changed = False
for i, grass_object in enumerate(grass_varieties):
new_grass = unreal.GrassVariety()
# Deep-copy all fields to preserve data
for field in fields_to_copy:
try:
value = grass_object.get_editor_property(field)
new_grass.set_editor_property(field, value)
except Exception:
pass # Ignore if property does not exist on this asset
# Override the target properties
try:
scd_struct = new_grass.get_editor_property("start_cull_distance")
scd_struct.set_editor_property("default", start_cull_distance)
except Exception as e:
print(f"Variety {i}: Failed to set start_cull_distance: {e}")
try:
ecd_struct = new_grass.get_editor_property("end_cull_distance")
ecd_struct.set_editor_property("default", end_cull_distance)
except Exception as e:
print(f"Variety {i}: Failed to set end_cull_distance: {e}")
new_grass_varieties.append(new_grass)
changed = True
if changed:
asset.set_editor_property("grass_varieties", new_grass_varieties)
unreal.EditorAssetLibrary.save_loaded_asset(asset)
print(f"Batch-modified: {asset_path}")
print("All assets updated.")
