Unity Editor 代碼中創建Prefab時無法關聯Material(Material丟失)問題

原實現如下:

private void createOne(string path)
    {
        StaticSkin skin = Utils.ReadSkins(path);
        Mesh mesh = Utils.BuildMeshBySkin(skin);

        string mp = meshPath + "/" + skin.name + ".asset";
        AssetDatabase.CreateAsset(mesh, mp);

        GameObject go = new GameObject(skin.name);
        MeshRenderer render = go.AddComponent<MeshRenderer>();
        render.sharedMaterials = new UnityEngine.Material[skin.Materials.Length];
        for (int i = 0; i < render.sharedMaterials.Length; ++i)
        {
            string materialName = skin.Materials[i].Texture;
            if (string.IsNullOrEmpty(materialName) == false)
            {
                Debug.Log("[" + materialName + "]");
                int idx = materialName.LastIndexOf('.');
                materialName = materialName.Substring(0, idx) + ".mat";
                UnityEngine.Material mat = AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(materialPath + "/" + materialName);
                render.sharedMaterials[i] = mat;
            }
        }

        MeshFilter mf = go.GetComponent<MeshFilter>();
        if (mf == null)
            mf = go.AddComponent<MeshFilter>();
        mf.sharedMesh = mesh;

        PrefabUtility.SaveAsPrefabAsset(go, prefabPath + "/" + skin.name + ".prefab");
        DestroyImmediate(go);
    }

但是發現代碼執行過後,Prefab中引用的Material全是none,模型顯示默認顏色紅色

裏面關鍵一行的錯誤在

render.sharedMaterials[i] = mat;

這裏Material數組需要一次性賦值,不能逐個賦值,所以代碼改成這樣:

private void createOne(string path)
    {
        StaticSkin skin = Utils.ReadSkins(path);
        Mesh mesh = Utils.BuildMeshBySkin(skin);

        string mp = meshPath + "/" + skin.name + ".asset";
        AssetDatabase.CreateAsset(mesh, mp);

        GameObject go = new GameObject(skin.name);
        MeshRenderer render = go.AddComponent<MeshRenderer>();
        UnityEngine.Material[] sharedMaterials = new UnityEngine.Material[skin.Materials.Length];
        for(int i = 0; i < sharedMaterials.Length; ++i)
        {
            string materialName = skin.Materials[i].Texture;
            if(string.IsNullOrEmpty(materialName) == false)
            {
                Debug.Log("[" + materialName + "]");
                int idx = materialName.LastIndexOf('.');
                materialName = materialName.Substring(0, idx) + ".mat";
                UnityEngine.Material mat = AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(materialPath + "/" + materialName);
                sharedMaterials[i] = mat;
            }
        }

        render.sharedMaterials = sharedMaterials;

        MeshFilter mf = go.GetComponent<MeshFilter>();
        if(mf == null)
            mf = go.AddComponent<MeshFilter>();
        mf.sharedMesh = mesh;

        PrefabUtility.SaveAsPrefabAsset(go, prefabPath + "/" + skin.name + ".prefab");
        DestroyImmediate(go);
    }

這段代碼中,單獨創建了一個數組UnityEngine.Material[] sharedMaterials,然後爲這個數組填充數據,最後一次性將數組傳入 Renderer

render.sharedMaterials = sharedMaterials;

 原因是這樣:

sharedMaterials是個屬性,也就是我們說的get/set,我們可以寫如下僞代碼

public class Renderer
{
    private Material[] shareMats;
    public Material[] sharedMaterials
    {
        get { retrun shareMats; }
        set
        {
            shareMats = value;
            // 做一些底層必要的操作
        }
    }
}

 從這份僞代碼中看到,如果我們使用renderer.sharedMaterials[i] = mat;

雖然把材質放進去了,但是實際上只執行了get方法,set方法卻沒有執行,註釋所代表的的那些“”底層必要的操作“”自然無法執行,這就導致你設置進去的材質不生效

而使用rendere.sharedMaterials = mats;

則會執行set方法而不是get方法,這樣,內部一些操作就會正常執行,所以材質就有效了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章