Unity【小问题】----URP项目中加载AssetBundle中的预设体即使加载了依赖的材质依然是紫色的问题

一、问题显示

①当前项目是URP

②已经正确让预设体相关联的材质被打包到AB包中,且AB包是其他平台下显示(比如我这里当前平台是安卓)

③在编辑器模式下加载显示对应的预设体模型

但是它加载显示的模型依然是紫色的!!!

但是我们可以看到它的材质实际上是已经加载到内存当中了的

**原因:**其实打包出去到对应平台后它也是可以正常显示的,只是这里是编辑器模式下。它这个材质的对应Shader没有能够被正确设置和找到,我们需要让他再自动找一遍。

二、解决方案

这里给出的方案只是为了在编辑器模式下确保能够正确显示,方便我们进行开发和调试。

解决方案代码来自于OverStack:3d - Unity LoadAssetBundles changes Material into Pink ? (URP) - Stack Overflow

1.随便在一个地方写一个该工具类,里面附带对应的工具方法

cs 复制代码
using TMPro;
using UnityEngine;
using UnityEngine.UI;

#if UNITY_EDITOR
    public static class AssetBundleEditorUtil
    {
        public static void FixShadersForEditor(GameObject prefab)
        {
            var renderers = prefab.GetComponentsInChildren<Renderer>(true);
            foreach (var renderer in renderers)
            {
                ReplaceShaderForEditor(renderer.sharedMaterials);
            }

            var tmps = prefab.GetComponentsInChildren<TextMeshProUGUI>(true);
            foreach (var tmp in tmps)
            {
                ReplaceShaderForEditor(tmp.material);
                ReplaceShaderForEditor(tmp.materialForRendering);
            }
            
            var spritesRenderers = prefab.GetComponentsInChildren<SpriteRenderer>(true);
            foreach (var spriteRenderer in spritesRenderers)
            {
                ReplaceShaderForEditor(spriteRenderer.sharedMaterials);
            }

            var images = prefab.GetComponentsInChildren<Image>(true);
            foreach (var image in images)
            {
                ReplaceShaderForEditor(image.material);
            }
            
            var particleSystemRenderers = prefab.GetComponentsInChildren<ParticleSystemRenderer>(true);
            foreach (var particleSystemRenderer in particleSystemRenderers)
            {
                ReplaceShaderForEditor(particleSystemRenderer.sharedMaterials);
            }

            var particles = prefab.GetComponentsInChildren<ParticleSystem>(true);
            foreach (var particle in particles)
            {
                var renderer = particle.GetComponent<Renderer>();
                if (renderer != null) ReplaceShaderForEditor(renderer.sharedMaterials);
            }
        }

        public static void ReplaceShaderForEditor(Material[] materials)
        {
            for (int i = 0; i < materials.Length; i++)
            {
                ReplaceShaderForEditor(materials[i]);
            }
        }

        public static void ReplaceShaderForEditor(Material material)
        {
            if (material == null) return;

            var shaderName = material.shader.name;
            var shader = Shader.Find(shaderName);

            if (shader != null) material.shader = shader;
        }
    }
#endif

2.在AB包加载得到模型之前,调用一次里面的FixShadersForEditor(要修复的GameObject)

cs 复制代码
        //Load Res
        if (isSync)
        {
            T res = _abDic[abName].LoadAsset<T>(resName);
#if UNITY_EDITOR
            if(res is GameObject)
            {
                AssetBundleEditorUtil.FixShadersForEditor(res as GameObject);
            }
#endif
            callback?.Invoke(res);
        }
        else
        {
            var abr = _abDic[abName].LoadAssetAsync<T>(resName);

            yield return abr;
            T res = abr.asset as T;
#if UNITY_EDITOR
            if(res is GameObject)
            {
                AssetBundleEditorUtil.FixShadersForEditor(res as GameObject);
            }
#endif
            callback?.Invoke(res as T);
        }

①我这里是封装了一个加载AB包的同步异步方法,总之就是在正式使用(实例化)这个预设体之前,调用一次该方法即可

cs 复制代码
        //假装这个物体是刚刚从AB包加载出来的
        GameObject obj = LoadFromAB();
        //传入到工具类中对其进行修复材质显示
        AssetBundleEditorUtil.FixShadersForEditor(obj);
        //再实例化使用它
        Instantiate(obj);

②更简洁的伪代码使用方式如上。

三、最终再运行的效果

可以看到在编辑器模式下加载出来的预设体,它的材质可以正常着色了的。

OK,问题解决。

四、AB包加载管理器代码留存展示

这里只是为了存档记录,不必看的

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// Load Res From the AB package
/// </summary>
public class ABMgr : SingletonAutoMono<ABMgr>
{
    private AssetBundle _mainAB = null;          //the main AssetBunld
    private AssetBundleManifest _manifest = null;    //the mainfest form the mainAB
    private Dictionary<string, AssetBundle> _abDic = new Dictionary<string, AssetBundle>();  //to record which ab has be loaded

    /// <summary>
    /// The assetBundle read place
    /// </summary>
    private string _PathUrl
    {
        get
        {
            return Application.streamingAssetsPath ;
        }
    }

    private string _MainName
    {
        get
        {
#if UNITY_IOS
            return "IOS";
#elif UNITY_ANDROID
            return "Android";
#else
            return "PC";
#endif
        }
    }

    /// <summary>
    /// Load the MainAB
    /// </summary>
    private void LoadMain()
    {
        if(_mainAB == null)
        {
            _mainAB = AssetBundle.LoadFromFile($"{_PathUrl}/{_MainName}");
            if(_mainAB != null)
            {
                _manifest = _mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            }
            else
            {
                Debug.LogError("The Main AB is not be find");
            }
        }
    }

    /// <summary>
    /// Load Res form the ab
    /// </summary>
    public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callback, bool isSync = false) where T : Object
    {
        StartCoroutine(RealLoadRes<T>(abName, resName, callback, isSync));
    }

    private IEnumerator RealLoadRes<T>(string abName, string resName, UnityAction<T> callback, bool isSync = false) where T : Object
    {
        LoadMain();

        //Load Dependenies
        string[] dependeniesNameArray = _manifest.GetAllDependencies(abName);
        for(int i = 0; i < dependeniesNameArray.Length; i++)
        {
            if (isSync)
            {
                if (!_abDic.ContainsKey(dependeniesNameArray[i]))
                {
                    _abDic.Add(dependeniesNameArray[i], AssetBundle.LoadFromFile($"{_PathUrl}/{dependeniesNameArray[i]}"));
                }
            }
            else
            {
                if (!_abDic.ContainsKey(dependeniesNameArray[i]))
                {
                    print("DependName is " + dependeniesNameArray[i]);
                    //Load this ab with async
                    _abDic.Add(dependeniesNameArray[i], null);
                    var abcr = AssetBundle.LoadFromFileAsync($"{_PathUrl}/{dependeniesNameArray[i]}");

                    yield return abcr;

                    _abDic[dependeniesNameArray[i]] = abcr.assetBundle;
                }
                else
                {
                    //means that this ab is been loading now, just wait
                    while(ReferenceEquals(_abDic[dependeniesNameArray[i]], null))
                    {
                        yield return null;
                    }
                }
            }
        }

        //Load Target
        if (isSync)
        {
            if (!_abDic.ContainsKey(abName))
            {
                _abDic.Add(abName, AssetBundle.LoadFromFile($"{_PathUrl}/{abName}"));
            }
        }
        else
        {
            if (!_abDic.ContainsKey(abName))
            {
                _abDic.Add(abName, null);
                var abcr = AssetBundle.LoadFromFileAsync($"{_PathUrl}/{abName}");

                yield return abcr;

                _abDic[abName] = abcr.assetBundle;
            }
            else
            {
                while(ReferenceEquals(_abDic[abName], null))
                {
                    yield return null;
                }
            }
        }


        //Load Res
        if (isSync)
        {
            T res = _abDic[abName].LoadAsset<T>(resName);
#if UNITY_EDITOR
            if(res is GameObject)
            {
                AssetBundleEditorUtil.FixShadersForEditor(res as GameObject);
            }
#endif
            callback?.Invoke(res);
        }
        else
        {
            var abr = _abDic[abName].LoadAssetAsync<T>(resName);

            yield return abr;
            T res = abr.asset as T;
#if UNITY_EDITOR
            if(res is GameObject)
            {
                AssetBundleEditorUtil.FixShadersForEditor(res as GameObject);
            }
#endif
            callback?.Invoke(res as T);
        }
    }

    /// <summary>
    /// Unload Certain AssetBundle
    /// </summary>
    /// <param name="abName"></param>
    public void UnLoadAB(string abName, bool unLoadAllLoadedObj, UnityAction<bool> callBackResult)
    {
        if (_abDic.ContainsKey(abName))
        {
            if(_abDic[abName] == null)
            {
                //the ab is still loading
                callBackResult?.Invoke(false);
                return;
            }
            else
            {
                _abDic[abName].Unload(unLoadAllLoadedObj);
                _abDic.Remove(abName);
                callBackResult?.Invoke(false);
                return;
            }
        }
    }

    public void UnLoadAll(bool unLoadAllLoadedObj)
    {
        //Stop All the coruntine
        StopAllCoroutines();
        //Unload All the abs
        AssetBundle.UnloadAllAssetBundles(unLoadAllLoadedObj);
        //Clear the abDic
        _abDic.Clear();

        //unload the main
        _mainAB = null;
        _manifest = null;
    }
}
相关推荐
YigAin40 分钟前
Unity中的Lock,到底在锁什么,什么时候该用?
unity
Var_al1 小时前
抖小Unity WebGL分包命令行工具实践指南
unity·游戏引擎·webgl
lGSrsEpqUxi3 小时前
双馈风机风电场经串补并网次同步振荡/谐振仿真模型探索
材质
天人合一peng3 小时前
unity 通过代码修改button及其名字字体的属性
unity·游戏引擎
GLDbalala7 小时前
Unity基于自定义管线实现经典经验光照模型
unity·游戏引擎
格林威8 小时前
Baumer相机玻璃制品裂纹自动检测:提高透明材质检测精度的 6 个关键步骤,附 OpenCV+Halcon 实战代码!
人工智能·opencv·视觉检测·材质·工业相机·sdk开发·堡盟相机
心疼你的一切9 小时前
Unity异步编程神器:Unitask库深度解析(功能+实战案例+API全指南)
深度学习·unity·c#·游戏引擎·unitask
呆呆敲代码的小Y11 小时前
【Unity 实用工具篇】 | Book Page Curl 快速实现翻书效果
游戏·unity·游戏引擎·u3d·免费游戏·翻书插件
AC梦1 天前
unity中如何将UI上的字高清显示
ui·unity
top_designer1 天前
Materialize:手绘地表太假?“PBR 纹理炼金术” 5分钟生成次世代材质
游戏·3d·aigc·材质·设计师·游戏美术·pbr