UnitTests/placeobj.cs
using Editor;
using Editor.Inspectors;
using Editor.MapEditor;
using Sandbox;
using Sandbox.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
[EditorTool] // this class is an editor tool
[Title( "Object placement tool" )] // title of your tool
[Icon( "add_circle" )]
[Description( "Shortcut : Shift+E" )]
[Group( "0" )]
[Shortcut( "editortool.placeobj", "Shift+E" )] // keyboard shortcut
public class PlaceObj : EditorTool
{
IntProperty deltaFloat;
Vector3 placePos;
Vector3 placeOffsetPos;
Rotation normalRot;
public GameObject prefabTemplate;
public float placeDeltaDistance;
public bool useSurfaceNormal;
public bool showOnlyCustom;
public Checkbox surfSwitch;
public Checkbox customSwitch;
PrefabFile prefabItem;
WidgetWindow window;
Label currentPrefabText;
public override void OnEnabled()
{
AllowGameObjectSelection = false;
Selection.Clear();
// create a widget window. This is a window that
// can be dragged around in the scene view
window = new WidgetWindow( SceneOverlay );
window.Layout = Layout.Column();
window.WindowTitle = "Select object";
window.Layout.Margin = 4;
window.AcceptDrops = false;
window.FixedWidth = 210f;
window.Icon = "add_circle";
window.IsDraggable = false;
surfSwitch = new Checkbox( "Align to surface", window );
window.Layout.Add( surfSwitch );
window.Layout.AddSpacingCell( 8 );
customSwitch = new Checkbox( "Show only custom prefabs", window );
window.Layout.Add( customSwitch );
window.Layout.AddSpacingCell( 8 );
//Create float for surface distance offset
deltaFloat = window.Layout.Add( new IntProperty( window ) { HighlightColor = Theme.Yellow, Icon = "height", ToolTip = "Distance from surface", Value = 8 } );
window.Layout.AddSpacingCell( 8 );
var button = new Button( "Open all" );
button.Pressed = () => OpenAllFunctions(button.ScreenPosition);
currentPrefabText = new Label() { Text = "none" };
window.Layout.Add( currentPrefabText );
// Add the button to the window's layout
window.Layout.Add( button );
// Calling this function means that when your tool is deleted,
// ui will get properly deleted too. If you don't call this and
// you don't delete your UI in OnDisabled, it'll hang around forever.
AddOverlay( window, TextFlag.LeftTop, 10 );
}
//adds an empty gameobject at location, should replace it with library later
void AddObject(PrefabFile entry)
{
if ( entry != null )
{
using var scope = SceneEditorSession.Scope();
var go = SceneUtility.GetPrefabScene( entry )?.Clone();
go.BreakFromPrefab();
go.Name = entry.MenuPath.Split( '/' ).Last();
go.Transform.Local = new Transform( placeOffsetPos, normalRot );
Selection.Set( go );
}
}
void OpenAllFunctions(Vector2 pos)
{
showOnlyCustom = customSwitch.Value;
var menu = new Menu();
if ( customSwitch.Value == false )
{
var prefabs = AssetSystem.All.Where( x => x.AssetType.FileExtension == "prefab" )
.Where( x => x.RelativePath.StartsWith( "templates/gameobject/" ) )
.Select( x => x.LoadResource<PrefabFile>() )
.Where( x => x.ShowInMenu )
.OrderByDescending( x => x.MenuPath.Count( x => x == '/' ) )
.ThenBy( x => x.MenuPath )
.ToArray();
foreach ( var entry in prefabs )
{
menu.AddOption( entry.MenuPath.Split( '/' ), entry.MenuIcon, () =>
{
using var scope = SceneEditorSession.Scope();
prefabItem = entry;
currentPrefabText.Text = entry.ToString();
} );
}
}
var CustomPrefabs = AssetSystem.All.Where( x => x.AssetType.FileExtension == "prefab" )
.Where( x => x.RelativePath.StartsWith( "prefabs/templates" ) )
.Select( x => x.LoadResource<PrefabFile>() )
.Where( x => x.ShowInMenu )
.OrderByDescending( x => x.MenuPath.Count( x => x == '/' ) )
.ThenBy( x => x.MenuPath )
.ToArray();
foreach ( var entry in CustomPrefabs )
{
menu.AddOption( entry.MenuPath.Split( '/' ), entry.MenuIcon, () =>
{
using var scope = SceneEditorSession.Scope();
prefabItem = entry;
currentPrefabText.Text = entry.ToString();
} );
}
menu.OpenAt( pos, false );
}
public override void OnUpdate()
{
placeDeltaDistance = deltaFloat.Value;
useSurfaceNormal = surfSwitch.Value;
var tr = Scene.Trace.Ray( Gizmo.CurrentRay, 5000 )
.UseRenderMeshes( true )
.UsePhysicsWorld( false )
.Run();
if ( tr.Hit )
{
using ( Gizmo.Scope( "cursor" ) )
{
placePos = tr.HitPosition;
placePos = placePos.SnapToGrid( EditorScene.GizmoInstance.Settings.GridSpacing, true, true, true );
Gizmo.Transform = new Transform( placePos, Rotation.LookAt( tr.Normal ) );
Gizmo.Draw.Color = Gizmo.Colors.Blue;
Gizmo.Draw.LineCircle( 0, 4f );
Gizmo.Draw.Color = Gizmo.Colors.Green;
placeOffsetPos = placePos + tr.Normal * placeDeltaDistance;
if ( useSurfaceNormal )
{
normalRot = Rotation.LookAt( tr.Normal );
}
else
{
normalRot = Angles.Zero;
}
Gizmo.Transform = new Transform( placeOffsetPos, Rotation.LookAt( tr.Normal ) );
Gizmo.Draw.LineBBox( BBox.FromPositionAndSize( 0, 4f ) );
if ( Gizmo.HasClicked ) AddObject(prefabItem);
}
}
}
}