一、要达到的效果
用户点击【走廊】菜单的时候,其他部件穿透显示,只有走廊正常显示,并高亮闪烁。
二、思路
- 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>();