Editor utility for scene cleanup and physics settling. Provides ScatterClutter to clear/erase or flush a clutter grid on the active scene, and SettlePhysicsAsync to enable editor physics on all or tagged Rigidbody components, wait a given time, then disable editor physics.
#nullable enable
using System;
using System.Linq;
using System.Threading.Tasks;
using Sandbox;
namespace Editor.Mcp
{
internal static class ClutterAndPhysicsDriver
{
public static ToolOutcome<object> ScatterClutter( string action = "flush", float radius = 500f, int density = 50, Vector3? position = null )
{
var scene = EditorRuntime.ResolveActiveScene();
if ( scene == null )
{
return ToolOutcome<object>.Failure( null, ToolStatus.Unavailable, "clutter.no_scene", "No active scene loaded." );
}
var system = scene.GetSystem<Sandbox.Clutter.ClutterGridSystem>();
if ( system == null )
{
return ToolOutcome<object>.Failure( null, ToolStatus.Unavailable, "clutter.no_system", "ClutterGridSystem is not registered on the active scene." );
}
var normAction = action.ToLowerInvariant();
var center = position ?? Vector3.Zero;
if ( normAction == "clear" )
{
system.ClearAllPainted();
system.Flush();
return ToolOutcome<object>.Ready( new { Action = "clear", Cleared = true } );
}
if ( normAction == "erase" )
{
system.Erase( center, radius );
system.Flush();
return ToolOutcome<object>.Ready( new { Action = "erase", Center = center, Radius = radius } );
}
// Flush / status
system.Flush();
return ToolOutcome<object>.Ready( new
{
Action = action,
Radius = radius,
Density = density,
Center = center,
Status = "Clutter grid flushed"
} );
}
public static async Task<ToolOutcome<object>> SettlePhysicsAsync( float durationSeconds = 1.5f, string? targetTag = null )
{
var scene = EditorRuntime.ResolveActiveScene();
if ( scene == null )
{
return ToolOutcome<object>.Failure( null, ToolStatus.Unavailable, "physics.no_scene", "No active scene loaded." );
}
var rigidBodies = scene.GetAllComponents<Rigidbody>().Where( rb => rb.IsValid() ).ToArray();
if ( !string.IsNullOrWhiteSpace( targetTag ) )
{
rigidBodies = rigidBodies.Where( rb => rb.GameObject.Tags.Has( targetTag ) ).ToArray();
}
if ( rigidBodies.Length == 0 )
{
return ToolOutcome<object>.Ready( new
{
Status = "No active Rigidbody components found in scene to simulate.",
SettledCount = 0,
Duration = durationSeconds,
Success = true
} );
}
var settleTime = Math.Clamp( durationSeconds, 0.2f, 10.0f );
try
{
scene.EnableEditorPhysics( true );
foreach ( var rb in rigidBodies )
{
scene.EnableEditorRigidBody( rb, true );
}
await Task.Delay( (int)(settleTime * 1000) );
}
finally
{
scene.DisableEditorRigidBodies();
scene.EnableEditorPhysics( false );
}
return ToolOutcome<object>.Ready( new
{
BodiesSettled = rigidBodies.Length,
DurationSeconds = settleTime,
RestingPositionsSaved = true
} );
}
}
}