Skip to content

Commit b628148

Browse files
authored
Merge pull request #56 from arimger/feature/scriptable-objects-creation-tool
Feature/scriptable objects creation tool
2 parents aeb191f + 23f2fff commit b628148

11 files changed

Lines changed: 319 additions & 11 deletions

Assets/Editor Toolbox/Editor/ToolboxEditorGui.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using UnityEditor;
44
using UnityEngine;
5+
using Object = UnityEngine.Object;
56

67
namespace Toolbox.Editor
78
{
@@ -206,16 +207,16 @@ public static void DrawMinMaxSlider(Rect rect, string label, ref float xValue, r
206207
public static void DrawMinMaxSlider(Rect rect, GUIContent label, ref float xValue, ref float yValue, float minValue, float maxValue)
207208
{
208209
rect = EditorGUI.PrefixLabel(rect, label);
209-
210+
210211
var fieldWidth = EditorGUIUtility.fieldWidth;
211212
var minFieldRect = new Rect(rect.xMin, rect.y, fieldWidth, rect.height);
212213
var maxFieldRect = new Rect(rect.xMax - fieldWidth, rect.y, fieldWidth, rect.height);
213214

214215
//set slider rect between min and max fields + additional padding
215216
var spacing = 8.0f;
216217
var sliderRect = Rect.MinMaxRect(minFieldRect.xMax + spacing,
217-
rect.yMin,
218-
maxFieldRect.xMin - spacing,
218+
rect.yMin,
219+
maxFieldRect.xMin - spacing,
219220
rect.yMax);
220221

221222
EditorGUI.BeginChangeCheck();
@@ -604,5 +605,31 @@ public static void CloseProperty()
604605
{
605606
EditorGUI.EndProperty();
606607
}
608+
609+
/// <summary>
610+
/// Displays all visible children (except the default script property) associated to the given <see cref="Object"/>.
611+
/// This method doesn't support Toolbox-based features.
612+
/// </summary>
613+
public static void DrawObjectProperties(Object instance)
614+
{
615+
using (SerializedObject serializedObject = new SerializedObject(instance))
616+
{
617+
var property = serializedObject.GetIterator();
618+
if (property.NextVisible(true))
619+
{
620+
if (!PropertyUtility.IsDefaultScriptProperty(property))
621+
{
622+
DrawNativeProperty(property.Copy());
623+
}
624+
625+
while (property.NextVisible(false))
626+
{
627+
DrawNativeProperty(property.Copy());
628+
}
629+
}
630+
631+
serializedObject.ApplyModifiedProperties();
632+
}
633+
}
607634
}
608635
}
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
using System;
2+
using System.IO;
3+
using System.Reflection;
4+
5+
using UnityEditor;
6+
using UnityEngine;
7+
using Object = UnityEngine.Object;
8+
9+
namespace Toolbox.Editor.Wizards
10+
{
11+
using Toolbox.Editor.Internal;
12+
13+
/// <summary>
14+
/// Utility window responsible for creation of <see cref="ScriptableObject"/>s.
15+
/// Allows to create multiple <see cref="ScriptableObject"/>s of the same <see cref="Type"/>.
16+
/// </summary>
17+
public class ScriptableObjectCreationWizard : ToolboxWizard
18+
{
19+
private class TypeConstraintScriptableObject : TypeConstraintStandard
20+
{
21+
public TypeConstraintScriptableObject() : base(typeof(ScriptableObject), TypeSettings.Class, false, false)
22+
{ }
23+
24+
public override bool IsSatisfied(Type type)
25+
{
26+
return Attribute.IsDefined(type, typeof(CreateAssetMenuAttribute)) && base.IsSatisfied(type);
27+
}
28+
}
29+
30+
[Serializable]
31+
private class CreationData
32+
{
33+
private bool IsDefaultObjectValid()
34+
{
35+
return DefaultObject != null && DefaultObject.GetType() == InstanceType;
36+
}
37+
38+
public void Validate()
39+
{
40+
if (string.IsNullOrEmpty(InstanceName))
41+
{
42+
InstanceName = InstanceType?.Name;
43+
}
44+
45+
InstancesCount = Mathf.Max(InstancesCount, 1);
46+
if (!IsDefaultObjectValid())
47+
{
48+
DefaultObject = null;
49+
}
50+
}
51+
52+
[field: SerializeField]
53+
public Type InstanceType { get; set; }
54+
[field: SerializeField]
55+
public string InstanceName { get; set; }
56+
[field: SerializeField]
57+
public int InstancesCount { get; set; } = 1;
58+
[field: SerializeField, InLineEditor]
59+
[field: Tooltip("Will be used as a blueprint for all created ScriptableObjects.")]
60+
public Object DefaultObject { get; set; }
61+
}
62+
63+
private static readonly TypeConstraintContext sharedConstraint = new TypeConstraintScriptableObject();
64+
private static readonly TypeAppearanceContext sharedAppearance = new TypeAppearanceContext(sharedConstraint, TypeGrouping.None, true);
65+
private static readonly TypeField typeField = new TypeField(sharedConstraint, sharedAppearance);
66+
67+
private readonly CreationData data = new CreationData();
68+
69+
private bool inspectDefaultObject;
70+
71+
[MenuItem("Assets/Create/Toolbox/ScriptableObject Creation Wizard", priority = 5)]
72+
internal static void Initialize()
73+
{
74+
var window = GetWindow<ScriptableObjectCreationWizard>();
75+
window.titleContent = new GUIContent("ScriptableObject Creation Window");
76+
window.Show();
77+
}
78+
79+
private void DrawSettingsPanel()
80+
{
81+
EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
82+
var rect = EditorGUILayout.GetControlRect(true);
83+
typeField.OnGui(rect, true, OnTypeSelected, data.InstanceType);
84+
if (data.InstanceType == null)
85+
{
86+
return;
87+
}
88+
89+
EditorGUI.BeginChangeCheck();
90+
data.InstanceName = EditorGUILayout.TextField(Style.nameContent, data.InstanceName);
91+
data.InstancesCount = EditorGUILayout.IntField(Style.countContent, data.InstancesCount);
92+
var assignedInstance = EditorGUILayout.ObjectField(Style.objectContent, data.DefaultObject, data.InstanceType, false);
93+
if (assignedInstance != null)
94+
{
95+
inspectDefaultObject = GUILayout.Toggle(inspectDefaultObject,
96+
Style.foldoutContent, Style.foldoutStyle, Style.foldoutOptions);
97+
}
98+
else
99+
{
100+
inspectDefaultObject = false;
101+
}
102+
103+
if (inspectDefaultObject)
104+
{
105+
using (new EditorGUILayout.VerticalScope(Style.backgroundStyle))
106+
{
107+
ToolboxEditorGui.DrawObjectProperties(assignedInstance);
108+
}
109+
}
110+
111+
data.DefaultObject = assignedInstance;
112+
if (EditorGUI.EndChangeCheck())
113+
{
114+
OnWizardUpdate();
115+
}
116+
}
117+
118+
private void CreateObjects()
119+
{
120+
CreateObjects(data);
121+
}
122+
123+
private void CreateObjects(CreationData data)
124+
{
125+
data.Validate();
126+
if (data.InstanceType == null)
127+
{
128+
ToolboxEditorLog.LogWarning("Cannot create ScriptableObjects, picked type is null.");
129+
return;
130+
}
131+
132+
var assetPath = GetActiveFolderPath();
133+
if (string.IsNullOrEmpty(assetPath))
134+
{
135+
ToolboxEditorLog.LogWarning("Cannot create ScriptableObjects, path cached from the Project Window is invalid.");
136+
return;
137+
}
138+
139+
var instancesCount = data.InstancesCount;
140+
for (var i = 0; i < instancesCount; i++)
141+
{
142+
var instance = CreateObject(data.InstanceType, data.DefaultObject);
143+
CreateAsset(instance, data.InstanceName, assetPath, i);
144+
}
145+
146+
AssetDatabase.SaveAssets();
147+
ToolboxEditorLog.LogInfo($"New ScriptableObjects created ({instancesCount}), at path: {assetPath}.");
148+
}
149+
150+
private Object CreateObject(Type targetType, Object defaultObject)
151+
{
152+
return defaultObject != null ? Instantiate(defaultObject) : CreateInstance(targetType);
153+
}
154+
155+
private void CreateAsset(Object asset, string assetName, string assetPath, int index)
156+
{
157+
var instanceName = assetName + (index > 0 ? $" [{index}]" : string.Empty);
158+
var instancePath = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(assetPath, $"{instanceName}.asset"));
159+
AssetDatabase.CreateAsset(asset, instancePath);
160+
}
161+
162+
private void OnTypeSelected(Type type)
163+
{
164+
data.InstanceType = type;
165+
var attribute = type?.GetCustomAttribute<CreateAssetMenuAttribute>();
166+
if (attribute != null)
167+
{
168+
data.InstanceName = attribute.fileName;
169+
}
170+
else
171+
{
172+
data.InstanceName = string.Empty;
173+
}
174+
}
175+
176+
private static string GetActiveFolderPath()
177+
{
178+
var projectWindowUtilType = typeof(ProjectWindowUtil);
179+
var getActiveFolderPath = projectWindowUtilType.GetMethod("GetActiveFolderPath", BindingFlags.Static | BindingFlags.NonPublic);
180+
var obj = getActiveFolderPath.Invoke(null, new object[0]);
181+
var pathToCurrentFolder = obj.ToString();
182+
return pathToCurrentFolder;
183+
}
184+
185+
protected override void OnWizardCreate()
186+
{
187+
base.OnWizardCreate();
188+
CreateObjects();
189+
}
190+
191+
protected override void OnWizardUpdate()
192+
{
193+
base.OnWizardUpdate();
194+
data.Validate();
195+
}
196+
197+
protected override void OnWizardGui()
198+
{
199+
base.OnWizardGui();
200+
using (new EditorGUILayout.VerticalScope(Style.backgroundStyle))
201+
{
202+
DrawSettingsPanel();
203+
}
204+
}
205+
206+
protected override bool CloseOnCreate => false;
207+
208+
private static class Style
209+
{
210+
internal static readonly GUIStyle backgroundStyle;
211+
internal static readonly GUIStyle foldoutStyle;
212+
213+
internal static readonly GUIContent nameContent = new GUIContent("Instance Name");
214+
internal static readonly GUIContent countContent = new GUIContent("Instances To Create", "Indicates how many instances will be created.");
215+
internal static readonly GUIContent objectContent = new GUIContent("Default Object", "Will be used as a blueprint for all created ScriptableObjects.");
216+
internal static readonly GUIContent foldoutContent = new GUIContent("Inspect", "Show/Hide Properties");
217+
218+
internal static readonly GUILayoutOption[] foldoutOptions = new GUILayoutOption[]
219+
{
220+
GUILayout.Width(60.0f)
221+
};
222+
223+
static Style()
224+
{
225+
backgroundStyle = new GUIStyle(EditorStyles.helpBox)
226+
{
227+
padding = new RectOffset(13, 13, 8, 8)
228+
};
229+
foldoutStyle = new GUIStyle(EditorStyles.miniButton)
230+
{
231+
#if UNITY_2019_3_OR_NEWER
232+
fontSize = 10,
233+
#else
234+
fontSize = 9,
235+
#endif
236+
alignment = TextAnchor.MiddleCenter
237+
};
238+
}
239+
}
240+
}
241+
}

Assets/Editor Toolbox/Editor/Wizards/ScriptableObjectCreationWizard.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Editor Toolbox/Editor/Windows/ToolboxWizard.cs renamed to Assets/Editor Toolbox/Editor/Wizards/ToolboxWizard.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ private void OnGUI()
4040
if (HandleCreateButton())
4141
{
4242
OnWizardCreate();
43-
Close();
44-
GUIUtility.ExitGUI();
43+
if (CloseOnCreate)
44+
{
45+
Close();
46+
GUIUtility.ExitGUI();
47+
}
4548
}
4649

4750
GUI.enabled = true;
@@ -60,6 +63,11 @@ private void PrepareEditor()
6063

6164
targetEditor = Editor.CreateEditor(this);
6265
targetEditor.hideFlags = HideFlags.HideAndDontSave;
66+
if (targetEditor is ToolboxEditor toolboxEditor)
67+
{
68+
toolboxEditor.IgnoreProperty(PropertyUtility.Defaults.scriptPropertyName);
69+
}
70+
6371
OnWizardUpdate();
6472
}
6573

@@ -88,6 +96,8 @@ public static T DisplayWizard<T>(string title) where T : ToolboxWizard
8896
return GetWindow<T>(true, title);
8997
}
9098

91-
protected bool IsValid { get; set; } = true;
99+
protected virtual bool IsValid { get; set; } = true;
100+
101+
protected virtual bool CloseOnCreate => true;
92102
}
93103
}

Assets/Editor Toolbox/Editor/Windows/ToolboxWizard.cs.meta renamed to Assets/Editor Toolbox/Editor/Wizards/ToolboxWizard.cs.meta

File renamed without changes.

Assets/Editor Toolbox/README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ Available features are divided into three groups:
6969
Each module is described in its respective section.
7070

7171
If you want to keep your custom settings between UET versions, create your own settings file:
72-
```Create/Editor Toolbox/Settings```
72+
```
73+
Create/Editor Toolbox/Settings
74+
```
7375

7476
## Attributes & Drawers <a name="drawers"></a>
7577

@@ -815,3 +817,10 @@ public static class MyEditorUtility
815817
Copy and paste all components from/to particular GameObject.
816818

817819
![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/utils.png)
820+
821+
Create multiple ScriptableObjects at once.
822+
```
823+
Assets/Create/Toolbox/ScriptableObject Creation Wizard
824+
```
825+
826+
![inspector](https://github.com/arimger/Unity-Editor-Toolbox/blob/develop/Docs/createso.png)

Assets/Examples/Scripts/SampleBehaviour6.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class SampleBehaviour6 : MonoBehaviour
1313
public ClassWithInterfaceBase var2;
1414
[SerializeReference, ReferencePicker(ParentType = typeof(ClassWithInterface2))]
1515
public ClassWithInterfaceBase var3;
16-
[SerializeField, SerializeReference, ReferencePicker]
16+
[SerializeReference, ReferencePicker]
1717
public Interface1[] vars;
1818
#endif
1919

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
using UnityEngine;
1+
using UnityEngine;
22

3-
[CreateAssetMenu]
3+
[CreateAssetMenu(fileName = "Sample Scriptable Object")]
44
public class SampleScriptableObject : ScriptableObject
55
{
66
public bool var1;
77
[EnableIf(nameof(var1), true)]
88
public int var2;
9+
public string[] vars;
910
}

Docs/createso.png

11.7 KB
Loading

0 commit comments

Comments
 (0)