How to have custom script icons other than using "Assets/Gizmos" in Unity3D
Asked Answered
A

3

9

I know this was asked a lot of times probably .. but it is very often answered wrong.

What I want is:

Use a custom icon for specific components/scripts in the Inspector (e.g. Figure 2 and Figure 3) and the ProjectView (e.g. Figure 1) alt text

What I do so far:

For each component/class that shall have the icon I have an accroding Icon file in the folder

Assets/Gizmos/<Path>/<To>/<Namespace>/<ClassName> icon

and in the Import Settigns set TextureType to Editor GUI and Legacy GUI

This is working fine .. and until now the only way how I could achieve that (having in mind the below section What I definitely do NOT want).


But

However, I wondered if there is really no better way having to have a unique Icon file for each script. This makes the project/UnityPackage unnecessarily huge. Also if I rename a class I always have to rename the according icon file as well ... This imply doesn't feel right!

Most Unity build-in Behaviours and Components have a unique icon. But also external Packages coming from the new PackageManager have built-in icons and sometimes a Gizmos folder but it is not following the above naming rule ... so apparently the icon is configured somehow else for them.

Therefore my questions:
Is there any better way to have those icons for scripts/components?

Preferably scripted and reusing ONE single icon file instead of having the same icon in multiple differently named files.

And/or also Where/How are those icons defined for the scripts coming from the PackageManager?


!NOTE! What I definitely do NOT want:

Show the Icon also in the SceneView for all GameObjects having those components attached (e.g. Figure 4). This is caused by either selecting the icon for this script via the Inspector as in Figure 5 (as allways suggested e.g. in this post or here and even by Unity - Assign Icons ) or using OnDrawGizmos or DrawGizmo. This is not happening using the approach I use currently with the Gizmos folder!

alt text

Update

Because this was suggested in this answer: I also know that I could do that and turn them off via the Gizmos settings of the SceneView. But imagine I have like 25 different modules and various different icons each. I don't want to have to disable their Gizmos in the SceneView settings one by one on a per project basis! Even the provided script seems like a vast hackaround. Reflection would be the very last resort I would ever take. Also I'ld prefer to not even have those icons appear as possible Gizmos at all instead of disabling them all.

Assisi answered 18/7, 2018 at 8:41 Comment(0)
J
3

You can set the icon with figure 5 and then turn the gizmos for that icon off from the gizmos drop down.

Edit: Injunction with the step above you could try this script derived from here it uses reflection to find the class responsible for turning off the the gizmos and icons. This would execute any time your scripts recompiled to keep those icons off or if you added any new icons to the autohide icon file. Note: scriptClass will be an empty string for built in components eg.Camera, AudoSource

using UnityEditor;
using System;
using System.Reflection;

public class DisableAllGizmos
{
    [UnityEditor.Callbacks.DidReloadScripts]
    private static void OnScriptsReloaded()
    {
        var Annotation = Type.GetType("UnityEditor.Annotation, UnityEditor");
        var ClassId = Annotation.GetField("classID");
        var ScriptClass = Annotation.GetField("scriptClass");
        var Flags = Annotation.GetField("flags");
        var IconEnabled = Annotation.GetField("iconEnabled");

        Type AnnotationUtility = Type.GetType("UnityEditor.AnnotationUtility, UnityEditor");
        var GetAnnotations = AnnotationUtility.GetMethod("GetAnnotations", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
        var SetIconEnabled = AnnotationUtility.GetMethod("SetIconEnabled", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);

        Array annotations = (Array)GetAnnotations.Invoke(null, null);
        foreach (var a in annotations)
        {
            int classId = (int)ClassId.GetValue(a);
            string scriptClass = (string)ScriptClass.GetValue(a);
            int flags = (int)Flags.GetValue(a);
            int iconEnabled = (int)IconEnabled.GetValue(a);

            // this is done to ignore any built in types
            if (string.IsNullOrEmpty(scriptClass))
            {
                continue;
            }

            // load a json or text file with class names

            const int HasIcon = 1;
            bool hasIconFlag = (flags & HasIcon) == HasIcon;

            // Added for refrence
            //const int HasGizmo = 2;
            //bool hasGizmoFlag = (flags & HasGizmo) == HasGizmo;

            if (/*Compare class names in file to scriptClass == true*/)
            {
                if (hasIconFlag && (iconEnabled != 0))
                {
                    UnityEngine.Debug.LogWarning(string.Format("Script:'{0}' is not ment to show its icon in the scene view and will auto hide now. " +
                        "Icon auto hide is checked on script recompile, if you'd like to change this please remove it from the config",scriptClass));
                    SetIconEnabled.Invoke(null, new object[] { classId, scriptClass, 0 });
                }
            }
        }
    }
}
  1. Shown in the inspector with a gizmo

  2. Hide Icon from gizmos dropdown

  3. Icon still appears in the inspector and in the project view but not in the scene

Johnsonjohnsonese answered 13/3, 2019 at 19:23 Comment(8)
Thanks for your answer .. ofcourse I already thought of this .. but I have like 30 different modules with various icons each this way .. I do not want to have to turn them all off one by one on a each project basis ... So compared to the current solution which as said basically works as expected this is not an option for me.Assisi
I found a way to supplement my answer with script to auto hide icons you don't want visibleJohnsonjohnsonese
Just tested it ... scriptClass = "" for all entries so it won't work. Also ... this seems like a vast workaround to me. Mainly for two reasons: 1. Using reflection .. for anything .. is the very last resort I would ever take .. 2. I know this might sound like a vague argument but just by making it appear in the Gizmos at all gives a user the feeling there is something he can enable and disable .. allways re-disabling it would seem like "unexpected" behaviour. Don't get me wrong your answer is good and proves some deeper background knowledge but I somhow feel it's not the solution yet :)Assisi
I 100% agree that you should only use reflection when there are no other options and because unity doesn't publicly expose this api, reflection is the only method or you can use your current solution with folders corresponding to the namespace and class name. Also scriptClass = "" with built in types like camera and audio source but if you have textmeshpro in your project you should see the scriptClass name for them. And what Unity/OS version are you using?Johnsonjohnsonese
v 2018.3.4f1 .. it didn't disable my icons that's why I started debugging but to be honest after I had seen the first 20 entries with scriptClass = "" I stopped debugging so I'll try it again later ;) I tried it with if(string.Equals(scriptClass, typeof(My class).Name))) maybe something went wrong there as wellAssisi
I had 53 scriptClass = "", you should have approximately that same. Also I added a check to skip as soon as it detects a scriptClass with no name.Johnsonjohnsonese
Hey thanks again for your answer. I tested it again and it basically works so you totally deserve the reward. For some reason I'm still not completely convinced though but that doesn't negate the fact that you provided a solid alternative to what I'm doing so far :)Assisi
:) Thanks alot. Yea I totally agree with you this should have been a lot easier and maybe at some point in the future unity will make this api public.Johnsonjohnsonese
J
1

So I did a bit more research about built in types as well as packages coming from the package manager and the asset store. Anything that is external (packagemanager or assetstore) if it has a custom icon for the script and inspector it will Always have a gizmo in the scene view. As it has its icon set using your figure 5 example, as seen in the screenshots with the debug inspector.

Also if you want to set the icon with a script or hide it ,currently reflection is your only option as these APIs are not publicly accessible.

My Script showing the debug inspector for its script

PixelPerfect package script from the packagemanager in the debug inspector

PixelPerfect Icon showing in the scene

I was hoping to add this as a comment to your original question but not enough rep yet.

Johnsonjohnsonese answered 18/3, 2019 at 19:6 Comment(0)
S
0

You can now use Icon("iconpath") in 2021.2:

#if UNITY_2021_2_OR_NEWER
    [Icon(k_IconPath)]
#endif
    public sealed class PhysicsBodyAuthoring : MonoBehaviour
    {
        const string k_IconPath = "Packages/com.unity.physics/Unity.Physics.Editor/Editor Default Resources/Icons/[email protected]";

        PhysicsBodyAuthoring() {}
    }
Senna answered 30/1 at 1:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.