Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

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:
    7,846
  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:
    7,846
  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.