Search Unity

Editor Script to set Icons impossible?

Discussion in 'Editor & General Support' started by Deleted User, Jun 27, 2013.

  1. Deleted User

    Deleted User

    Guest

    Greetings,

    Can I access these via an Editor Script?

    $icons.png

    I have read up on Gizmos, Handle.Label and such, but none of them are suitable. I have found no information
    on this.
    It would be fantastic to simply access and set icons and icon colors to which ever object(s) we wish via an editor window in bulk!

    I decided to come here before going to the suggestions page since I feel perhaps I am over looking something I am not able to find via a simple google search.

    Thank you for any info in advance!
     
  2. Deleted User

    Deleted User

    Guest

    *Bump* Surely someone must know at least if it is possible or not? :confused:
     
  3. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,620
    I'd love to know this too.

    There's Gizmos.DrawIcon(...) but it'd be super handy if I could just use one of these.
     
  4. BEIEIA

    BEIEIA

    Joined:
    Sep 17, 2012
    Posts:
    14
    +1
     
  5. Deleted User

    Deleted User

    Guest

    Yes this I am aware of, and I found that if you set [CustomEditor (typeof(GameObject))] you can access the texture inspector for the Icon, so it's as if they are just using DrawIcon, this might be the solution.

    I will test it first, but it might just be Gizmos.DrawIcon and accessing the icons within the Unity files (if they are accessible)!

    edit:

    I have found that it "works" but the names of the objects don't come up. Feels like jumping through hoops when one could just set "gizmos.Icon" for example. Will add this to the suggestions page since I presume it is NOT possible to get icons with the names set via script.
     
    Last edited by a moderator: Jul 1, 2013
  6. Deleted User

    Deleted User

    Guest

    The best solution for this so far has been to assign empty scipts with Icons to the game objects. This will take the gameobject name and the script color.

    However this is messy and shouldn't be necessary at all. Hopefully Unity guys will sort this out I see no reason why:
    a) we cannot set icons to multiple objects and selections
    b) why we can't access the icon option from Gizmo
     
  7. ArkaneX

    ArkaneX

    Joined:
    Jul 21, 2012
    Posts:
    14
    Quite old thread, but...

    I have investigated this today, as I saw a question related to object icons at Unity Answers. It is possible to assign an icon, but you have to use reflection. Example:

    Code (csharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System.Reflection;
    4.  
    5. public class CreateObject : Editor
    6. {
    7.     [MenuItem("Tools/Create object with icon")]
    8.     static void CreateSphereWithIcon()
    9.     {
    10.         Texture2D icon = (Texture2D)Resources.Load("CustomIcon");
    11.         var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
    12.  
    13.         var editorGUIUtilityType = typeof(EditorGUIUtility);
    14.         var bindingFlags = BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic;
    15.         var args = new object[] { go, icon };
    16.         editorGUIUtilityType.InvokeMember("SetIconForObject", bindingFlags, null, null, args);
    17.         //var editorUtilityType = typeof(EditorUtility);
    18.         //editorUtilityType.InvokeMember("ForceReloadInspectors", bindingFlags, null, null, null);
    19.     }
    20. }
    ForceReloadInspector method is called in original source (inside UnityEditor.IconSelector.DoButton method), but when I tested above solution, the call was not required. I left these lines here 'just in case', so if there are any problems, maybe uncommenting them will help.
     
    CoughE likes this.
  8. Aram-Azhari

    Aram-Azhari

    Joined:
    Nov 18, 2009
    Posts:
    142
    This does not work anymore as SetIconForObject has been removed from EditorGUIUtility
     
  9. Thundernerd

    Thundernerd

    Joined:
    Jan 24, 2014
    Posts:
    20
    For anyone who is still wondering how to do this, I made a script that does this.

    Code (CSharp):
    1. using System;
    2. using System.Reflection;
    3. using UnityEditor;
    4. using UnityEngine;
    5.  
    6. public class IconManager {
    7.  
    8.     public enum LabelIcon {
    9.         Gray = 0,
    10.         Blue,
    11.         Teal,
    12.         Green,
    13.         Yellow,
    14.         Orange,
    15.         Red,
    16.         Purple
    17.     }
    18.  
    19.     public enum Icon {
    20.         CircleGray = 0,
    21.         CircleBlue,
    22.         CircleTeal,
    23.         CircleGreen,
    24.         CircleYellow,
    25.         CircleOrange,
    26.         CircleRed,
    27.         CirclePurple,
    28.         DiamondGray,
    29.         DiamondBlue,
    30.         DiamondTeal,
    31.         DiamondGreen,
    32.         DiamondYellow,
    33.         DiamondOrange,
    34.         DiamondRed,
    35.         DiamondPurple
    36.     }
    37.  
    38.     private static GUIContent[] labelIcons;
    39.     private static GUIContent[] largeIcons;
    40.  
    41.     public static void SetIcon( GameObject gObj, LabelIcon icon ) {
    42.         if ( labelIcons == null ) {
    43.             labelIcons = GetTextures( "sv_label_", string.Empty, 0, 8 );
    44.         }
    45.  
    46.         SetIcon( gObj, labelIcons[(int)icon].image as Texture2D );
    47.     }
    48.  
    49.     public static void SetIcon( GameObject gObj, Icon icon ) {
    50.         if ( largeIcons == null ) {
    51.             largeIcons = GetTextures( "sv_icon_dot", "_pix16_gizmo", 0, 16 );
    52.         }
    53.  
    54.         SetIcon( gObj, largeIcons[(int)icon].image as Texture2D );
    55.     }
    56.  
    57.     private static void SetIcon( GameObject gObj, Texture2D texture ) {
    58.         var ty = typeof( EditorGUIUtility );
    59.         var mi = ty.GetMethod( "SetIconForObject", BindingFlags.NonPublic | BindingFlags.Static );
    60.         mi.Invoke( null, new object[] { gObj, texture } );
    61.     }
    62.  
    63.     private static GUIContent[] GetTextures( string baseName, string postFix, int startIndex, int count ) {
    64.         GUIContent[] guiContentArray = new GUIContent[count];
    65.  
    66.         var t = typeof( EditorGUIUtility );
    67.         var mi = t.GetMethod( "IconContent", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof( string ) }, null );
    68.  
    69.         for ( int index = 0; index < count; ++index ) {
    70.             guiContentArray[index] = mi.Invoke( null, new object[] { baseName + (object)(startIndex + index) + postFix } ) as GUIContent;
    71.         }
    72.  
    73.         return guiContentArray;
    74.     }
    75. }
     
    apkdev, CoughE, kyle_v and 2 others like this.
  10. A_Strandfelt

    A_Strandfelt

    Joined:
    Nov 5, 2014
    Posts:
    1
    Thanks. I used this script and it worked. However, it seems like the IconContent has been changed to a "public static" method, so the binding flag on line 67 has to be changed to BindingFlags.Public | BindingFlags.Static for it to work. :)
     
    Ethereal_Sky and Thundernerd like this.
  11. delzhand

    delzhand

    Joined:
    Jul 20, 2012
    Posts:
    26
    Here's a version that's less flexible, but more concise and maybe a little easier to follow:

    Code (CSharp):
    1.   public static void AssignLabel(GameObject g)
    2.   {
    3.     Texture2D tex = EditorGUIUtility.IconContent("sv_label_0").image as Texture2D;
    4.     Type editorGUIUtilityType  = typeof(EditorGUIUtility);
    5.     BindingFlags bindingFlags = BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic;
    6.     object[] args = new object[] {g, tex};
    7.     editorGUIUtilityType.InvokeMember("SetIconForObject", bindingFlags, null, null, args);
    8.   }
     
    asafsitner, Estecka, Marrt and 2 others like this.
  12. DGLWilkins

    DGLWilkins

    Joined:
    Sep 25, 2012
    Posts:
    7
    That works so well. Thank you.
     
  13. satanin

    satanin

    Joined:
    Mar 1, 2016
    Posts:
    2
    Hi, I tried to use thudernerd's script but icons are not changing, clearly I'm doing something wrong. How is this script suposed to be used?
    Thank You.

    update:
    Im getting this error using the script:
    Code (csharp):
    1.  
    2. NullReferenceException: Object reference not set to an instance of an object
    3. IconManager.GetTextures (System.String baseName, System.String postFix, Int32 startIndex, Int32 count) (at Assets/Scripts/IconManager.cs:70)
    This is the Line with the error:
    Code (csharp):
    1.  
    2. guiContentArray[index] = mi.Invoke( null, new object[] { baseName + (object)(startIndex + index) + postFix } ) as GUIContent;
    3.  
     
    Last edited: Jun 25, 2016
  14. Thundernerd

    Thundernerd

    Joined:
    Jan 24, 2014
    Posts:
    20
    I've put the source on my github with an updated version for 5.3 and higher.
    I've also added extension methods for GameObjects to eliminate the call to IconManager (you're still free to do so though)

    It's available here: https://github.com/Thundernerd/Unity3D-IconManager
     
    KateKim and gagneux like this.
  15. satanin

    satanin

    Joined:
    Mar 1, 2016
    Posts:
    2
    Thx!
    do I need the ProjectSettings folder in my project too?
     
  16. Thundernerd

    Thundernerd

    Joined:
    Jan 24, 2014
    Posts:
    20
    Nah you just need the IconManager code file tbh
     
  17. Kappe

    Kappe

    Joined:
    May 2, 2014
    Posts:
    5
    Super handy script! Thx...
     
  18. KateKim

    KateKim

    Joined:
    Mar 8, 2017
    Posts:
    1
    Thank you so much from 2018 :)
     
  19. Estecka

    Estecka

    Joined:
    Oct 11, 2013
    Posts:
    62
    Is this possible to permanently change a monobehaviour's icon like this ?

    I used @delzhand 's snippet to build my own editor window. While it works fine with scene objects and prefab assets, the changes on scripts won't persist after I restart Unity.
     
  20. Thundernerd

    Thundernerd

    Joined:
    Jan 24, 2014
    Posts:
    20
    It sounds like the objects aren't marked as dirty, and maybe the scene isn't either. Are you sure that's happening?
     
  21. Estecka

    Estecka

    Joined:
    Oct 11, 2013
    Posts:
    62
    Certain.

    What's that about object being marked as dirty? I've heard the word before, but I'm not familiar with the concept. I tought this was actually something you'd want to avoid.

    Here's the full code for my EditorWindow, I've been trying to assign icon from existing components, icons like
    "rigidbody icon"
    or
    "charactercontroller icon"
    :
    (Ah yes, I forgot to mention that I was not assigning any of the icons that are available through the manual menu. That's actually the only reason I looked for this kind of code.)


    Code (CSharp):
    1. #if UNITY_EDITOR
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEditor;
    6.  
    7. using System.Reflection;
    8.  
    9. namespace Estecka.EsteckaEditor {
    10.     public class IconAssignationWindow : EditorWindow {
    11.  
    12.         static readonly float
    13.         iconMinSize = 19,
    14.         iconMaxSize = 67;
    15.  
    16.         [SerializeField] string iconName;
    17.         GUIContent icon;
    18.  
    19.         static void AssignIcon(Object target, GUIContent icon){
    20.             if (target == null || icon == null)
    21.                 throw new System.ArgumentNullException ();
    22.  
    23.             Texture2D tex = icon.image as Texture2D;
    24.             if (tex == null) {
    25.                 Debug.LogError ("Invalid Icon format : Not a Texture2D");
    26.                 return;
    27.             }
    28.  
    29.             System.Type editorgui = typeof(EditorGUIUtility);
    30.             BindingFlags flags = BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic;
    31.             object[] args = new object[] {target, tex};
    32.             editorgui.InvokeMember ("SetIconForObject", flags, null, null, args);
    33.  
    34.         }//
    35.  
    36.         [MenuItem("Estecka/Icon Assignation")]
    37.         static public void Init(){
    38.             EditorWindow.GetWindow<IconAssignationWindow> (true, "Icon Assigner").Show ();
    39.         }
    40.  
    41.  
    42.         public void OnGUI(){
    43.  
    44.             Selection.activeObject = EditorGUILayout.ObjectField ("Target Object", Selection.activeObject, typeof(Object), false);
    45.  
    46.             EditorGUI.BeginChangeCheck ();
    47.             iconName = EditorGUILayout.DelayedTextField ("Icon Name", iconName);
    48.  
    49.             if (EditorGUI.EndChangeCheck()){
    50.                 try {
    51.                     icon = EditorGUIUtility.IconContent (iconName);
    52.                 }
    53.                 catch (System.Exception e){
    54.                     Debug.Log (e.GetType ());
    55.                 }
    56.             }
    57.  
    58.             if (icon == null)
    59.                 EditorGUILayout.LabelField ("No icon found");
    60.             else {
    61.                 Rect pos;
    62.  
    63.                 pos = EditorGUILayout.GetControlRect (false, iconMaxSize);
    64.                 EditorGUI.LabelField (pos, icon);
    65.                 pos.x += iconMaxSize;
    66.                 pos.height = iconMinSize;
    67.                 EditorGUI.LabelField (pos, icon);
    68.                 EditorGUILayout.LabelField ("InstanceID: "+icon.image.GetInstanceID());
    69.  
    70.                 if (Selection.activeObject && GUILayout.Button ("Perform Assignation"))
    71.                     AssignIcon (Selection.activeObject, icon);
    72.  
    73.             }
    74.         }//
    75.  
    76.  
    77.     } // END Window
    78. }// END Namespace
    79. #endif
     
    Last edited: Aug 21, 2018
  22. Thundernerd

    Thundernerd

    Joined:
    Jan 24, 2014
    Posts:
    20
    Whenever you make a change to an object through code you have to notify Unity that the object you've changed had something changed. You do this by marking it dirty with the following function: EditorUtility.SetDirty (read the text though, it mentions that the proper way to do it nowadays is using Undo.RecordObject).

    Looking at your code I don't see this happening, that's why the icon doesn't get updated in your scene file, and therefore doesn't persist.

    You might also want to take a look at marking your scene dirty after marking the object dirty. You can do this through the EditorSceneManager.

    If you have any other questions, let me know!
     
  23. Estecka

    Estecka

    Joined:
    Oct 11, 2013
    Posts:
    62
    Still not working, neither with
    EditorUtility.SetDirty
    or
    Undo.RecordObject
    .

    I tried several other things which lead me to think that assigning these icon to a monoscript is actually possible, but maybe not through this exact method, namely:
    - My script can't even persistently assign the more conventional icons (
    "sv_label_0"
    and such)
    - I could persistently change the monoscript's icon by manually fiddling with its *.meta file.

    I've been watching the target monoscript's meta file in a text editor while doing some change. No surprise, assigning an icon through the menu changes the meta, assigning an icon through my script doesn't.
    Since I still could edit prefabs persistently, I did the same here; then if I just copy the icon datas from the prefab file, and paste it into the monoscript's meta file, I can change the icon persistently.

    Now, whereas a monoscript's icon is stored in its meta file, a prefab's icon is stored in its own file, maybe this has to do with why it wouldn't work ?
    Does meta-file editing have its own API ?
     
  24. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    To set script icon programmatically, I have used reflection to
    static
    methods
    EditorGUIUtility.SetIconForObject
    and then
    MonoImporter.CopyMonoScriptIconToImporters
    . It works in 2019.1. The project will recompile, but only once if you do it multiple times.

    This is an example code I used to set icons to defined scripts that has a certain interface all at once. The icon is hard coded to meta GUID in the project. I think it is more resilient than string path.
    https://github.com/5argon/Minefield/blob/master/Editor/AssignIconTool.cs
     
    Last edited: Jun 20, 2019
  25. Estecka

    Estecka

    Joined:
    Oct 11, 2013
    Posts:
    62
    Thanks on that one !
    I got it working in 2017.4.18 as well.

    To sum it up all, the proper way to call these methods is :
    Code (CSharp):
    1. MethodInfo SetIconForObject = typeof(EditorGUIUtility).GetMethod("SetIconForObject", BindingFlags.Static | BindingFlags.NonPublic);
    2. MethodInfo CopyMonoScriptIconToImporters = typeof(MonoImporter).GetMethod("CopyMonoScriptIconToImporters", BindingFlags.Static | BindingFlags.NonPublic);
    3.  
    4. Monoscript script = whatever;
    5. Texture2D icon = whatever;
    6.  
    7. SetIconForObject.Invoke(null, new[]{ script, icon });
    8. CopyMonoScriptToImporters.invoke(null, script);
    Edit :
    And here's the corrected version of my editor windows to quickly assign icons to scripts :
    https://github.com/Estecka/Estecka-...Editor/EditorWindows/IconAssignationWindow.cs
     
    Last edited: Jul 3, 2019
    Rafael_SGP likes this.
  26. Thundernerd

    Thundernerd

    Joined:
    Jan 24, 2014
    Posts:
    20
    Apologies. I must've misread the initial question, and thankfully your issue has been resolved now :).

    However, the IconManager class is just for changing the icon of a GameObject in a scene. Not for changing the icon of a file in the Project view.
     
  27. markvi

    markvi

    Joined:
    Oct 31, 2016
    Posts:
    118
    For anyone who stumbles onto this thread: starting with Unity 2021.2a19, the necessary APIs are now public.

    EditorGUIUtility.SetIconForObject/GetIconForObject
    are now public and should be called without the use of reflection.
    EditorGUIUtility.SetIconForObject
    immediately updates the icon in the Editor. However, it won't persist the change. The next time the MonoScript is imported, the icon will revert back to its default.

    To persist the change, use the following code:

    Code (CSharp):
    1. // given monoScript and icon
    2. var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(monoScript));
    3. importer.SetIcon(icon);
    4. importer.SaveAndReimport();
    This does the same thing as
    CopyMonoScriptIconToImporters
    , but uses the newly public
    MonoImporter.SetIcon
    and
    PluginImporter.SetIcon
    APIs.

    CopyMonoScriptIconToImporters
    is now obsolete and will output a warning to the console when called.
     
    Last edited: May 26, 2021
  28. TustinJimberlake

    TustinJimberlake

    Joined:
    May 14, 2016
    Posts:
    4
    ITS BACK BABY
     
  29. TustinJimberlake

    TustinJimberlake

    Joined:
    May 14, 2016
    Posts:
    4
    Is there any way to do this on ScriptableObject instances? I have an `Item` ScriptableObject with a `Sprite icon` property. I would like this property icon to become the asset icon per `Item` asset instance.
     
  30. TheVirtualMunk

    TheVirtualMunk

    Joined:
    Sep 6, 2019
    Posts:
    150
    There's probably a way to do this with attributes and reflection.

    See my (old) answer here; https://www.reddit.com/r/Unity3D/comments/g13odm/unique_icon_to_every_specific_scriptable_object/

    The gist of it would be;
    • Make a property attribute "unique icon" that can be assigned to Texture2D fields only.
    • Make an editor script that (using reflection) finds all class types that implement a field with that attribute.
    • Find all instance of that class; Probably with Resources.FindObjectsOfTypeAll(type);
    • Get the instance value of the attribute field (the texture2D) and draw it like in my comment on reddit.


    If there's any other way to draw per-instance icon's I would also like to know.
     
  31. markvi

    markvi

    Joined:
    Oct 31, 2016
    Posts:
    118
    TheVirtualMunk likes this.
  32. MrG

    MrG

    Joined:
    Oct 6, 2012
    Posts:
    368
    This still is a manual process (Menu clicks).

    Let's say I have a base class of
    MyBaseClass : MonoBehaviour
    and I want to fully automate setting an icon for scripts, persistently, that inherit from MyBaseClass, and using a GUID instead of a path since the path may change if the folder the icon is in (or a parent) gets moved?
     
  33. HMTEngineering

    HMTEngineering

    Joined:
    Aug 8, 2015
    Posts:
    39
    So I made a combination of some ideas in here to make it work with scriptable objects!
    In my case the Scriptable Object has a Sprite field, but you can easily adjust it to make it work with Textures:
    Code (CSharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CreateAssetMenu(fileName = "Demo", menuName = "DemoSO", order = 1)]
    5. public class DemoScriptableObject: ScriptableObject
    6. {
    7.     public Sprite PreviewIcon;
    8. #if UNITY_EDITOR
    9.     private void OnValidate()
    10.     {
    11.         if (PreviewIcon!= null)
    12.         {
    13.             var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(this));
    14.             EditorGUIUtility.SetIconForObject(this, this.PreviewIcon.texture);
    15.             importer.SaveAndReimport();
    16.         }
    17.     }
    18. #endif
    19. }

    UPDATE:
    Ah too bad, this gets reset on every full reimport... Maybe someone has an idea on how to make it persistant?