Error Creating Assets from Code! (bug for Sure)

Hello Everyone,

I am faced with a weird bug again which prevent me from creating Assets via Code using the AssetDatabase.CreateAsset API.

My function to create a Material. (snipped from unity Documentation)

public void CreateMaterial()
        {
            GeneratedMaterial = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));
            GeneratedMaterial.SetTexture("_MainTex", TextureAtlas);
            var materialPath = FoldersPaths.RenderingAssetsHoldersPath + $"\\{SceneName}_Material.mat";
            AssetDatabase.CreateAsset(GeneratedMaterial, materialPath);

            // Print the path of the created asset
            Debug.Log(AssetDatabase.GetAssetPath(GeneratedMaterial));
        }

error:

UnityException: Creating asset at path Assets\Scripts\ScriptableObjects\Map Asset Holders\Instances\Map5_Material.mat failed.

note:
calling this same function from the Editor using [MenuItem(“MyMenu/Do Something”)] works.

Hello @Opeth001 ,

The AssetDatabase.CreateAsset is an Editor-only method and can’t be called from GameObject.

Could you please check if you’re calling your ‘CreateMaterial’ method not in runtime and not from a GameObject?

Thank you!

2 Likes

Hello @DiegoDePalacio_1 ,
Thank you for the reply!
can you please clarify what you mean by not from a Gameobject?

im trying to generate a single material by packaging all the textures of Material using the same Shader type and updating all the concerned meshes UVs .
this process is performed when a SubScene is closed (Edit Time) using GameObjectConversionSystem.
is it considered a as calling from a GameObject?

My Test code.

 [UpdateInGroup(typeof(GameObjectBeforeConversionGroup))]
    public class SceneRenderingAssetsCollectorSystem : GameObjectConversionSystem
    {
        public int MaxOriginalTextureResolution = 512;

        RenderingAssetsHolder renderingAssetsHolder;

        private List<Material> materialsWithoutTexture;

        protected override void OnStartRunning()
        {
            base.OnStartRunning();

            //Generate the RenderingAssetsHolder if not existing
            var (renderingAssetsHolders,_) = ScriptableObjectUtils.GetAllInstances<RenderingAssetsHolder>(new string[] {FoldersPaths.RenderingAssetsHoldersPath});

            /* // Not Working so im creating the Scriptable Object Manually from the Editor
            bool rahExists = false;
            var activeSceneName = SceneManager.GetActiveScene().name;

            Debug.Log($"activeSceneName : {activeSceneName}");
            for (var i = 0; i < renderingAssetsHolders.Length; i++)
            {
                if (renderingAssetsHolders[i].SceneName == activeSceneName)
                {
                    rahExists = true;
                    renderingAssetsHolder = renderingAssetsHolders[i];
                }
            }


            if (rahExists == false)
            {
                //FIXME: The Editor is not Able to Create the ScriptableObject Asset
                renderingAssetsHolder = ScriptableObject.CreateInstance<RenderingAssetsHolder>();

                renderingAssetsHolder = ScriptableObjectUtils.CreateScriptableObject<RenderingAssetsHolder>(FoldersPaths.RenderingAssetsHoldersPath+"\\"+ renderingAssetsHolders.Length + "_Asset_Holder.asset");
                Debug.Log("New RenderingAssetsHolder Created for this Scene");
            }
            else
            {
                Debug.Log("RenderingAssetsHolder already Existing for this Scene");
            }*/
          
            if(renderingAssetsHolders.Length > 0)
            {
                renderingAssetsHolder = renderingAssetsHolders[0];
                Debug.Log($"Rendering Assets Holder Found : {renderingAssetsHolder.SceneName}");
            }

            materialsWithoutTexture = new List<Material>();
        }

        /// <summary>
        /// Pack All Collected Texture to a single Atlas
        /// </summary>
        public void PackTextures(ref RenderingAssetsHolder rah)
        {
            var atlas = new Texture2D(rah.AtlasResolution, rah.AtlasResolution);

            var tempTexturesArray = new Texture2D[rah.OriginalTextures.Count];

            for (var i = 0; i < rah.OriginalTextures.Count; i++)
                tempTexturesArray[i] = duplicateTexture(rah.OriginalTextures[i]);

            rah.AtlasUvs = atlas.PackTextures(tempTexturesArray, 2, rah.AtlasResolution);

            var path = FoldersPaths.RenderingAssetsHoldersPath + $"\\{rah.SceneName}_Atlas.png";
            //AssetDatabase.CreateAsset(atlas, path); // Not working as workaround i use the line above
            File.WriteAllBytes(path, atlas.EncodeToPNG());
            AssetDatabase.ImportAsset(path);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();

            rah.TextureAtlas = AssetDatabase.LoadAssetAtPath<Texture2D>(path);


            rah.GeneratedMaterial.SetTexture("_MainTex", rah.TextureAtlas);

            EditorUtility.SetDirty(rah);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();
        }

        // Im using this Function as workaround cause  Enabling/Disabling Textures Assets IsReadable field from script is not allowed. (maybe the Same Bug)
        Texture2D duplicateTexture(Texture2D source)
        {
            RenderTexture renderTex = RenderTexture.GetTemporary(
                        source.width,
                        source.height,
                        0,
                        RenderTextureFormat.Default,
                        RenderTextureReadWrite.Linear);

            Graphics.Blit(source, renderTex);
            RenderTexture previous = RenderTexture.active;
            RenderTexture.active = renderTex;
            Texture2D readableText = new Texture2D(source.width, source.height);
            readableText.ReadPixels(new Rect(0, 0, renderTex.width, renderTex.height), 0, 0);
            readableText.Apply();
            RenderTexture.active = previous;
            RenderTexture.ReleaseTemporary(renderTex);
            return readableText;
        }

 
        public void CreateMaterial(ref RenderingAssetsHolder rah)
        {
            rah.GeneratedMaterial = new Material(Shader.Find("Universal Render Pipeline/Simple Lit"));

            var materialPath = FoldersPaths.RenderingAssetsHoldersPath + $"\\{rah.SceneName}_Material.mat";
            AssetDatabase.CreateAsset(rah.GeneratedMaterial, materialPath);

            // Print the path of the created asset
            Debug.Log(AssetDatabase.GetAssetPath(rah.GeneratedMaterial));
        }

        protected override void OnUpdate()
        {
         
            materialsWithoutTexture.Clear();


            Entities.ForEach((MeshRenderer meshRenderer, MeshFilter meshFilter) =>
            {
                var sharedMaterial = meshRenderer.sharedMaterial;


                if (sharedMaterial == null)
                {
                    Debug.Log($"this GameObject '{meshRenderer.name}'  do not Contain a sharedMaterial");
                    return;
                }

                if (sharedMaterial.shader.name != "Universal Render Pipeline/Simple Lit")
                    return;



                var mainTexture = meshRenderer.sharedMaterial.GetTexture("_MainTex") as Texture2D;



                if (null == mainTexture)
                {
                    if (materialsWithoutTexture.Contains(sharedMaterial))
                        return;

                    Debug.Log($"The material Linked to this GameObject '{meshRenderer.name}' do not Contain a '_MainTex'");
                    materialsWithoutTexture.Add(sharedMaterial);
                    // Debug.Log("Path Added!");
                    return;
                }

                /*
                if (mainTexture == null)
                {
                    Debug.Log($"The material Linked to this GameObject '{meshRenderer.name}' do not Contain a mainTexture");
                    return;
                }
                */

                if (!renderingAssetsHolder.OriginalTextures.Contains(mainTexture))
                {
                    if (mainTexture.width > MaxOriginalTextureResolution)
                    {
                        Debug.Log($"the material {sharedMaterial.name} with Texture '{mainTexture.name}' has a resolution is higher of {mainTexture.width} which is higher than the threshhold {MaxOriginalTextureResolution} ");
                    }
                    renderingAssetsHolder.OriginalTextures.Add(mainTexture);
                }


                var sharedMesh = meshFilter.sharedMesh;
                if (sharedMesh == null)
                {
                    Debug.Log($"this GameObject '{meshRenderer.name}'  do not Contain a sharedMesh");
                    return;
                }

                if (!renderingAssetsHolder.SharedMeshes.Contains(sharedMesh))
                {
                    renderingAssetsHolder.SharedMeshes.Add(sharedMesh);
                }
            });

          //  CreateMaterial(ref renderingAssetsHolder);
            PackTextures(ref renderingAssetsHolder);
            EditorUtility.SetDirty(renderingAssetsHolder);
            AssetDatabase.SaveAssets();
            AssetDatabase.Refresh();


            materialsWithoutTexture.Clear();
            Debug.Log("SceneRenderingAssetsCollectorSystem end!");
        }
    }

btw event calling ScriptableObjectUtils.CreateScriptableObject api is giving the same error.
calling this function from the Mono.Start Methode works.
i’m really confused.