├── README.md.meta ├── package.json.meta ├── Editor.meta ├── Editor ├── MiddleMast.ScriptableObjectWizard.Editor.asmdef.meta ├── StringExtensions.cs.meta ├── ReflectionExtensions.cs.meta ├── ScriptableObjectWizardWindow.cs.meta ├── StringExtensions.cs ├── MiddleMast.ScriptableObjectWizard.Editor.asmdef ├── ReflectionExtensions.cs └── ScriptableObjectWizardWindow.cs ├── package.json └── README.md /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4a7535fa614f7d849aca7c6b6f0a301b 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0fa06602e434ae64a96f303eb8fa6cfd 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4cce52780baa67d43a01c95b94104f98 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/MiddleMast.ScriptableObjectWizard.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bb3e2b3e506bf324492059996978488b 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/StringExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: af80d1f7bb0f51e40ae1e45b40126e36 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/ReflectionExtensions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 377f184484d1f7148bc076f27e693317 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/ScriptableObjectWizardWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 82444a3abdfde704c8a542752186e95f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiddleMast.ScriptableObjectWizard.Editor 4 | { 5 | public static class StringExtensions 6 | { 7 | public static bool Contains(this string source, string toCheck, StringComparison comp) 8 | { 9 | return source?.IndexOf(toCheck, comp) >= 0; 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Editor/MiddleMast.ScriptableObjectWizard.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MiddleMast.ScriptableObjectWizard.Editor", 3 | "rootNamespace": "", 4 | "references": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.middlemast.scriptableobjectwizard", 3 | "version": "1.0.3", 4 | "displayName": "ScriptableObject Wizard", 5 | "description": "A very simple editor tool that allows the cration of any ScriptableObject asset you want.", 6 | "unity": "2019.1", 7 | "keywords": [ 8 | "ScriptableObject", 9 | "Wizard", 10 | "MiddleMast" 11 | ], 12 | "author": { 13 | "name": "MiddleMast - Giovanni Zambiasi", 14 | "email": "giovanni.zambiasi@gmail.com", 15 | "url": "https://github.com/GiovanniZambiasi" 16 | }, 17 | "type": "tool" 18 | } -------------------------------------------------------------------------------- /Editor/ReflectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | 4 | namespace MiddleMast.ScriptableObjectWizard.Editor 5 | { 6 | public static class ReflectionExtensions 7 | { 8 | public static void GetAllConcreteInheritors(this System.Type type, Assembly assembly, List inheritors) 9 | { 10 | var __types = assembly.GetTypes(); 11 | 12 | for (int __j = 0; __j < __types.Length; __j++) 13 | { 14 | var __type = __types[__j]; 15 | if (__type.IsClass && !__type.IsAbstract && __type.IsSubclassOf(type)) 16 | inheritors.Add(__type); 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ScriptableObjectWizard 2 | 3 | This is a tool for anyone who's tired of having to add [CreateAssetMenu] to all scriptable objects they make. A simple editor window that allows you to create an instance of any concrete type that inherits from ScriptableObject. It features an assembly filter and a search function, to speed up the asset creation process. 4 | 5 | ## Installation 6 | 7 | ### OpenUPM 8 | [![openupm](https://img.shields.io/npm/v/com.middlemast.scriptableobjectwizard?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.middlemast.scriptableobjectwizard/) 9 | 10 | ### Git URL 11 | - Install via [UPM](https://docs.unity3d.com/Manual/upm-ui-giturl.html) using the URL: ``https://github.com/GiovanniZambiasi/ScriptableObjectWizard.git`` 12 | 13 | # Usage 14 | 15 | The wizard is available via the MiddleMast/ScriptableObject Wizard option in the toolbar 16 | 17 | ![still](https://i.imgur.com/JyCxsml.png) 18 | 19 | Demo: https://youtu.be/QT1lSJCeqLc 20 | 21 | -------------------------------------------------------------------------------- /Editor/ScriptableObjectWizardWindow.cs: -------------------------------------------------------------------------------- 1 | // Created by Giovanni Tramarin Zambiasi 2 | // giovanni.zambiasi@gmail.com 3 | // 22/11/2020 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | using UnityEditor; 9 | using UnityEngine; 10 | 11 | namespace MiddleMast.ScriptableObjectWizard.Editor 12 | { 13 | public class ScriptableObjectWizardWindow : EditorWindow 14 | { 15 | const string AssembliesLabel = "Assemblies to include:"; 16 | 17 | class AssemblyConfig : IComparable 18 | { 19 | public Assembly assembly; 20 | public System.Type[] scriptableObjectTypes; 21 | public bool isEnabled; 22 | 23 | public AssemblyConfig(Assembly assembly, System.Type[] scriptableObjectTypes) 24 | { 25 | this.assembly = assembly; 26 | this.scriptableObjectTypes = scriptableObjectTypes; 27 | isEnabled = false; 28 | } 29 | 30 | public int CompareTo(AssemblyConfig other) 31 | { 32 | if (ReferenceEquals(this, other)) return 0; 33 | if (ReferenceEquals(null, other)) return 1; 34 | return assembly.FullName.CompareTo(other.assembly.FullName); 35 | } 36 | } 37 | 38 | [MenuItem("MiddleMast/ScriptableObject Wizard")] 39 | static void Create() 40 | { 41 | var window = EditorWindow.GetWindow(false, "ScriptableObject Wizard"); 42 | window.Show(); 43 | } 44 | 45 | List _activeTypes = new List(128); 46 | List _assemblyConfigs = new List(128); 47 | GUILayoutOption[] _areaOptions; 48 | GUILayoutOption[] _typeLabelOptions; 49 | Vector2 _assembliesScrollPosition; 50 | Vector2 _typesScrollPosition; 51 | string _searchFilter = string.Empty; 52 | bool _showAssemblyOptions = true; 53 | bool _showTypeOptions = true; 54 | 55 | void Awake() 56 | { 57 | var __position = position; 58 | __position.width = 1240; 59 | position = __position; 60 | 61 | _areaOptions = new[] 62 | { 63 | GUILayout.Width(__position.width / 2), 64 | }; 65 | 66 | _typeLabelOptions = new[] 67 | { 68 | GUILayout.MaxWidth(246f), 69 | }; 70 | 71 | FindAllConcreteScriptableObjectTypes(); 72 | } 73 | 74 | void OnGUI() 75 | { 76 | DrawHeader(); 77 | 78 | var __isInvalid = _assemblyConfigs == null || _assemblyConfigs.Count == 0; 79 | if (__isInvalid) 80 | { 81 | EditorGUILayout.HelpBox("Something is wrong. Please refresh", MessageType.Warning); 82 | } 83 | else 84 | { 85 | EditorGUILayout.BeginHorizontal(); 86 | 87 | DrawAssemblyPanelIfEnabled(); 88 | 89 | DrawCreationPanelfEnabled(); 90 | 91 | EditorGUILayout.EndHorizontal(); 92 | } 93 | } 94 | 95 | void DrawHeader() 96 | { 97 | EditorGUILayout.LabelField("Scriptable Object Wizard", EditorStyles.whiteLargeLabel); 98 | 99 | EditorGUILayout.Space(12); 100 | 101 | if (GUILayout.Button("Refresh Assemblies", GUILayout.MaxWidth(124f))) 102 | { 103 | FindAllConcreteScriptableObjectTypes(); 104 | } 105 | 106 | EditorGUILayout.Space(12); 107 | } 108 | 109 | bool MatchesSearchCriteria(Type type) 110 | => _searchFilter == string.Empty || type.Name.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase); 111 | 112 | void CreateAsset(System.Type type) 113 | { 114 | var __pathAndName = EditorUtility.SaveFilePanelInProject($"Create {type.Name}", type.Name, "asset", ""); 115 | 116 | var __object = ScriptableObject.CreateInstance(type); 117 | AssetDatabase.CreateAsset(__object, __pathAndName); 118 | 119 | Selection.activeObject = __object; 120 | } 121 | 122 | void DrawCreateOptionForType(System.Type type) 123 | { 124 | EditorGUILayout.BeginHorizontal(); 125 | 126 | EditorGUILayout.LabelField($"{type.Name}", _typeLabelOptions); 127 | 128 | if (GUILayout.Button("Create")) 129 | { 130 | CreateAsset(type); 131 | } 132 | 133 | EditorGUILayout.EndHorizontal(); 134 | } 135 | 136 | void DrawAssemblyOption(AssemblyConfig assemblyConfig) 137 | { 138 | var __wasEnabled = assemblyConfig.isEnabled; 139 | 140 | GUILayout.BeginHorizontal(); 141 | EditorGUILayout.LabelField($"{assemblyConfig.assembly.GetName().Name}"); 142 | assemblyConfig.isEnabled = EditorGUILayout.Toggle($"[{assemblyConfig.scriptableObjectTypes.Length.ToString()}] SO derived types", assemblyConfig.isEnabled); 143 | GUILayout.EndHorizontal(); 144 | 145 | var __isEnabled = assemblyConfig.isEnabled; 146 | 147 | if (__wasEnabled != __isEnabled) 148 | { 149 | if (__isEnabled) 150 | ActivateAssembly(assemblyConfig); 151 | else 152 | DeactivateAssembly(assemblyConfig); 153 | } 154 | } 155 | 156 | void DeactivateAssembly(AssemblyConfig assemblyConfig) 157 | { 158 | var __types = assemblyConfig.scriptableObjectTypes; 159 | for (var __i = 0; __i < __types.Length; __i++) 160 | { 161 | var __type = __types[__i]; 162 | _activeTypes.Remove(__type); 163 | } 164 | } 165 | 166 | void ActivateAssembly(AssemblyConfig assemblyConfig) 167 | { 168 | _activeTypes.AddRange(assemblyConfig.scriptableObjectTypes); 169 | } 170 | 171 | void SetAllAssemblies(bool isEnabled) 172 | { 173 | for (var __i = 0; __i < _assemblyConfigs.Count; __i++) 174 | { 175 | var __assembly = _assemblyConfigs[__i]; 176 | __assembly.isEnabled = isEnabled; 177 | if(isEnabled) 178 | ActivateAssembly(__assembly); 179 | else 180 | DeactivateAssembly(__assembly); 181 | } 182 | } 183 | 184 | void FindAllConcreteScriptableObjectTypes() 185 | { 186 | var __assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); 187 | 188 | _assemblyConfigs.Clear(); 189 | 190 | var __scriptableObjectType = typeof(ScriptableObject); 191 | 192 | for (var __i = 0; __i < __assemblies.Length; __i++) 193 | { 194 | _activeTypes.Clear(); 195 | 196 | var __assembly = __assemblies[__i]; 197 | 198 | __scriptableObjectType.GetAllConcreteInheritors(__assembly, _activeTypes); 199 | 200 | if (_activeTypes.Count == 0) 201 | continue; 202 | 203 | _assemblyConfigs.Add(new AssemblyConfig(__assembly, _activeTypes.ToArray())); 204 | } 205 | 206 | _assemblyConfigs.Sort(); 207 | 208 | _activeTypes.Clear(); 209 | } 210 | 211 | void DrawCreationPanelfEnabled() 212 | { 213 | EditorGUILayout.BeginVertical(EditorStyles.helpBox, _areaOptions); 214 | 215 | _showTypeOptions = EditorGUILayout.BeginFoldoutHeaderGroup(_showTypeOptions, "Available Types:"); 216 | 217 | if (_showTypeOptions) 218 | { 219 | DrawCreationPanel(); 220 | } 221 | 222 | EditorGUILayout.EndFoldoutHeaderGroup(); 223 | 224 | EditorGUILayout.EndVertical(); 225 | } 226 | 227 | void DrawCreationPanel() 228 | { 229 | if (_activeTypes.Count > 0) 230 | { 231 | EditorGUI.indentLevel++; 232 | 233 | DrawSearchBar(); 234 | 235 | _typesScrollPosition = EditorGUILayout.BeginScrollView(_typesScrollPosition, false, false); 236 | 237 | DrawCreationOptions(); 238 | 239 | EditorGUILayout.EndScrollView(); 240 | 241 | EditorGUI.indentLevel--; 242 | } 243 | else 244 | { 245 | EditorGUILayout.HelpBox($"No ScriptableObjects available to create in active assemblies. Try enabling different assemblies via the \"{AssembliesLabel}\" menu above", MessageType.Info); 246 | } 247 | } 248 | 249 | void DrawCreationOptions() 250 | { 251 | for (int __i = 0; __i < _activeTypes.Count; __i++) 252 | { 253 | var __type = _activeTypes[__i]; 254 | 255 | if (MatchesSearchCriteria(__type)) 256 | DrawCreateOptionForType(__type); 257 | } 258 | } 259 | 260 | void DrawSearchBar() 261 | { 262 | EditorGUILayout.BeginHorizontal(); 263 | 264 | EditorGUILayout.LabelField("Search:", GUILayout.Width(58f)); 265 | 266 | _searchFilter = EditorGUILayout.TextField(_searchFilter); 267 | 268 | if (GUILayout.Button("X", GUILayout.Width(24f))) 269 | { 270 | _searchFilter = string.Empty; 271 | } 272 | 273 | EditorGUILayout.EndHorizontal(); 274 | } 275 | 276 | void DrawAssemblyPanelIfEnabled() 277 | { 278 | EditorGUILayout.BeginVertical(EditorStyles.helpBox, _areaOptions); 279 | 280 | _showAssemblyOptions = EditorGUILayout.BeginFoldoutHeaderGroup(_showAssemblyOptions, AssembliesLabel); 281 | 282 | if (_showAssemblyOptions) 283 | { 284 | DrawAssemblyPanel(); 285 | } 286 | 287 | EditorGUILayout.EndFoldoutHeaderGroup(); 288 | 289 | EditorGUILayout.EndVertical(); 290 | } 291 | 292 | void DrawAssemblyPanel() 293 | { 294 | EditorGUI.indentLevel++; 295 | 296 | _assembliesScrollPosition = EditorGUILayout.BeginScrollView(_assembliesScrollPosition, false, false); 297 | 298 | for (var __i = 0; __i < _assemblyConfigs.Count; __i++) 299 | { 300 | var __assemblyData = _assemblyConfigs[__i]; 301 | 302 | DrawAssemblyOption(__assemblyData); 303 | } 304 | 305 | EditorGUILayout.EndScrollView(); 306 | 307 | EditorGUILayout.BeginHorizontal(); 308 | 309 | if (GUILayout.Button("Activate all", GUILayout.Width(128f))) 310 | { 311 | SetAllAssemblies(true); 312 | } 313 | if (GUILayout.Button("Deactivate all", GUILayout.Width(128f))) 314 | { 315 | SetAllAssemblies(false); 316 | } 317 | 318 | EditorGUILayout.EndHorizontal(); 319 | 320 | EditorGUI.indentLevel--; 321 | } 322 | } 323 | } --------------------------------------------------------------------------------