How to use addressable system in editor script

Hi,

I have been working on adding the addressable system to my project, and one of the things I want to do is press a button in the inspector on my editor script and load a list of objects using their addresses. I have been trying to use several methods that work during play, but not in the editor. Is there something that needs to be done to make it work in the editor?

Here is what I have so far, and this doesn’t work at all (OnBGDLoad() doesn’t run once). Forgive me if I have made a stupid mistake, I am still relatively new to C#:

[CustomEditor(typeof(LoadTexture))]
public class LoadTextureEditor : Editor
{
    Camera cam;

    public void OnEnable()
    {
        EditorApplication.update += Update;
    }

    public void OnDisable()
    {
        EditorApplication.update -= Update;
    }

    public override void OnInspectorGUI()
    {
        var lt = target as LoadTexture;

        base.DrawDefaultInspector();
        EditorGUILayout.Space();

        if (GUILayout.Button("Save addresses and delete textures"))
        {
            lt.ConvertToAddressables();
        }

        if (GUILayout.Button("Reload textures"))
        {
            lt.reload = true;
        }
    }

    void Update()
    {
        var lt = target as LoadTexture;
        if (lt.reload)
        {
            Debug.LogError("Reload Executed");
            lt.reload = false;
            lt.allCams = Resources.FindObjectsOfTypeAll<Camera>();
            foreach (Camera c in lt.allCams)
            {
                if (c.name != "SettingsCam" && c.name != "SceneCamera" && c.name != "Preview Camera")
                {
                    if (c.transform.parent.Find("Background") != null)
                    {
                        cam = c;
                        Addressables.LoadAssetAsync<Sprite>(c.GetComponent<LoadTexture>().backgroundTextureAddress).Completed += OnBGDLoad;
                      
                    }
                }
            }
        }
    }

    private void OnBGDLoad(UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle<Sprite> op)
    {
        cam.transform.parent.Find("Background").GetComponent<SpriteRenderer>().sprite = op.Result;
        Debug.LogError(cam.name);
    }
}

P.S. The reason I am using the Update method is because I was having trouble getting foreach loops to run in editor, so I thought if I use update, I can force it to run as long as I have that inspector open (based on my understanding of how EditorApplication.update works)

So I realized that update would be running multiple times (obviously, duh) and thus it would cancel the foreach loop in progress. So I created a different solution, but it is still not working. Here is my code:

public void ReloadAddressables()
    {
        StartCoroutine(ReloadAddressablesIE());
    }

    IEnumerator ReloadAddressablesIE()
    {
        Debug.LogError("Reload Executed");
        allCams = Resources.FindObjectsOfTypeAll<Camera>();
        foreach (Camera c in allCams)
        {
            if (c.name != "SettingsCam" && c.name != "SceneCamera" && c.name != "Preview Camera")
            {
                if (c.transform.parent.Find("Background") != null)
                {
                    cam = c;
                    var op = Addressables.LoadAssetAsync<Sprite>(c.GetComponent<LoadTexture>().backgroundTextureAddress);
                    op.Completed += OnBGDLoad;

                    while (op.Status != AsyncOperationStatus.Succeeded)
                    {
                        Debug.LogError("op running");
                        //Do nothing
                        yield return null;
                    }

                    Debug.LogError(c.name + " finished loading");
                    yield return new WaitForSecondsRealtime(0.01f); //== Maybe this will give it enough time to finish OnBGDLoad() before starting another async op
                }
            }
        }
    }

    private void OnBGDLoad(AsyncOperationHandle<Sprite> async)
    {
        cam.transform.parent.Find("Background").GetComponent<SpriteRenderer>().sprite = async.Result;
        Debug.LogError(cam.name + " added to scene");
    }
}

#if UNITY_EDITOR
[CustomEditor(typeof(LoadTexture))]
public class LoadTextureEditor : Editor
{
    public override void OnInspectorGUI()
    {
        var lt = target as LoadTexture;

        base.DrawDefaultInspector();
        EditorGUILayout.Space();

        if (GUILayout.Button("Save addresses and delete textures"))
        {
            lt.ConvertToAddressables();
        }

        if (GUILayout.Button("Reload textures"))
        {
            lt.ReloadAddressables();
        }
    }
}
#endif

When I click “Reload Textures”, in the log, it says “Reload Executed” and then says “op running” once or maybe twice. That is it. I can’t figure out why this wouldn’t be working.

Ok, so I was able to fix my issue. The key was to abandon the addressable system entirely when reloading the textures in the editor. Here is my code:

public void ReloadAddressables()
    {
        allCams = Resources.FindObjectsOfTypeAll<Camera>();
        foreach (Camera c in allCams)
        {
            if (c.name != "SettingsCam" && c.name != "SceneCamera" && c.name != "Preview Camera")
            {
                if (c.transform.parent.Find("Background") != null)
                {
                    c.transform.parent.Find("Background").GetComponent<SpriteRenderer>().sprite = AssetDatabase.LoadAssetAtPath<Sprite>(c.gameObject.GetComponent<LoadTexture>().backgroundTextureAddress);
                }
            }
        }
    }
#if UNITY_EDITOR
[CustomEditor(typeof(LoadTexture))]
public class LoadTextureEditor : Editor
{
    public override void OnInspectorGUI()
    {
        var lt = target as LoadTexture;

        base.DrawDefaultInspector();
        EditorGUILayout.Space();

        if (GUILayout.Button("Save addresses and delete textures"))
        {
            lt.ConvertToAddressables();
        }

        if (GUILayout.Button("Reload textures"))
        {
            lt.ReloadAddressables();
        }
    }
}
#endif

The reason this works for me is because my address happens to be the same as the asset path. Even if it isn’t, you could get something working using AssetDatabase.GetAssetPath().

So in conclusion, I don’t know if this is a bug with the addressable system or not, but I had to use AssetDatabase.LoadAssetAtPath() when trying to load in the editor. Hope this helps someone.

2 Likes

not a bug. maybe a “missing feature”. Addressbles just isn’t set up for editor loading (outside of play mode) yet. It could be extended to work, and we’ve been looking at adding it ourselves, but it’s not there today.

4 Likes

Any update on extending the package to include this feature of loading in editor scripts?

2 Likes

Loading addressables in editor is important feature…

7 Likes

@unity_bill

Hi,

I also have to say why loading addressable into the editor is very important.

My project size is fairly large(~100GB) and the Editor is dog slow and it’s almost impossible to work in the current state. I’m in a huge pain every day and it’s really hopeless if I have to keep working like this.
And it’s getting worse every day with increasing size and newer Unity version update makes it even worse. I cannot believe what’s going on.

However, I’ll have to finish what I started and I’m doing my best trying to figure out a way to make it somewhat workable.

The only way I can do this is to split the project into smaller projects. I don’t think there isn’t a better way other than that.

Well, it looks like using Addressable is the best way to split the project.

The largest portion of the project is Maps(2/3 of the total project size are maps) and I first decided to split the Maps into its own project.

However, Maps contains both Static and Dynamic elements.
The Dynamic element often contains Scripts, thus it has to stay with the Main project.
Therefore, the new Map project will only contain Static elements, and they are built using Addressable.

So far so good.

Now it’s time to bring this Static Addressable Scene into the Main project so that I can layer Dynamic Scene on top of the Static Scene.

I of course thought loading Addressable into the Editor is a supported feature. It has to. If it can be dynamically loaded, why won’t it loaded into the Editor? I just learned that it’s not supported. And my hope is shattered.

Why!!! This is a huge surprise and I’m stuck right now and all my efforts will going to be wasted.

I’m pretty sure you are well aware that there are many complaints about slow Editor.
I think it’s to a point that it’s not acceptable.
If you agree, can you please support this feature, soonish? So that it can help us do the work, instead of fighting with the slow Editor?

If you read this far, I think you know why it’s important for the editor to be able to load addressables into the editor.
Even if you don’t agree, please give us a logical explanation or let us know better ways to work large projects.

If you don’t have answers, just make the Editor faster so that we don’t have to do this monkey work.

Thanks.

2 Likes

Super important, subscribing to hear when it happens hopefully?

2 Likes

Oh, I just ran into this problem. I would have thought this would have been an obvious feature. :slight_smile: Subscribing to thread.

@unity_bill
Hi, Bill, I think there are enough interests among users who will appreciate the feature.
I’m pretty sure many are not even aware of what this feature can enable us to do. If they do, they will change how they work with their project quite significantly.

I pushed Fast-Enter-to-Play mode hard and we finally have it in 2019.3. It’s a small(seemingly) feature but it’s the best feature I ever had past few years to improve the workflow significantly. And I believe many users finally appreciate it after experiencing it. I think loading Addressable into Editor is going to be another incredible feature similar to Fast-Enter-to-Play mode. Trust me, it worths having in a Unite Keynotes because it will change how we will work with large size projects forever. Unable to scale up to large projects has been one of the weakest points in Unity and it will alleviate the problem quite a bit.

Please understand that I’m trying to separate the large project into smaller parts with Addressable out of necessity not because I just want to. And I really don’t want to do this if the Editor could handle a large project. Besides, having multiple projects has more side benefits because you can separate the projects depends on the work responsibilities for more efficient and better security.

I understand that loading Addressables into the Editor is not on the original spec but I think it’s a very reasonable feature. You may say, it goes against the normal workflow but I can reason that there are many extensions already exists in the Editor that were not in the normal workflow, such as Prefabs editor, DOTS sub-scenes. To me, Addressbles is just read-only asset and I don’t expect to edit them after loading. If you treat Addressable as read-only assets, it will simplify many problems, am I wrong? And having it pre-loaded into the editor will also reduce the time to load every time you click on “Play” if you have Fast-Enter-to-Play mode enabled.

Please consider adding the support and it’s really crucial for the success of my project.

Thank you very much for your understanding.

7 Likes

Why isn’t this written front and centre on the documentation for the addressables system? At least in the migration guide. Instead I need to find a random forum thread to learn that this new system, which the docs proudly proclaim can be seamlessly migrated to, actually does not work for many common uses (edit mode or synchronous usage)?

11 Likes

Ran in to this today. I suppose it’s still not possible to use Addressables outside Play mode?
I have a lot of scripts automating work on fbx models. Migrated from Resources to Addressables and just realized I don’t know how to replace Resources.Load in these cases.

1 Like

I was able to load an Audio Clip addressable in edit mode with the following code. You can change it to Game Object by changing the method signature and all instances of “AudioClip”.

    public static AudioClip EditModeLoadAddressable(AssetReference assetRef) {
        if (!IsAddressableValid(assetRef)) { // seems a good way to check if you chose something other than "none".
            return null;
        }

        var path = AssetDatabase.GUIDToAssetPath(assetRef.RuntimeKey.ToString());
        var type = AssetDatabase.GetMainAssetTypeAtPath(path);
        if (type != typeof(AudioClip)) {
            Debug.Log("Your addressable is not an Audio Clip. Can't play.");
            return null;
        }
        var clip = AssetDatabase.LoadAssetAtPath<AudioClip>(path);
        return clip;
    }

        public static bool IsAddressableValid(AssetReference addressable) {
            if (addressable == null) {
                return false;
            }

#if UNITY_EDITOR
            return addressable.editorAsset != null;
#else
        return addressable.RuntimeKeyIsValid();
#endif
        }
9 Likes

Sharing this other utility function we made for those using string addresses directly instead.

        /// <summary>
        /// Load the asset using the asset database. (or return null if the address does not match any)
        /// </summary>
        /// <param name="address"></param>
        /// <typeparam name="TValue"></typeparam>
        /// <returns></returns>
        public static TValue LoadInEditor<TValue>(string address)
            where TValue : Object
        {
            AddressableAssetSettings settings = AddressableAssetSettingsDefaultObject.Settings;

            List<AddressableAssetEntry> allEntries = new List<AddressableAssetEntry>(settings.groups.SelectMany(g => g.entries));
            AddressableAssetEntry       foundEntry = allEntries.FirstOrDefault(e => e.address == address);
            return foundEntry != null
                       ? AssetDatabase.LoadAssetAtPath<TValue>(foundEntry.AssetPath)
                       : null;
        }

Note that writing custom code for editor is actually not that big of a deal, because you should not be using a single point of access for your loaded assets in the first place. For example, if you are loading inside of a ScriptableObject and keeping the loaded instance there, you are using Addressables wrong and you will not benefit from its automatic ref-count management.

Loading should happen where the asset is actually needed, i.e. your editor script or a component/functionality which uses the asset, not inside the asset itself. As such it is simply a matter of using a different function, such as the one I shared above. It would be nice if could use the same APIs, but this is at worse a minor inconvenience.

(Obviously I am not talking about actual support of the whole addressables system in editor which would increase performance and scalability. I just thought some of the frustration in this thread may actually be from incorrect usage of addressables, especially those migrating from a synchronous solution like Resources who would be inclined to handle loading lazily and synchronously as part of their classes, such as a getter which fetches the asset and caches it on first use.)

Edit: note that the function above will not scale well the larger your project gets and can degrade performances. Unfortunately this is the only way to achieve this right now.

4 Likes

This seems great for the catalog already loaded, however I am facing a problem where I have a remote content catalog file which I want to read and then get the AssetPath from that. This script seems to work for only the loaded content catalog, is there anyway to get the AddressableAssetSettings for another catalog because the LoadContentCatalogAsync function doesnt work in editor either.

1 Like
  • 1 we would also like to load addressables in edit mode.
1 Like

Read my post up 4. It works for me.

Hey thanks for the ping, got it working with your reference!

Sure no problem. Glad it’s working for you.

+1 for editor support.

After using this solution, I maintain that I would very much appreciate better support for addressables in edit mode so that I don’t need to write and maintain extra code just to repeat the same functionality of the library while in edit mode.