Search Unity

Custom "Add Component" like Button

Discussion in 'Immediate Mode GUI (IMGUI)' started by Spy-Shifty, Nov 5, 2016.

  1. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    Hi,
    I have a scriptable object "ManagerXY" that can hold other scriptable objects "BaseComponent"

    No I want to add a "Add Component" like Button to the "ManagerXY" scriptable object so that I can add
    scriptable objects of type "BaseComponent" to my ManagerXY asset.

    My question is:
    Is where a editor internal method to a generic dialog for this or something like this?

    Add Behaviour Dialog.png

    Thanks in advance
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
  3. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    No That isn't what i want.

    I've created my own editor:

    AddEquipmentBehaviourWindow:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEditor;
    5. using System.Collections.Generic;
    6. using System;
    7. using System.IO;
    8. using System.Linq;
    9. using System.CodeDom.Compiler;
    10. using System.Reflection;
    11.  
    12. namespace Game {
    13.  
    14.     public class AddEquipmentBehaviourWindow : EditorWindow {
    15.  
    16.         static AddEquipmentBehaviourWindow _instance;
    17.         static Styles _styles;
    18.  
    19.         Equipment _obj;
    20.         Vector2 _scrollPosition;
    21.         string _className = "NewEquipmentBehaviourScript";
    22.         bool _activeParent = true;
    23.  
    24.  
    25.         string _searchString = string.Empty;
    26.  
    27.         const char UNITY_FOLDER_SEPARATOR = '/';
    28.         const  string SCRIPT_TEMPLATE_PATH = "ScriptTemplate";
    29.  
    30.         public static bool HasAssetToAdd() {
    31.             return !EquipmentBehaviourWindowBackup.Instance.addAsset;
    32.         }
    33.  
    34.         public static void Show(Rect rect, Equipment obj) {
    35.             if (_instance == null) {
    36.                 _instance = ScriptableObject.CreateInstance<AddEquipmentBehaviourWindow>();
    37.             }
    38.             _instance.Init(rect, obj);
    39.         }
    40.  
    41.         public static void Backup(Equipment obj) {
    42.             if (_instance == null) {
    43.                 _instance = ScriptableObject.CreateInstance<AddEquipmentBehaviourWindow>();
    44.             }
    45.             _instance._obj = obj;
    46.             if (EquipmentBehaviourWindowBackup.Instance.addAsset) {
    47.                 var script = AssetDatabase.LoadAssetAtPath<MonoScript>(EquipmentBehaviourWindowBackup.Instance.scriptPath);    
    48.                 //Debug.Log("GetClass: " + script.GetClass());
    49.                 if (_instance.CreateScriptInstance(script)) {
    50.                     EquipmentBehaviourWindowBackup.Instance.Reset();
    51.                 }
    52.             }
    53.         }
    54.  
    55.         private void Init(Rect rect, Equipment obj) {
    56.             var v2 = GUIUtility.GUIToScreenPoint(new Vector2(rect.x, rect.y));
    57.             rect.x = v2.x;
    58.             rect.y = v2.y;
    59.  
    60.             //CreateComponentTree();
    61.             ShowAsDropDown(rect, new Vector2(rect.width, 320f));
    62.             Focus();
    63.             wantsMouseMove = true;
    64.             _obj = obj;
    65.         }
    66.  
    67.         void OnGUI() {
    68.             if (_styles == null) {
    69.                 _styles = new Styles();
    70.             }
    71.             GUI.Label(new Rect(0.0f, 0.0f, this.position.width, this.position.height), GUIContent.none, _styles.background);
    72.  
    73.             //GUILayout.BeginHorizontal(GUI.skin.FindStyle("Toolbar"));
    74.             GUILayout.Space(7);
    75.             GUILayout.BeginHorizontal();
    76.             //GUILayout.FlexibleSpace();
    77.             EditorGUI.BeginDisabledGroup(!_activeParent);
    78.             _searchString = GUILayout.TextField(_searchString, GUI.skin.FindStyle("SearchTextField"));
    79.             var buttonStyle = _searchString == string.Empty ? GUI.skin.FindStyle("SearchCancelButtonEmpty") : GUI.skin.FindStyle("SearchCancelButton");
    80.             if (GUILayout.Button(string.Empty, buttonStyle)) {
    81.                 // Remove focus if cleared
    82.                 _searchString = string.Empty;
    83.                 GUI.FocusControl(null);
    84.             }
    85.             EditorGUI.EndDisabledGroup();
    86.             GUILayout.EndHorizontal();
    87.             //GUILayout.Space(9);
    88.  
    89.             if (_activeParent) {
    90.                 _className = _searchString;
    91.                 ListGUI();
    92.             } else {
    93.                 NewScriptGUI();
    94.             }
    95.  
    96.  
    97.            //EditorGUI.FocusTextInControl("ComponentSearch");
    98.         }
    99.  
    100.         void ListGUI() {
    101.             var rect = position;
    102.             rect.x = +1f;
    103.             rect.y = 30f;
    104.             rect.height -= 30f;
    105.             rect.width -= 2f;
    106.             GUILayout.BeginArea(rect);
    107.  
    108.             rect = GUILayoutUtility.GetRect(10f, 25f);
    109.             GUI.Label(rect, _searchString == string.Empty ? "Behaviour" : "Search", _styles.header);
    110.             _scrollPosition = GUILayout.BeginScrollView(_scrollPosition);
    111.             var scripts = Resources.FindObjectsOfTypeAll<MonoScript>();
    112.             var searchString = _searchString.ToLower();
    113.             foreach (var script in scripts) {
    114.                 if (!script || script.GetClass() == null || script.GetClass().BaseType != typeof(EquipmentBehaviour)) {
    115.                     continue;
    116.                 }
    117.                 if (searchString != string.Empty && !script.name.ToLower().Contains(searchString)) {
    118.                     continue;
    119.                 }
    120.  
    121.                 var buttonRect = GUILayoutUtility.GetRect(16f, 20f, GUILayout.ExpandWidth(true));
    122.                 if (GUI.Button(buttonRect, script.name, _styles.componentButton)) {
    123.                     CreateScriptInstance(script);
    124.                     Close();
    125.                 }
    126.             }
    127.             var rect2 = GUILayoutUtility.GetRect(16f, 20f, GUILayout.ExpandWidth(true));
    128.             if (GUI.Button(rect2, "New Script", _styles.componentButton)) {
    129.                 _activeParent = false;
    130.             }
    131.             GUI.Label(new Rect((float)((double)rect2.x + (double)rect2.width - 13.0), rect2.y + 4f, 13f, 13f), "", _styles.rightArrow);
    132.  
    133.             GUILayout.EndScrollView();
    134.             GUILayout.EndArea();
    135.         }
    136.  
    137.         void NewScriptGUI() {
    138.             var rect = position;
    139.             rect.x = +1f;
    140.             rect.y = 30f;
    141.             rect.height -= 30f;
    142.             rect.width -= 2f;
    143.             GUILayout.BeginArea(rect);
    144.  
    145.             rect = GUILayoutUtility.GetRect(10f, 25f);
    146.             GUI.Label(rect, "New Script", _styles.header);
    147.  
    148.             GUILayout.Label("Name", EditorStyles.label);
    149.             EditorGUI.FocusTextInControl("NewScriptName");
    150.             GUI.SetNextControlName("NewScriptName");
    151.             _className = EditorGUILayout.TextField(_className);
    152.  
    153.             EditorGUILayout.Space();
    154.             string error;
    155.             bool flag = CanCreate(out error);
    156.             if (!flag && _className != string.Empty) {
    157.                 GUILayout.Label(error, EditorStyles.helpBox);
    158.             }
    159.  
    160.  
    161.             EditorGUI.BeginDisabledGroup(!flag);
    162.             GUILayout.FlexibleSpace();
    163.             if (GUILayout.Button("Create and Add")) {
    164.                 GenerateAndLoadScript();  
    165.             }
    166.             EditorGUI.EndDisabledGroup();
    167.             GUILayout.EndArea();
    168.         }
    169.  
    170.         private bool CanCreate(out string error) {
    171.             error = string.Empty;
    172.             if (_className == string.Empty) {
    173.                 return false;
    174.             }
    175.             if (ClassAlreadyExists()) {
    176.                 error = "A class called \"" + _className + "\" already exists.";
    177.             } else if (ClassNameIsInvalid()) {
    178.                 error = "The script name may only consist of a-z, A-Z, 0-9, _.";
    179.             } else {
    180.                 return true;
    181.             }
    182.             return false;
    183.         }
    184.  
    185.         private bool ClassNameIsInvalid() {
    186.             return !CodeGenerator.IsValidLanguageIndependentIdentifier(_className);
    187.         }
    188.  
    189.         private bool ClassAlreadyExists() {
    190.             if (_className == string.Empty)
    191.                 return false;
    192.             return ClassExists(_className);
    193.         }
    194.  
    195.         private bool ClassExists(string className) {
    196.             return AppDomain.CurrentDomain.GetAssemblies().Any((x) =>
    197.                  x.GetFiles().Any((y) => y.Name == className));
    198.         }
    199.  
    200.         string PathCombine(params string[] paths) {
    201.             if (paths.Length < 2) {
    202.                 throw new ArgumentException("Argument must contain at least 2 strings to combine.");
    203.             }
    204.  
    205.             var combinedPath = _PathCombine(paths[0], paths[1]);
    206.             var restPaths = new string[paths.Length - 2];
    207.  
    208.             Array.Copy(paths, 2, restPaths, 0, restPaths.Length);
    209.             foreach (var path in restPaths) combinedPath = _PathCombine(combinedPath, path);
    210.  
    211.             return combinedPath;
    212.         }
    213.  
    214.         string _PathCombine (string head, string tail) {
    215.             if (!head.EndsWith(UNITY_FOLDER_SEPARATOR.ToString())) {
    216.                 head = head + UNITY_FOLDER_SEPARATOR;
    217.             }
    218.  
    219.             if (string.IsNullOrEmpty(tail)) {
    220.                 return head;
    221.             }
    222.  
    223.             if (tail.StartsWith(UNITY_FOLDER_SEPARATOR.ToString())) {
    224.                 tail = tail.Substring(1);
    225.             }
    226.  
    227.             return Path.Combine(head, tail);
    228.         }
    229.  
    230.         void CopyFileFromGlobalToLocal(string absoluteSourceFilePath, string localTargetFilePath) {
    231.             var parentDirectoryPath = Path.GetDirectoryName(localTargetFilePath);
    232.             Directory.CreateDirectory(parentDirectoryPath);
    233.             //File.Copy(absoluteSourceFilePath, localTargetFilePath, true);
    234.             var text = File.ReadAllText(absoluteSourceFilePath);
    235.             text = text.Replace("MyEquipmentBehaviour", _className);
    236.             File.WriteAllText(localTargetFilePath, text);
    237.         }
    238.  
    239.         void GenerateAndLoadScript() {
    240.             var editorPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this)));
    241.             var sourceFileName = PathCombine(editorPath, PathCombine(SCRIPT_TEMPLATE_PATH, "MyEquipmentBehaviour.cs.template"));
    242.             var destinationPath = PathCombine("Assets", _className + ".cs");
    243.  
    244.             if (string.IsNullOrEmpty(sourceFileName)) {
    245.                 return;
    246.             }
    247.             var backup = EquipmentBehaviourWindowBackup.Instance;
    248.             backup.addAsset = true;
    249.             backup.scriptPath = destinationPath;
    250.             EditorUtility.SetDirty(backup);
    251.  
    252.             CopyFileFromGlobalToLocal(sourceFileName, destinationPath);
    253.             AssetDatabase.ImportAsset(destinationPath);
    254.             AssetDatabase.Refresh();
    255.             Close();
    256.             //var script = AssetDatabase.LoadAssetAtPath<MonoScript>(destinationPath);
    257.             //Debug.Log("GetClass: " + script.GetClass());
    258.             //CreateScriptInstance(script);
    259.  
    260.         }
    261.  
    262.         bool CreateScriptInstance(MonoScript script) {
    263.             if (!script || script.GetClass() == null) {
    264.                 return false;
    265.             }
    266.             //var instance = Activator.CreateInstance(script.GetClass()) as EquipmentBehaviour;
    267.             var instance = ScriptableObject.CreateInstance(script.GetClass().Name) as EquipmentBehaviour;
    268.             _obj.AddBehaviour(instance);
    269.             AssetDatabase.AddObjectToAsset(instance, _obj);
    270.             EditorUtility.SetDirty(_obj);
    271.             return true;
    272.         }
    273.  
    274.         private class Styles {
    275.             public GUIStyle header = new GUIStyle((GUIStyle)"In BigTitle");
    276.             public GUIStyle componentButton = new GUIStyle((GUIStyle)"PR Label");
    277.             public GUIStyle background = (GUIStyle)"grey_border";
    278.             public GUIStyle previewBackground = (GUIStyle)"PopupCurveSwatchBackground";
    279.             public GUIStyle previewHeader = new GUIStyle(EditorStyles.label);
    280.             public GUIStyle previewText = new GUIStyle(EditorStyles.wordWrappedLabel);
    281.             public GUIStyle rightArrow = (GUIStyle)"AC RightArrow";
    282.             public GUIStyle leftArrow = (GUIStyle)"AC LeftArrow";
    283.             public GUIStyle groupButton;
    284.  
    285.             public Styles() {
    286.                 this.header.font = EditorStyles.boldLabel.font;
    287.                 this.componentButton.alignment = TextAnchor.MiddleLeft;
    288.                 this.componentButton.padding.left -= 15;
    289.                 this.componentButton.fixedHeight = 20f;
    290.                 this.groupButton = new GUIStyle(this.componentButton);
    291.                 this.groupButton.padding.left += 17;
    292.                 this.previewText.padding.left += 3;
    293.                 this.previewText.padding.right += 3;
    294.                 ++this.previewHeader.padding.left;
    295.                 this.previewHeader.padding.right += 3;
    296.                 this.previewHeader.padding.top += 3;
    297.                 this.previewHeader.padding.bottom += 2;
    298.             }
    299.         }
    300.     }
    301. }
    EquipmentBehaviourWindowBackup:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using System;
    5.  
    6. namespace Game {
    7.     [Serializable]
    8.     class EquipmentBehaviourWindowBackup : ScriptableObject {
    9.         public string scriptPath;
    10.         public bool addAsset;
    11.  
    12.         public static EquipmentBehaviourWindowBackup Instance {
    13.             get {
    14.                 var objs = Resources.FindObjectsOfTypeAll<EquipmentBehaviourWindowBackup>();
    15.                 if (objs.Length == 0 || objs[0] == null) {
    16.                     return ScriptableObject.CreateInstance<EquipmentBehaviourWindowBackup>();
    17.                 }
    18.                 return objs[0];
    19.             }
    20.         }
    21.  
    22.         void OnEnable() {
    23.             hideFlags = HideFlags.HideAndDontSave;
    24.         }
    25.  
    26.         public void Reset() {
    27.             addAsset = false;
    28.             scriptPath = string.Empty;
    29.         }
    30.     }
    31. }
    32.  
    Usage:
    Code (CSharp):
    1.  
    2.             EditorGUILayout.BeginHorizontal();
    3.             GUILayout.FlexibleSpace();
    4.             GUIStyle style = new GUIStyle(GUI.skin.button);
    5.             style.fontSize = 12;
    6.             style.fixedWidth = 230;
    7.             style.fixedHeight = 23;
    8.  
    9.             var rect = GUILayoutUtility.GetLastRect();
    10.  
    11.             var hasAssetToAdd = AddEquipmentBehaviourWindow.HasAssetToAdd();
    12.             EditorGUI.BeginDisabledGroup(!hasAssetToAdd);
    13.             if (GUILayout.Button("Add Behaviour", style)) {
    14.                 rect.y += 26f;
    15.                 rect.x += rect.width;
    16.                 rect.width = style.fixedWidth;
    17.                 AddEquipmentBehaviourWindow.Show(rect, serializedObject.targetObject as Equipment);
    18.                 Repaint();
    19.             }
    20.             EditorGUI.EndDisabledGroup();
    21.             GUILayout.FlexibleSpace();
    22.             EditorGUILayout.EndHorizontal();
    23.  
    24.             if (hasAssetToAdd) {
    25.                 AddEquipmentBehaviourWindow.Backup(serializedObject.targetObject as Equipment);
    26.             }
    27.  
    28.             }
    Result:
    EquipmentAssetEditor.png
     
    Last edited: Nov 7, 2016
  4. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    8,300
  5. Spy-Shifty

    Spy-Shifty

    Joined:
    May 5, 2011
    Posts:
    546
    This is a more generic Version off my Editor:

    AddScriptWindow.cs:
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using UnityEditor;
    5. using System.Collections.Generic;
    6. using System;
    7. using System.IO;
    8. using System.Linq;
    9. using System.CodeDom.Compiler;
    10. using System.Reflection;
    11. using System.Threading;
    12.  
    13. namespace UnityEditorAddon {
    14.  
    15.     public class AddScriptWindow : EditorWindow {
    16.  
    17.         static AddScriptWindow _instance;
    18.         static Styles _styles;
    19.  
    20.         Action<MonoScript> CreateScriptDelegate;
    21.         Func<MonoScript, bool> FilerScriptDelegate;
    22.  
    23.         Vector2 _scrollPosition;
    24.         string _className = "NewEquipmentBehaviourScript";
    25.         bool _activeParent = true;
    26.  
    27.  
    28.         string _searchString = string.Empty;
    29.  
    30.         const char UNITY_FOLDER_SEPARATOR = '/';
    31.         string _templatePath;
    32.  
    33.         public static bool HasAssetToAdd() {
    34.             return AddScriptWindowBackup.Instance.addAsset;
    35.         }
    36.  
    37.         public static void Show(Action<MonoScript> onCreateScript, Func<MonoScript, bool> onFilerScript, string templatePath) {
    38.  
    39.             if (_instance == null) {
    40.                 _instance = ScriptableObject.CreateInstance<AddScriptWindow>();
    41.             }
    42.  
    43.             EditorGUILayout.BeginHorizontal();
    44.             GUILayout.FlexibleSpace();
    45.  
    46.             GUIStyle style = new GUIStyle(GUI.skin.button);
    47.             style.fontSize = 12;
    48.             style.fixedWidth = 230;
    49.             style.fixedHeight = 23;
    50.  
    51.             var rect = GUILayoutUtility.GetLastRect();
    52.             var hasAssetToAdd = HasAssetToAdd();
    53.             EditorGUI.BeginDisabledGroup(hasAssetToAdd);
    54.             if (GUILayout.Button("Add Behaviour", style)) {
    55.                 rect.y += 26f;
    56.                 rect.x += rect.width;
    57.                 rect.width = style.fixedWidth;
    58.                 _instance.Init(rect, onCreateScript, onFilerScript, templatePath);
    59.                 _instance.Repaint();
    60.             }
    61.             EditorGUI.EndDisabledGroup();
    62.             GUILayout.FlexibleSpace();
    63.             EditorGUILayout.EndHorizontal();
    64.  
    65.             if (hasAssetToAdd) {
    66.                 Backup(onCreateScript);
    67.             }
    68.         }
    69.  
    70.         public static void Backup(Action<MonoScript> onCreateScript) {
    71.             if (_instance == null) {
    72.                 _instance = ScriptableObject.CreateInstance<AddScriptWindow>();
    73.             }
    74.             _instance.CreateScriptDelegate = onCreateScript;
    75.             if (AddScriptWindowBackup.Instance.addAsset) {
    76.                 var script = AssetDatabase.LoadAssetAtPath<MonoScript>(AddScriptWindowBackup.Instance.scriptPath);
    77.                 if (script.GetClass() == null) {
    78.                     return;
    79.                 }
    80.                 _instance.CreateScriptDelegate(script);
    81.                 AddScriptWindowBackup.Instance.Reset();    
    82.             }
    83.         }
    84.  
    85.         private void Init(Rect rect, Action<MonoScript> onCreateScript, Func<MonoScript, bool> onFilerScript, string templatePath) {
    86.             var v2 = GUIUtility.GUIToScreenPoint(new Vector2(rect.x, rect.y));
    87.             rect.x = v2.x;
    88.             rect.y = v2.y;
    89.  
    90.             //CreateComponentTree();
    91.             ShowAsDropDown(rect, new Vector2(rect.width, 320f));
    92.             Focus();
    93.             wantsMouseMove = true;
    94.             CreateScriptDelegate = onCreateScript;
    95.             FilerScriptDelegate = onFilerScript;
    96.             _templatePath = templatePath;
    97.         }
    98.  
    99.         void OnGUI() {
    100.             if (_styles == null) {
    101.                 _styles = new Styles();
    102.             }
    103.             GUI.Label(new Rect(0.0f, 0.0f, this.position.width, this.position.height), GUIContent.none, _styles.background);
    104.  
    105.             //GUILayout.BeginHorizontal(GUI.skin.FindStyle("Toolbar"));
    106.             GUILayout.Space(7);
    107.             GUILayout.BeginHorizontal();
    108.             //GUILayout.FlexibleSpace();
    109.             EditorGUI.BeginDisabledGroup(!_activeParent);
    110.             _searchString = GUILayout.TextField(_searchString, GUI.skin.FindStyle("SearchTextField"));
    111.             var buttonStyle = _searchString == string.Empty ? GUI.skin.FindStyle("SearchCancelButtonEmpty") : GUI.skin.FindStyle("SearchCancelButton");
    112.             if (GUILayout.Button(string.Empty, buttonStyle)) {
    113.                 // Remove focus if cleared
    114.                 _searchString = string.Empty;
    115.                 GUI.FocusControl(null);
    116.             }
    117.             EditorGUI.EndDisabledGroup();
    118.             GUILayout.EndHorizontal();
    119.             //GUILayout.Space(9);
    120.  
    121.             if (_activeParent) {
    122.                 _className = _searchString;
    123.                 ListGUI();
    124.             } else {
    125.                 NewScriptGUI();
    126.             }
    127.         }
    128.  
    129.         void ListGUI() {
    130.             var rect = position;
    131.             rect.x = +1f;
    132.             rect.y = 30f;
    133.             rect.height -= 30f;
    134.             rect.width -= 2f;
    135.             GUILayout.BeginArea(rect);
    136.  
    137.             rect = GUILayoutUtility.GetRect(10f, 25f);
    138.             GUI.Label(rect, _searchString == string.Empty ? "Behaviour" : "Search", _styles.header);
    139.             _scrollPosition = GUILayout.BeginScrollView(_scrollPosition);
    140.             var scripts = Resources.FindObjectsOfTypeAll<MonoScript>();
    141.             var searchString = _searchString.ToLower();
    142.             foreach (var script in scripts) {
    143.                 if (!script || script.GetClass() == null || !FilerScriptDelegate(script)) {
    144.                     continue;
    145.                 }
    146.                 if (searchString != string.Empty && !script.name.ToLower().Contains(searchString)) {
    147.                     continue;
    148.                 }
    149.  
    150.                 var buttonRect = GUILayoutUtility.GetRect(16f, 20f, GUILayout.ExpandWidth(true));
    151.                 if (GUI.Button(buttonRect, script.name, _styles.componentButton)) {
    152.                     CreateScriptDelegate(script);
    153.                     //CreateScriptInstance(script);
    154.                     Close();
    155.                 }
    156.             }
    157.             var rect2 = GUILayoutUtility.GetRect(16f, 20f, GUILayout.ExpandWidth(true));
    158.             if (GUI.Button(rect2, "New Script", _styles.componentButton)) {
    159.                 _activeParent = false;
    160.             }
    161.             GUI.Label(new Rect((float)((double)rect2.x + (double)rect2.width - 13.0), rect2.y + 4f, 13f, 13f), "", _styles.rightArrow);
    162.  
    163.             GUILayout.EndScrollView();
    164.             GUILayout.EndArea();
    165.         }
    166.  
    167.         void NewScriptGUI() {
    168.             var rect = position;
    169.             rect.x = +1f;
    170.             rect.y = 30f;
    171.             rect.height -= 30f;
    172.             rect.width -= 2f;
    173.             GUILayout.BeginArea(rect);
    174.  
    175.             rect = GUILayoutUtility.GetRect(10f, 25f);
    176.             GUI.Label(rect, "New Script", _styles.header);
    177.  
    178.             GUILayout.Label("Name", EditorStyles.label);
    179.             EditorGUI.FocusTextInControl("NewScriptName");
    180.             GUI.SetNextControlName("NewScriptName");
    181.             _className = EditorGUILayout.TextField(_className);
    182.  
    183.             EditorGUILayout.Space();
    184.             string error;
    185.             bool flag = CanCreate(out error);
    186.             if (!flag && _className != string.Empty) {
    187.                 GUILayout.Label(error, EditorStyles.helpBox);
    188.             }
    189.  
    190.  
    191.             EditorGUI.BeginDisabledGroup(!flag);
    192.             GUILayout.FlexibleSpace();
    193.             if (GUILayout.Button("Create and Add")) {
    194.                 GenerateAndLoadScript();  
    195.             }
    196.             EditorGUI.EndDisabledGroup();
    197.             GUILayout.EndArea();
    198.         }
    199.  
    200.         private bool CanCreate(out string error) {
    201.             error = string.Empty;
    202.             if (_className == string.Empty) {
    203.                 return false;
    204.             }
    205.             if (ClassAlreadyExists()) {
    206.                 error = "A class called \"" + _className + "\" already exists.";
    207.             } else if (ClassNameIsInvalid()) {
    208.                 error = "The script name may only consist of a-z, A-Z, 0-9, _.";
    209.             } else {
    210.                 return true;
    211.             }
    212.             return false;
    213.         }
    214.  
    215.         private bool ClassNameIsInvalid() {
    216.             return !CodeGenerator.IsValidLanguageIndependentIdentifier(_className);
    217.         }
    218.  
    219.         private bool ClassAlreadyExists() {
    220.             if (_className == string.Empty)
    221.                 return false;
    222.             return ClassExists(_className);
    223.         }
    224.  
    225.         private bool ClassExists(string className) {
    226.             return AppDomain.CurrentDomain.GetAssemblies().Any((x) =>
    227.                  x.GetFiles().Any((y) => y.Name == className));
    228.         }
    229.  
    230.         string PathCombine(params string[] paths) {
    231.             if (paths.Length < 2) {
    232.                 throw new ArgumentException("Argument must contain at least 2 strings to combine.");
    233.             }
    234.  
    235.             var combinedPath = _PathCombine(paths[0], paths[1]);
    236.             var restPaths = new string[paths.Length - 2];
    237.  
    238.             Array.Copy(paths, 2, restPaths, 0, restPaths.Length);
    239.             foreach (var path in restPaths) combinedPath = _PathCombine(combinedPath, path);
    240.  
    241.             return combinedPath;
    242.         }
    243.  
    244.         string _PathCombine(string head, string tail) {
    245.             if (!head.EndsWith(UNITY_FOLDER_SEPARATOR.ToString())) {
    246.                 head = head + UNITY_FOLDER_SEPARATOR;
    247.             }
    248.  
    249.             if (string.IsNullOrEmpty(tail)) {
    250.                 return head;
    251.             }
    252.  
    253.             if (tail.StartsWith(UNITY_FOLDER_SEPARATOR.ToString())) {
    254.                 tail = tail.Substring(1);
    255.             }
    256.  
    257.             return Path.Combine(head, tail);
    258.         }
    259.  
    260.         void CopyFileFromGlobalToLocal(string absoluteSourceFilePath, string localTargetFilePath) {
    261.             var parentDirectoryPath = Path.GetDirectoryName(localTargetFilePath);
    262.             Directory.CreateDirectory(parentDirectoryPath);
    263.             //File.Copy(absoluteSourceFilePath, localTargetFilePath, true);
    264.             var text = File.ReadAllText(absoluteSourceFilePath);
    265.             text = text.Replace("MyEquipmentBehaviour", _className);
    266.             File.WriteAllText(localTargetFilePath, text);
    267.         }
    268.  
    269.         void GenerateAndLoadScript() {
    270.             var sourceFileName = _templatePath;
    271.             var destinationPath = PathCombine("Assets", _className + ".cs");
    272.  
    273.             if (string.IsNullOrEmpty(sourceFileName)) {
    274.                 return;
    275.             }
    276.             var backup = AddScriptWindowBackup.Instance;
    277.             backup.addAsset = true;
    278.             backup.scriptPath = destinationPath;
    279.             EditorUtility.SetDirty(backup);
    280.  
    281.             CopyFileFromGlobalToLocal(sourceFileName, destinationPath);
    282.             AssetDatabase.ImportAsset(destinationPath);
    283.             AssetDatabase.Refresh();
    284.             Close();
    285.  
    286.         }
    287.  
    288.         private class Styles {
    289.             public GUIStyle header = new GUIStyle((GUIStyle)"In BigTitle");
    290.             public GUIStyle componentButton = new GUIStyle((GUIStyle)"PR Label");
    291.             public GUIStyle background = (GUIStyle)"grey_border";
    292.             public GUIStyle previewBackground = (GUIStyle)"PopupCurveSwatchBackground";
    293.             public GUIStyle previewHeader = new GUIStyle(EditorStyles.label);
    294.             public GUIStyle previewText = new GUIStyle(EditorStyles.wordWrappedLabel);
    295.             public GUIStyle rightArrow = (GUIStyle)"AC RightArrow";
    296.             public GUIStyle leftArrow = (GUIStyle)"AC LeftArrow";
    297.             public GUIStyle groupButton;
    298.  
    299.             public Styles() {
    300.                 this.header.font = EditorStyles.boldLabel.font;
    301.                 this.componentButton.alignment = TextAnchor.MiddleLeft;
    302.                 this.componentButton.padding.left -= 15;
    303.                 this.componentButton.fixedHeight = 20f;
    304.                 this.groupButton = new GUIStyle(this.componentButton);
    305.                 this.groupButton.padding.left += 17;
    306.                 this.previewText.padding.left += 3;
    307.                 this.previewText.padding.right += 3;
    308.                 ++this.previewHeader.padding.left;
    309.                 this.previewHeader.padding.right += 3;
    310.                 this.previewHeader.padding.top += 3;
    311.                 this.previewHeader.padding.bottom += 2;
    312.             }
    313.         }
    314.     }
    315. }
    AddScriptWindowBackup.cs:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System;
    4.  
    5. namespace UnityEditorAddon {
    6.     [Serializable]
    7.     class AddScriptWindowBackup : ScriptableObject {
    8.         public string scriptPath;
    9.         public bool addAsset;
    10.  
    11.         public static AddScriptWindowBackup Instance {
    12.             get {
    13.                 var objs = Resources.FindObjectsOfTypeAll<AddScriptWindowBackup>();
    14.                 if (objs.Length == 0 || objs[0] == null) {
    15.                     return ScriptableObject.CreateInstance<AddScriptWindowBackup>();
    16.                 }
    17.                 return objs[0];
    18.             }
    19.         }
    20.  
    21.         void OnEnable() {
    22.             hideFlags = HideFlags.HideAndDontSave;
    23.         }
    24.  
    25.         public void Reset() {
    26.             addAsset = false;
    27.             scriptPath = string.Empty;
    28.         }
    29.     }
    30. }
    Usage:
    Code (CSharp):
    1.  
    2. public override void OnInspectorGUI() {
    3.      ...
    4.      var editorPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this)));
    5.      var templatePath = editorPath + "/ScriptTemplate/MyEquipmentBehaviour.cs.template";
    6.      AddScriptWindow.Show(CreateScriptInstance, CanAddScript, templatePath);
    7.      ...
    8. }
    9.  
    10. void CreateScriptInstance(MonoScript script) {
    11.      if (!script || script.GetClass() == null) {
    12.          return;
    13.      }
    14.      //var instance = Activator.CreateInstance(script.GetClass()) as EquipmentBehaviour;
    15.      var instance = ScriptableObject.CreateInstance(script.GetClass().Name) as EquipmentBehaviour;
    16.      (target as Equipment).AddBehaviour(instance);
    17.  
    18.      AssetDatabase.AddObjectToAsset(instance, target);
    19.      EditorUtility.SetDirty(target);
    20.      return;
    21. }
    22.  
    23. bool CanAddScript(MonoScript script) {
    24.      var scriptClass = script.GetClass();
    25.      if(scriptClass == null){
    26.           return false;
    27.      }
    28.      return !scriptClass.IsAbstract && scriptClass.IsSubclassOf(typeof(EquipmentBehaviour));
    29. }
    30.  
    We need the AddScriptWindowBackup class for saving the added script. So we can add them after the compiler has finished his work. The AddComponentWindow use a internal function and don't need to wait for the compiler.

    And for you who don't know how to render the child objects like components:
    Code (CSharp):
    1.  
    2. public override void OnInspectorGUI() {
    3.      ...
    4.      foreach(var child in childComponents) {
    5.           DrawEquipmentBehaviour(Editor.CreateEditor(child));      
    6.      }
    7.      ...
    8.      EditorUtility.SetDirty(target);
    9. }
    10.  
    11. void DrawEquipmentBehaviour(Editor editor) {
    12.      if (!editor) {
    13.          return;
    14.      }
    15.      var foldout = EditorGUILayout.InspectorTitlebar(EditorPrefs.GetBool(editor.target.GetType().Name, true), editor.target);
    16.      EditorPrefs.SetBool(editor.target.GetType().Name, foldout);
    17.      if (foldout) {
    18.          editor.DrawDefaultInspector();
    19.          editor.serializedObject.ApplyModifiedProperties();
    20.      }
    21. }
    22.  
     
    Last edited: Nov 9, 2016
  6. shuao23

    shuao23

    Joined:
    Dec 19, 2013
    Posts:
    33
    For those who want to it in a more recent version of Unity, the "AdvancedDropdown" is what you are looking for
     
    Creta_Park, oscarAbraham and erika_d like this.