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+ }
0 commit comments