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+ [ MenuItem ( "Assets/Create/Toolbox/ScriptableObject Creation Wizard" , priority = 9 ) ]
70+ internal static void Initialize ( )
71+ {
72+ var window = GetWindow < ScriptableObjectCreationWizard > ( ) ;
73+ window . titleContent = new GUIContent ( "ScriptableObject Creation Window" ) ;
74+ window . Show ( ) ;
75+ }
76+
77+ private void DrawSettingsPanel ( )
78+ {
79+ EditorGUILayout . LabelField ( "Settings" , EditorStyles . boldLabel ) ;
80+ var rect = EditorGUILayout . GetControlRect ( true ) ;
81+ typeField . OnGui ( rect , true , OnTypeSelected , data . InstanceType ) ;
82+ if ( data . InstanceType == null )
83+ {
84+ return ;
85+ }
86+
87+ EditorGUI . BeginChangeCheck ( ) ;
88+ data . InstanceName = EditorGUILayout . TextField ( "Instance Name" , data . InstanceName ) ;
89+ data . InstancesCount = EditorGUILayout . IntField ( "Instances To Create" , data . InstancesCount ) ;
90+ var content = new GUIContent ( "Default Object" , "Will be used as a blueprint for all created ScriptableObjects." ) ;
91+ data . DefaultObject = EditorGUILayout . ObjectField ( content , data . DefaultObject , data . InstanceType , false ) ;
92+ if ( EditorGUI . EndChangeCheck ( ) )
93+ {
94+ OnWizardUpdate ( ) ;
95+ }
96+ }
97+
98+ private void CreateObjects ( )
99+ {
100+ CreateObjects ( data ) ;
101+ }
102+
103+ private void CreateObjects ( CreationData data )
104+ {
105+ data . Validate ( ) ;
106+ if ( data . InstanceType == null )
107+ {
108+ ToolboxEditorLog . LogWarning ( "Cannot create ScriptableObjects, picked type is null." ) ;
109+ return ;
110+ }
111+
112+ var assetPath = GetActiveFolderPath ( ) ;
113+ if ( string . IsNullOrEmpty ( assetPath ) )
114+ {
115+ ToolboxEditorLog . LogWarning ( "Cannot create ScriptableObjects, path cached from the Project Window is invalid." ) ;
116+ return ;
117+ }
118+
119+ var instancesCount = data . InstancesCount ;
120+ for ( var i = 0 ; i < instancesCount ; i ++ )
121+ {
122+ var instance = CreateObject ( data . InstanceType , data . DefaultObject ) ;
123+ CreateAsset ( instance , data . InstanceName , assetPath , i ) ;
124+ }
125+
126+ AssetDatabase . SaveAssets ( ) ;
127+ ToolboxEditorLog . LogInfo ( $ "New ScriptableObjects created ({ instancesCount } ), at path: { assetPath } .") ;
128+ }
129+
130+ private Object CreateObject ( Type targetType , Object defaultObject )
131+ {
132+ return defaultObject != null ? Instantiate ( defaultObject ) : CreateInstance ( targetType ) ;
133+ }
134+
135+ private void CreateAsset ( Object asset , string assetName , string assetPath , int index )
136+ {
137+ var instanceName = assetName + ( index > 0 ? $ " [{ index } ]" : string . Empty ) ;
138+ var instancePath = AssetDatabase . GenerateUniqueAssetPath ( Path . Combine ( assetPath , $ "{ instanceName } .asset") ) ;
139+ AssetDatabase . CreateAsset ( asset , instancePath ) ;
140+ }
141+
142+ private void OnTypeSelected ( Type type )
143+ {
144+ data . InstanceType = type ;
145+ var attribute = type ? . GetCustomAttribute < CreateAssetMenuAttribute > ( ) ;
146+ if ( attribute != null )
147+ {
148+ data . InstanceName = attribute . fileName ;
149+ }
150+ else
151+ {
152+ data . InstanceName = string . Empty ;
153+ }
154+ }
155+
156+ private static string GetActiveFolderPath ( )
157+ {
158+ var projectWindowUtilType = typeof ( ProjectWindowUtil ) ;
159+ var getActiveFolderPath = projectWindowUtilType . GetMethod ( "GetActiveFolderPath" , BindingFlags . Static | BindingFlags . NonPublic ) ;
160+ var obj = getActiveFolderPath . Invoke ( null , new object [ 0 ] ) ;
161+ var pathToCurrentFolder = obj . ToString ( ) ;
162+ return pathToCurrentFolder ;
163+ }
164+
165+ protected override void OnWizardCreate ( )
166+ {
167+ base . OnWizardCreate ( ) ;
168+ CreateObjects ( ) ;
169+ }
170+
171+ protected override void OnWizardUpdate ( )
172+ {
173+ base . OnWizardUpdate ( ) ;
174+ data . Validate ( ) ;
175+ }
176+
177+ protected override void OnWizardGui ( )
178+ {
179+ base . OnWizardGui ( ) ;
180+ using ( new EditorGUILayout . VerticalScope ( EditorStyles . helpBox ) )
181+ {
182+ DrawSettingsPanel ( ) ;
183+ }
184+ }
185+
186+ protected override bool CloseOnCreate => false ;
187+ }
188+ }
0 commit comments