一、要达到的效果

用户点击【走廊】菜单的时候,其他部件穿透显示,只有走廊正常显示,并高亮闪烁。

unity ui怎么让图片的白底透明 unity怎么设置透明材质_unity

二、思路

  • 1、读取材质信息:读取各个部件的材质信息并存储,恢复材质的时候用
  • 2、设置物体为透明:把物体原来的材质换成透明材质
  • 3、取消物体的透明:把物体的透明材质换成原来的材质

三、关键步骤的代码

  • 只列了关键的方法

1、提取物体的材质生成一个字典

/// <summary>
    /// 给定一个设备(3d物体root),遍历该物体的所有子部件(子部件的root名字包含[#],命名格式如【#部件名字】),读取子部件物体的材质信息,返回一个字典:
    /// Dictionary<string-部件的物体名字, List<(GameObject, Material[])-物体和它的材质数组>>
    /// key用物体名字是用来快速提取某个子部件的材质信息,用于更换材质或者恢复材质
    /// 
    /// 层级关系及命名
    /// root
    ///     #部件1
    ///        gameobject1
    ///        gameobject2
    ///        ...
    ///        gameobjectn
    ///     ...
    ///     #部件n
    /// 
    /// </summary>
    /// <param name="root">3d物体——部件的root</param>
    /// <returns> Dictionary<string-子部件名字, List<(GameObject -- 子部件的子物体, Material[] -- 物体对应的材质数组)>>  </returns>
    static Dictionary<string, List<(GameObject, Material[])>> GetMaterialsDict(GameObject root)
    {
        //提前生成空的返回值
        var partsMaterialDict = new Dictionary<string, List<(GameObject, Material[])>>();

        //获取所有的零件
        var parts = root.GetComponentsInChildren<Transform>(true).Where(x => x.name.Contains('$')).ToList();
        
        Debug.Log(parts.Count);
        Debug.Log(parts.Select(x => x.name.Split('$')[1]).First());
        parts.ForEach(x => Debug.Log(x.name.Split('$')[1]));

        //每个零件获取它的子物体的材质信息
        parts.ForEach(part =>
        {
            var partName = part.name.Split('$')[1];

            List<(GameObject, Material[])> goMatList = new List<(GameObject, Material[])>();
            part.GetComponentsInChildren<Transform>().ToList().ForEach(p =>
            {
                //Debug.Log(p.GetComponent<Renderer>());
                if (p.GetComponent<Renderer>() != null)
                {
                    goMatList.Add((p.gameObject, p.GetComponent<Renderer>().materials));
                }
            });

            partsMaterialDict[partName] = goMatList;
        });

        partsMaterialDict.Keys.ToList().ForEach(x =>
        {
            Debug.Log($"======{x}====== {partsMaterialDict[x].Count}");
        });

        return partsMaterialDict;
    }

2、其它物体设置成半透明

var partsMaterialDict = GetMaterialsDict(root);  //设备零部件的的父节点
partsMaterialDict.ToList()
            .Where(x => x.Key != name)   //给定的名字
            .SelectMany(x => x.Value).ToList()
            .ForEach(x =>
            {
                //x.Item1.GetComponent<MeshRenderer>().sharedMaterials = new Material[] { transMat }; //此处有bug,数量要做一致
                x.Item1.GetComponent<MeshRenderer>().sharedMaterials = x.Item1.GetComponent<MeshRenderer>().sharedMaterials.Select(x => transMat).ToArray();//生成对数量的透明材质数组
            });

3、所有物体设置为正常材质

partsMaterialDict.ToList()
            .SelectMany(x => x.Value).ToList()   //展开成1d数组
            .ForEach(x => x.Item1.GetComponent<MeshRenderer>().sharedMaterials = x.Item2);

4、展示一个部件

  • 1、显示全貌
  • 2、等待1秒
  • 3、其它半透明显示,自己正常显示,且高亮
/// <summary>
    /// 显示指定的构件
    /// 指定构件从所有构件中脱颖而出(其他的透明显示,自己正常显示),高亮显示自己。
    /// 1、显示全貌
    /// 2、其它半透明显示,自己正常显示,且高亮
    /// </summary>
    /// <param name="name">部件名字</param>
    /// <param name="partsMaterialDict">部件的【物体】【材质信息】字典</param>
    /// <param name="transMat">透明材质</param>
    /// <param name="partsInfos">部件信息表</param>
    /// <returns>UniTask</returns>
    public static async UniTask Show(string name, Dictionary<string, List<(GameObject, Material[])>> partsMaterialDict, Material transMat, List<PartsInfo> partsInfos)
    {
        //【1】显示所有【还原材质】
        partsMaterialDict.ToList()
            .SelectMany(x => x.Value).ToList()
            .ForEach(x => x.Item1.GetComponent<MeshRenderer>().sharedMaterials = x.Item2);

        //【2】关闭所有的高亮
        partsInfos.ForEach(x => x.part3D.GetComponent<Highlighter>().tween = false);

        await UniTask.Delay(TimeSpan.FromSeconds(1));

        //【3】其它半透明
        partsMaterialDict.ToList()
            .Where(x => x.Key != name)
            .SelectMany(x => x.Value).ToList()
            .ForEach(x =>
            {
                //x.Item1.GetComponent<MeshRenderer>().sharedMaterials = new Material[] { transMat }; //此处有bug,数量要做一致
                x.Item1.GetComponent<MeshRenderer>().sharedMaterials = x.Item1.GetComponent<MeshRenderer>().sharedMaterials.Select(x => transMat).ToArray();//生成对数量的透明材质数组
            });

        //【4】自己高亮显示        
        partsInfos.Where(x => x.name == name).First().part3D.GetComponent<Highlighter>().tween = true;
    }

5、其它用到的数据结构

/// <summary>
    /// 构件信息
    /// </summary>
    [Serializable]
    public class PartsInfo
    {
        /// <summary>
        /// 构件名字
        /// </summary>
        [Header("构件名字")]
        [SerializeField]
        public string name;

        /// <summary>
        /// 菜单button
        /// </summary>
        [Header("菜单button")]
        [SerializeField]
        public Button button;

        /// <summary>
        /// 构件的3D物体
        /// </summary>
        [Header("构件的3D物体")]
        [SerializeField]
        public GameObject part3D;

        /// <summary>
        /// 是否学习过
        /// </summary>
        [Header("是否学习过")]
        [SerializeField]
        public bool hasStudied;
    }

    /// <summary>
    /// 部件信息表
    /// </summary>
    [Header("部件信息表")]
    [SerializeField]
    public List<PartsInfo> partsInfos = new List<PartsInfo>();