Editor/GridMapTool.cs
using System.Text.Json.Nodes;
namespace Editor;
/// <summary>
/// An editor tool for placing models on a grid.
/// </summary>
[EditorTool]
[Title( "Grid Map Tool" )]
[Icon( "apps", "#F9B003", "#00" )]
[Group( "Grid Map Editor" )]
[Order( 5 )]
public partial class GridMapTool : EditorTool
{
bool ShouldSnapToGrid = true;
public PrefabFile PrefabResourse { get; set; }
public PrefabFile oldresource { get; set; }
public string SearchString { get; set; } = "";
public float floors = 0.0f;
[ReadOnly]
public int floorCount = 0;
public Rotation rotation = Rotation.From( 90, 0, 0 );
float rotationSnap = 90.0f;
public enum PaintMode
{
Place = 0,
Remove = 1,
Move = 2,
Copy = 3,
Duplicate = 4,
Decal = 5,
}
public PaintMode CurrentPaintMode { get; set; } = PaintMode.Place;
public PaintMode LastPaintMode { get; set; } = PaintMode.Place;
public GameObject CurrentGameObjectCollection;
public List<GameObject> GameObjectCollection { get; set; } = new();
public List<DuplicatedItems> DuplicateObjectCollection { get; set; } = new();
public struct DuplicatedItems
{
public GameObject gameObject;
public Vector3 position;
public Rotation rotation;
}
TimeSince timeSinceChangedCollection = 0;
bool gridActiveState = true;
SerializedObject serializedObject;
//Get and store the gameobjects from the prefab file.
GameObject SelectedObject { get; set; }
GameObject CopyObject { get; set; }
[Property]
GameObject GizmoGameObject { get; set; }
JsonObject SelectedJsonObject { get; set; }
List<JsonObject> SelectedRandomJsonObject { get; set; } = new();
//Widget
public ListView tilelistView { get; set; } = new();
public List<string> tileGroups { get; set; } = new();
//
public List<TileList> tileList { get; set; } = new();
public struct TileList
{
public string name;
public string group;
public JsonObject jsonObject;
public Pixmap icon;
public List<JsonObject> ranomObjectList;
public bool isPrefab;
public bool isRandom;
public bool isDecal;
}
float decalX = 32.0f;
float decalY = 32.0f;
float decalZ = 32.0f;
bool DecalTriPlanar = false;
public override void OnEnabled()
{
gridActiveState = SceneViewportWidget.LastSelected.State.ShowGrid;
var so = EditorTypeLibrary.GetSerializedObject( this );
serializedObject = so;
AllowGameObjectSelection = false;
foreach ( var objectinscene in Scene.GetAllObjects( true ) )
{
if ( objectinscene.Tags.Has( "Collection" ) && (objectinscene.Parent is Scene) )
{
collectionDropDown.AddItem( objectinscene.Name );
GameObjectCollection.Add( objectinscene );
}
}
FloorHeight = (int)ProjectCookie.Get<float>( "GridHeight", 128 );
MainWindow( so );
ThreeDGizmo( so );
UpdateListViewItems();
ToolWindow( so );
SceneViewportWidget.LastSelected.State.ShowGrid = false;
Grid( new Vector2( 16384, 16384 ), gridRotation, Gizmo.Settings.GridSpacing, SceneViewportWidget.LastSelected.State.GridOpacity );
}
private void OnSearchTextChanged( string searchText )
{
SearchString = searchText.ToLower();
UpdateListViewItems();
}
private void OnGroupChanged()
{
UpdateListViewItems();
}
private void GroupList()
{
tileGroups.Add( "" );
groupDropDown.AddItem( "" );
foreach ( var item in tileList )
{
//Add the groups to the dropdown
if ( !tileGroups.Contains( item.group ) && item.group != null)
{
tileGroups.Add( item.group );
groupDropDown.AddItem( item.group );
}
}
}
private void UpdateListViewItems()
{
// Get the currently selected group from the dropdown
var selectedGroup = groupDropDown.CurrentText;
// Define a lambda function to determine if an item should be included based on the current PaintMode
Func<TileList, bool> shouldInclude = ( model ) =>
{
if ( CurrentPaintMode == PaintMode.Decal )
return model.isDecal;
else
return !model.isDecal;
};
Log.Info( CurrentPaintMode );
// Filter the tile list based on the search string, the selected group, and the PaintMode
var filteredTileList = tileList
.Where( model =>
model.name.ToLower().Contains( SearchString.ToLower() ) &&
(string.IsNullOrEmpty( selectedGroup ) || model.group == selectedGroup) &&
shouldInclude( model ) )
.ToList();
tilelistView.FocusMode = FocusMode.None;
tilelistView.SetItems( filteredTileList.Cast<object>() );
tilelistView.Update(); // Refresh ListView
oldresource = PrefabResourse;
}
public override void OnDisabled()
{
base.OnDisabled();
so.Delete();
SceneViewportWidget.LastSelected.State.ShowGrid = gridActiveState;
EndGameObjectGizmo();
}
private Vector3 startSelectionPoint;
private Vector3 endSelectionPoint;
private bool isSelecting = false;
bool _prevFilled = false;
bool _prevGridded = false;
private void FillSelectionWithTiles( Vector3 start, Vector3 end )
{
float gridSpacing = Gizmo.Settings.GridSpacing;
Vector3 lowerCorner = new Vector3( Math.Min( start.x, end.x ), Math.Min( start.y, end.y ), Math.Min( start.z, end.z ) );
Vector3 upperCorner = new Vector3( Math.Max( start.x, end.x ), Math.Max( start.y, end.y ), Math.Max( start.z, end.z ) );
// Calculate the number of tiles in each dimension
int tilesX = (int)Math.Floor( (upperCorner.x - lowerCorner.x) / gridSpacing ) + 1;
int tilesY = (int)Math.Floor( (upperCorner.y - lowerCorner.y) / gridSpacing ) + 1;
int tilesZ = (int)Math.Floor( (upperCorner.z - lowerCorner.z) / gridSpacing ) + 1;
// Iterate based on the selected axis
switch ( Axis )
{
case GroundAxis.X:
for ( int i = 0; i < tilesY; i++ )
{
for ( int j = 0; j < tilesZ; j++ )
{
PlaceTileAtPosition( new Vector3( lowerCorner.x, lowerCorner.y + i * gridSpacing, lowerCorner.z + j * gridSpacing ) );
}
}
break;
case GroundAxis.Y:
for ( int i = 0; i < tilesX; i++ )
{
for ( int j = 0; j < tilesZ; j++ )
{
PlaceTileAtPosition( new Vector3( lowerCorner.x + i * gridSpacing, lowerCorner.y, lowerCorner.z + j * gridSpacing ) );
}
}
break;
case GroundAxis.Z:
for ( int i = 0; i < tilesX; i++ )
{
for ( int j = 0; j < tilesY; j++ )
{
PlaceTileAtPosition( new Vector3( lowerCorner.x + i * gridSpacing, lowerCorner.y + j * gridSpacing, lowerCorner.z ) );
}
}
break;
}
}
private void PlaceTileAtPosition( Vector3 position )
{
if ( SelectedRandomJsonObject is not null && SelectedRandomJsonObject.Count != 0 )
{
// Create an instance of the Random class
Random random = new Random();
Log.Info( "Random object count: " + SelectedRandomJsonObject.Count );
// Get a random index
int randomIndex = random.Next( SelectedRandomJsonObject.Count );
// Select a random tile from the list
var randomTileJson = SelectedRandomJsonObject[randomIndex];
// Instantiate the game object
var go = new GameObject( true, "GridTile" );
PrefabUtility.MakeGameObjectsUnique( randomTileJson );
go.Deserialize( randomTileJson );
go.Parent = CurrentGameObjectCollection;
go.Transform.Position = position;
go.Transform.Rotation = Rotation.FromPitch( -90 ) * rotation;
go.Tags.Add( "gridtile" );
_prevFilled = false;
}
if ( SelectedJsonObject != null )
{
var go = new GameObject( true, "GridTile" );
PrefabUtility.MakeGameObjectsUnique( SelectedJsonObject );
go.Deserialize( SelectedJsonObject );
go.MakeNameUnique();
go.Parent = CurrentGameObjectCollection;
go.Transform.Position = position;
go.Transform.Rotation = Rotation.FromPitch( -90 ) * rotation;
go.Tags.Add( "gridtile" );
_prevFilled = false;
}
}
//GPT Moment
private SceneTraceResult ProjectRayOntoGroundPlane( Vector3 rayOrigin, Vector3 rayDirection, float groundHeight )
{
Vector3 planeNormal;
Vector3 planePoint;
GroundAxis axis = Axis;
switch ( axis )
{
case GroundAxis.X:
planeNormal = Vector3.Forward; // Normal perpendicular to X-axis
planePoint = new Vector3( SnapToNearestFloor( groundHeight), 0, 0 ); // Point on the X-axis plane
break;
case GroundAxis.Y:
planeNormal = Vector3.Left; // Normal perpendicular to Y-axis
planePoint = new Vector3( 0, SnapToNearestFloor( groundHeight), 0 ); // Point on the Y-axis plane
break;
default: // Z-axis
planeNormal = Vector3.Up; // Normal perpendicular to Z-axis
planePoint = new Vector3( 0, 0, SnapToNearestFloor( groundHeight) ); // Point on the Z-axis plane
break;
}
float denom = Vector3.Dot( planeNormal, rayDirection );
if ( Math.Abs( denom ) > 0.0001f ) // Ensure not parallel
{
float num = Vector3.Dot( planeNormal, planePoint - rayOrigin );
float distance = num / denom;
if ( distance >= 0 )
{
Vector3 hitPoint = rayOrigin + rayDirection * distance;
// Snap to grid, but do not snap the axis that is currently being used
switch ( axis )
{
case GroundAxis.X:
hitPoint = new Vector3( hitPoint.x, SnapToGrid( hitPoint.y ), SnapToGrid( hitPoint.z ) );
break;
case GroundAxis.Y:
hitPoint = new Vector3( SnapToGrid( hitPoint.x ), hitPoint.y, SnapToGrid( hitPoint.z ) );
break;
default: // Z-axis
hitPoint = new Vector3( SnapToGrid( hitPoint.x ), SnapToGrid( hitPoint.y ), hitPoint.z );
break;
}
return new SceneTraceResult { Hit = true, EndPosition = hitPoint };
}
}
return new SceneTraceResult { Hit = false };
}
private float SnapToNearestFloor( float value)
{
// Snap to the nearest height value
return MathF.Floor( value / FloorHeight ) * FloorHeight;
}
private float SnapToGrid( float value )
{
if( !ShouldSnapToGrid )
{
return value;
}
// Assuming you have a grid spacing value
return MathF.Round( value / Gizmo.Settings.GridSpacing ) * Gizmo.Settings.GridSpacing;
}
List<GameObject> SelectedGroupObjects { get; set; } = new List<GameObject>();
private void SelectObjectsInBox( BBox selectionBox )
{
foreach ( var obj in Scene.GetAllObjects( false ) )
{
if ( selectionBox.Contains( obj.Transform.Position ) && obj.Tags.Has( "gridtile" ) && !SelectedGroupObjects.Contains(obj) )
{
SelectedGroupObjects.Add( obj );
}
}
selectedamount.Text = $"Selected: {SelectedGroupObjects.Count}";
}
bool finishedLoadedFromScene = false;
async Task LoadFromScene()
{
if ( !finishedLoadedFromScene && !loadscene )
{
var gameObject = SceneUtility.GetPrefabScene( PrefabResourse ).Clone( new Transform( Vector3.Up * 100000 ) );
gameObject.BreakFromPrefab();
gameObject.Flags = GameObjectFlags.NotSaved | GameObjectFlags.Hidden | GameObjectFlags.Loading;
var allObjects = gameObject.GetAllObjects( true );
bool isFirst = true;
GameObject lastFoundObject = null;
foreach ( var obj in allObjects )
{
// Skip the first object
if ( isFirst )
{
isFirst = false;
continue;
}
if ( !tileList.Any( x => x.name == obj.Name ) && !obj.Tags.Has( "ignore" ) && !obj.IsAncestor( lastFoundObject) )
{
if ( !obj.Tags.Has( "random" ) && obj.Tags.Has( "group" ) && obj.Components.Get<ModelRenderer>( FindMode.EverythingInSelf ) != null )
{
lastFoundObject = obj;
tileList.Add( new TileList()
{
name = obj.Name,
group = obj.Parent.Name,
jsonObject = obj.Serialize(),
isPrefab = obj.IsPrefabInstance,
icon = AssetSystem.FindByPath( obj.Components.Get<ModelRenderer>( FindMode.EnabledInSelfAndChildren ).Model.ResourcePath ).GetAssetThumb()
} );
//Log.Info( obj.Parent.Name );
}
else if ( !obj.Tags.Has( "random" ) && obj.Tags.Has( "decal" ) && obj.Components.Get<DecalRenderer>( FindMode.EnabledInSelf ) != null )
{
lastFoundObject = obj;
tileList.Add( new TileList()
{
name = obj.Name,
jsonObject = obj.Serialize(),
isPrefab = obj.IsPrefabInstance,
icon = AssetSystem.FindByPath( obj.Components.Get<DecalRenderer>( FindMode.EnabledInSelfAndChildren ).Material.ResourcePath ).GetAssetThumb(),
isDecal = true
} );
//Log.Info( obj.Components.Get<ModelRenderer>( FindMode.EnabledInSelfAndChildren ).Model );
}
else if ( !obj.Tags.Has( "random" ) && !obj.Tags.Has( "group" ) && obj.Components.Get<ModelRenderer>( FindMode.EnabledInSelf ) != null )
{
lastFoundObject = obj;
tileList.Add( new TileList()
{
name = obj.Name,
jsonObject = obj.Serialize(),
isPrefab = obj.IsPrefabInstance,
icon = AssetSystem.FindByPath( obj.Components.Get<ModelRenderer>( FindMode.EnabledInSelfAndChildren ).Model.ResourcePath ).GetAssetThumb()
} );
//Log.Info( obj.Components.Get<ModelRenderer>( FindMode.EnabledInSelfAndChildren ).Model );
}
else if ( obj.Tags.Has( "random" ) && !obj.Tags.Has( "group" ) )
{
lastFoundObject = obj;
var randList = new List<JsonObject>();
foreach ( var randobj in obj.Children )
{
randList.Add( randobj.Serialize() );
//Log.Info( randobj.Name );
}
tileList.Add (new TileList()
{
name = obj.Name,
//group = obj.Parent.Name,
//jsonObject = obj.Serialize(),
icon = AssetSystem.FindByPath( obj.Components.Get<ModelRenderer>( FindMode.EnabledInSelfAndChildren ).Model.ResourcePath ).GetAssetThumb(),
isRandom = true,
isPrefab = obj.IsPrefabInstance,
ranomObjectList = new (randList)
});
//Log.Info( randList.FirstOrDefault().ToString() );
}
//await Task.Delay( 10 );
}
}
GroupList();
UpdateListViewItems();
finishedLoadedFromScene = true;
gameObject.Destroy();
}
}
bool loadscene;
Vector3 gridRotation = Vector3.Up;
public override void OnUpdate()
{
Grid( new Vector2( 16384, 16384 ), gridRotation, Gizmo.Settings.GridSpacing, SceneViewportWidget.LastSelected.State.GridOpacity );
UpdateWidgetValues();
gizmowidg.CameraRotation = Gizmo.Camera.Rotation.RotateAroundAxis( Vector3.Backward, 90 ).Inverse;
gizmowidg.Update();
if ( PrefabResourse is not null && tileList is not null)
{
if ( PrefabResourse.ResourceName.Contains( "_tileset" ) )
{
_ = LoadFromScene();
loadscene = true;
}
}
if ( GameObjectCollection is not null && PrefabResourse is not null )
{
CurrentGameObjectCollection = GameObjectCollection.FirstOrDefault( x => x.Name == collectionDropDown.CurrentText );
collectionDropDown.ItemChanged += () =>
{
timeSinceChangedCollection = 0;
};
}
if ( PrefabResourse != oldresource )
{
Log.Info( "Resource Changed" );
tileList.Clear();
UpdateListViewItems();
}
//tilelistView.ItemsSelected = SetSelection;
// Do gizmos and stuff
var cursorRay = Gizmo.CurrentRay;
var tr = SceneEditorSession.Active.Scene.Trace
.Ray( cursorRay, 5000 )
.UseRenderMeshes( true )
.UsePhysicsWorld( false )
.WithoutTags( "gridtile" )
.Run();
var boxtr = SceneEditorSession.Active.Scene.Trace
.Ray( cursorRay, 5000 )
.UsePhysicsWorld( true )
.WithoutTags( "gridtile" )
.Run();
if ( !boxtr.Hit )
{
Vector3 rayOrigin = cursorRay.Position;
Vector3 rayDirection = cursorRay.Forward;
boxtr = ProjectRayOntoGroundPlane( rayOrigin, rayDirection, 0 );
}
GroundGizmo( cursorRay );
HandleRotation();
PaintGizmos( tr );
DecalScale();
if ( Gizmo.IsShiftPressed && SelectedObject is null )
{
projectedPoint = ProjectRayOntoGroundPlane( cursorRay.Position, cursorRay.Forward, floors );
if ( Gizmo.WasLeftMousePressed && CurrentPaintMode != PaintMode.Duplicate )
{
var snappedPosition = projectedPoint.EndPosition;
startSelectionPoint = snappedPosition;
isSelecting = true;
}
else if ( isSelecting )
{
var snappedPosition = projectedPoint.EndPosition;
endSelectionPoint = snappedPosition;
switch ( Axis )
{
case GroundAxis.X:
endSelectionPoint.x += FloorHeight;
break;
case GroundAxis.Y:
endSelectionPoint.y += FloorHeight;
break;
default:
endSelectionPoint.z += FloorHeight;
break;
}
if ( Gizmo.WasLeftMouseReleased )
{
var bbox = new BBox( startSelectionPoint, endSelectionPoint );
SelectObjectsInBox( bbox );
isSelecting = false;
}
}
if ( Application.IsKeyDown( KeyCode.F ) && !_prevFilled )
{
FillSelectionWithTiles( startSelectionPoint, endSelectionPoint );
isSelecting = false;
}
_prevFilled = Application.IsKeyDown( KeyCode.F );
if ( Application.IsKeyDown( KeyCode.G ) && !_prevGridded )
{
ShouldSnapToGrid = !ShouldSnapToGrid;
}
_prevGridded = Application.IsKeyDown( KeyCode.G );
if ( Application.IsKeyDown( KeyCode.R ) )
{
foreach ( var obj in SelectedGroupObjects )
{
obj.Destroy();
}
SelectedGroupObjects.Clear();
selectedamount.Text = $"Selected: {SelectedGroupObjects.Count}";
}
if ( Application.IsKeyDown( KeyCode.T ) )
{
if ( CurrentPaintMode != PaintMode.Duplicate )
{
LastPaintMode = CurrentPaintMode;
}
foreach ( var obj in SelectedGroupObjects )
{
DuplicateObjectCollection.Add( new DuplicatedItems()
{
gameObject = obj,
position = obj.Transform.Position,
rotation = obj.Transform.Rotation
} );
}
EndGameObjectGizmo();
CurrentPaintMode = PaintMode.Duplicate;
}
if ( CurrentPaintMode == PaintMode.Duplicate && DuplicateObjectCollection != null )
{
HandleDuplicate(tr ,cursorRay );
}
if( Gizmo.WasLeftMousePressed && CurrentPaintMode == PaintMode.Duplicate )
{
HandlePlaceDuplicatedGroup( tr, cursorRay );
}
if ( Application.IsKeyDown( KeyCode.Z ) )
{
Axis = GroundAxis.Z;
gridRotation = Vector3.Up;
so.Delete();
so = null;
Grid( new Vector2( 16384, 16384 ), gridRotation, Gizmo.Settings.GridSpacing, SceneViewportWidget.LastSelected.State.GridOpacity );
}
else if ( Application.IsKeyDown( KeyCode.C ) )
{
Axis = GroundAxis.X;
gridRotation = Vector3.Left;
so.Delete();
so = null;
Grid( new Vector2( 16384, 16384 ), gridRotation, Gizmo.Settings.GridSpacing, SceneViewportWidget.LastSelected.State.GridOpacity );
}
else if ( Application.IsKeyDown( KeyCode.X ) )
{
Axis = GroundAxis.Y;
gridRotation = Vector3.Forward;
so.Delete();
so = null;
Grid( new Vector2( 16384, 16384 ), gridRotation, Gizmo.Settings.GridSpacing, SceneViewportWidget.LastSelected.State.GridOpacity );
}
FloorHeightShortCut();
if ( SelectedGroupObjects is not null )
{
using ( Gizmo.Scope( "Selection" ) )
{
foreach ( var obj in SelectedGroupObjects )
{
Gizmo.Draw.Color = Color.Red;
Gizmo.Draw.LineBBox( obj.GetBounds() );
}
}
}
using ( Gizmo.Scope( "selection_box" ) )
{
var rect = new BBox( startSelectionPoint, endSelectionPoint );
Gizmo.Draw.Color = Color.Blue.WithAlpha( 0.25f );
Gizmo.Draw.SolidBox( rect );
Gizmo.Draw.Color = Color.Blue;
Gizmo.Draw.LineBBox( rect );
}
UpdateRotationSnapWithKeybind();
return;
}
else if ( isSelecting )
{
isSelecting = false;
}
else
{
if ( SelectedGroupObjects != null )
{
SelectedGroupObjects.Clear();
}
startSelectionPoint = Vector3.Zero;
endSelectionPoint = Vector3.Zero;
selectedamount.Text = $"Selected: {SelectedGroupObjects.Count}";
EndDuplicateGizmo();
}
if ( !Gizmo.IsShiftPressed && CurrentPaintMode == PaintMode.Duplicate )
{
//Log.Info( LastPaintMode );
CurrentPaintMode = LastPaintMode;
}
if ( !Gizmo.IsCtrlPressed )
{
if ( Gizmo.WasLeftMousePressed && CurrentPaintMode == PaintMode.Place )
{
HandlePlacement( tr, cursorRay );
}
else if ( Gizmo.WasLeftMousePressed && CurrentPaintMode == PaintMode.Decal )
{
HandleDecalPlace();
}
else if ( Gizmo.WasLeftMousePressed && CurrentPaintMode == PaintMode.Remove )
{
HandleRemove( cursorRay );
}
if ( Gizmo.WasLeftMousePressed && CurrentPaintMode == PaintMode.Move )
{
HandleGetMove( cursorRay );
}
else if ( Gizmo.WasLeftMouseReleased && CurrentPaintMode == PaintMode.Move && SelectedObject is not null )
{
SelectedObject = null;
}
if ( Gizmo.IsLeftMouseDown && CurrentPaintMode == PaintMode.Move && SelectedObject is not null )
{
HandleMove( cursorRay );
}
if ( Gizmo.WasLeftMouseReleased && CurrentPaintMode == PaintMode.Copy && CopyObject == null )
{
HandleCopy( cursorRay );
}
if ( Gizmo.WasLeftMousePressed && CurrentPaintMode == PaintMode.Copy && CopyObject != null )
{
HandleCopyPlace( tr, cursorRay );
}
if ( Application.IsKeyDown( KeyCode.Escape ) )
{
CopyObject = null;
SelectedJsonObject = null;
//SelectedRandomJsonObject.Clear();
SelectedRandomJsonObject = null;
EndGameObjectGizmo();
}
}
if ( CurrentGameObjectCollection is not null )
{
CollectionGroupHighLight();
}
if ( !Gizmo.IsLeftMouseDown )
return;
}
void SetSelection( object o )
{
if ( o is JsonObject s )
{
SelectedJsonObject = s;
Log.Info( $"Selected {s}" );
}
}
private Action DoRotation( bool leftright, GroundAxis axis )
{
beenRotated = true;
float rotationIncrement = leftright ? rotationSnap : -rotationSnap;
return () =>
{
switch ( axis )
{
case GroundAxis.X:
rotation *= Rotation.FromAxis( Vector3.Right, rotationIncrement );
break;
case GroundAxis.Y:
rotation *= Rotation.FromAxis( Vector3.Forward, rotationIncrement );
break;
case GroundAxis.Z:
rotation *= Rotation.FromAxis( Vector3.Up, rotationIncrement );
break;
}
};
}
void SnapToClosest( GroundAxis axis )
{
var a = rotation.Angles();
switch (axis)
{
case GroundAxis.X:
a.pitch = a.pitch.SnapToGrid( rotationSnap );
break;
case GroundAxis.Y:
a.yaw = a.yaw.SnapToGrid( rotationSnap );
break;
case GroundAxis.Z:
a.roll = a.roll.SnapToGrid( rotationSnap );
break;
}
rotation = Rotation.From( a );
}
private Action DoFloors( int i )
{
return () =>
{
floors += i;
if ( i > 0 )
{
floorCount++;
}
else if ( i < 0 )
{
floorCount--;
}
};
}
public void CreateCollection( string name )
{
using var scope = SceneEditorSession.Scope();
var go = new GameObject( true, name );
go.Transform.Position = Vector3.Zero;
go.Tags.Add( "collection" );
GameObjectCollection.Add( go );
Log.Info( "Created collection" );
}
void ClearAll()
{
foreach ( var obj in Scene.GetAllObjects( false ).Where( x => x.Tags.Has( "gridtile" ) ).ToArray() )
{
obj.Destroy();
}
}
}