How can I add to a List in [InitializeOnLoad] script only once?

The problem when using InitializeOnLoad it's invalidating all the time. Then even if I'm making an instance once for a List at the top of the script it will keep making instance for it all the time and adding more and more items to the List.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public static class CustomHierarchy
{
    private static Vector2 offset = new Vector2(0, 2);
    public static Color gameObjectFontColor = Color.black;
    public static Color prefabOrgFontColor = Color.black;
    public static Color prefabModFontColor = Color.white;
    public static Color inActiveColor = new Color(0.01f, 0.4f, 0.25f);
    public static Color meshRendererColor = Color.yellow;

    public static List<GameObject> gameobjectsHasAll = new List<GameObject>();

    static CustomHierarchy()
    {
        EditorApplication.hierarchyWindowItemOnGUI += HandleHierarchyWindowItemOnGUI;
    }
    private static void HandleHierarchyWindowItemOnGUI(int instanceID, Rect selectionRect)
    {
        Color fontColor = gameObjectFontColor;
        Color backgroundColor = new Color(.76f, .76f, .76f);
        FontStyle styleFont = FontStyle.Normal;
        var obj = EditorUtility.InstanceIDToObject(instanceID);
        GameObject gameObj = EditorUtility.InstanceIDToObject(instanceID) as GameObject;

        if (Selection.instanceIDs.Contains(instanceID))
        {
            backgroundColor = new Color(0.24f, 0.48f, 0.90f);
        }
        if (obj != null)
        {
            var prefabType = PrefabUtility.GetPrefabType(obj);
            if (gameObj.activeInHierarchy == false)
            {
                backgroundColor = inActiveColor;
            }

            if (prefabType == PrefabType.PrefabInstance)
            {
                styleFont = FontStyle.Bold;
                PropertyModification[] prefabMods = PrefabUtility.GetPropertyModifications(obj);
                foreach (PropertyModification prefabMod in prefabMods)
                {
                    if (prefabMod.propertyPath.ToString() != "m_Name" && prefabMod.propertyPath.ToString() != "m_LocalPosition.x" && prefabMod.propertyPath.ToString() != "m_LocalPosition.y" && prefabMod.propertyPath.ToString() != "m_LocalPosition.z" && prefabMod.propertyPath.ToString() != "m_LocalRotation.x" && prefabMod.propertyPath.ToString() != "m_LocalRotation.y" && prefabMod.propertyPath.ToString() != "m_LocalRotation.z" && prefabMod.propertyPath.ToString() != "m_LocalRotation.w" && prefabMod.propertyPath.ToString() != "m_RootOrder" && prefabMod.propertyPath.ToString() != "m_IsActive")
                    {
                        if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
                        {
                            if (!gameobjectsHasAll.Contains(gameObj))
                                gameobjectsHasAll.Add(gameObj);
                            fontColor = meshRendererColor;
                        }
                        else
                        {
                            fontColor = prefabModFontColor;
                        }

                        break;
                    }
                }
                if (fontColor != prefabModFontColor)
                {
                    if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
                    {
                        gameobjectsHasAll.Add(gameObj);
                        fontColor = meshRendererColor;
                    }
                    else
                    {
                        fontColor = prefabOrgFontColor;
                    }
                }
            }
            else
            {
                if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
                {
                    gameobjectsHasAll.Add(gameObj);
                    fontColor = meshRendererColor;
                }
            }
            Rect offsetRect = new Rect(selectionRect.position + offset, selectionRect.size);
            EditorGUI.DrawRect(selectionRect, backgroundColor);
            EditorGUI.LabelField(offsetRect, obj.name, new GUIStyle()
            {
                normal = new GUIStyleState() { textColor = fontColor },
                fontStyle = styleFont
            }
            );
        }
    }

    public static bool HasAllComponents(GameObject gameObject, params System.Type[] types)
    {
        for (int i = 0; i < types.Length; i++)
        {
            if (gameObject.GetComponent(types[i]) == null)
                return false;
        }

        return true;
    }
}

The List variable is:

public static List<GameObject> gameobjectsHasAll = new List<GameObject>();

And I'm adding objects to the list in 3 places in the code in each place where HasAllComponents is true for example:

if (HasAllComponents(gameObj, typeof(MeshRenderer), typeof(BoxCollider)))
                    {
                        gameobjectsHasAll.Add(gameObj);
                        fontColor = meshRendererColor;
                    }

In the Hierarchy I have 6 items that meet the HasAllComponents condition. But at the first time the List contain 36 items it keep adding the 6 items 6 times.

Then next 120 items or so and it keep adding the same items nonstop. I'm calling this List from another script EditorWindow script:

using System;
using UnityEditor;
using UnityEngine;
using System.Collections;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;

public class HierarchyEditor : EditorWindow
{
    public static bool multipleComponents = false;
    public static string multipleComponentsString = "";

    private static SearchableEditorWindow hierarchy { get; set; }
    private string filterText = "";
    private string oldFilterText = "";

    [MenuItem("Tools/Hierarchy Editor")]
    public static void ShowWindow()
    {
        GetWindow<HierarchyEditor>("HierarchyEditor");

        Test();
    }

Where Test is:

private static void Test()
    {
        var all = CustomHierarchy.gameobjectsHasAll;
    }

2 answers

  • answered 2018-11-08 06:15 derHugo

    Before all of the lines

    gameobjectsHasAll.Add(gameObj);
    

    check if the object is already in the list and only add it if it is not there yet

    if(!gameobjectsHasAll.Contains(gameObj))
    {
        gameobjectsHasAll.Add(gameObj);
        fontColor = meshRendererColor;
    }
    

    so far it seems that you check this only once.

  • answered 2018-11-08 06:16 maximelian1986

    I believe you need to process only one element inside your HandleHierarchyWindowItemOnGUI method as you assign it to EditorApplication.hierarchyWindowItemOnGUI delegate. Manual says "Delegate for OnGUI events for every visible list item in the HierarchyWindow." (https://docs.unity3d.com/ScriptReference/EditorApplication-hierarchyWindowItemOnGUI.html), so for each list element (you have 6) it call it for each element again. And here is unity live cycle (https://docs.unity3d.com/Manual/ExecutionOrder.html), you see it continuously calling OnGUI(), so as you list is static instance of cause it does not remove previous objects from it, and as OnGUI() processing list and create new instances which pass through your check, list will grow and grow.

    So you may want to clear list inside you HandleHierarchyWindowItemOnGUI method, before processing it again.