【Unity学习过程踩坑记录】高亮显示敌人出生点地砖时所有地砖全部闪烁
问题描述:
今天实现的一个功能是在敌人实例化之前,先让出生点的地砖闪烁一秒,再生成敌人。使用协程实现。先获取目标地砖的材质属性,确定初始色和高亮色,然后使用Mathf.PingPong()产生振荡效果,进行初始色到高亮色的插值变化。结果发现所有的地砖同时闪烁。
解决:
经过调试发现确实获取到了出生点处的地砖及其材质,但是获取材质的方法用的是GetComponent<Renderer>().sharedMaterial,应该是GetComponent<Renderer>().material。之前在渲染障碍物颜色时使用过这个sharedMaterial,当时在网上查了一下两者的区别:
- sharedMaterial 是共用的 Material,称为共享材质。修改共享材质会改变所用使用该材质的物体,并且编辑器中的材质设置也会改变。
- material 是独立的 Material,返回分配给渲染器的第一个材质。修改材质仅会改变该物体的材质。如果该材质被其他的渲染器使用,将克隆该材质并用于当前的渲染器。
所以当我改变sharedMaterial的颜色时,所有地砖都使用的改材质,所以全部都会变色。那么问题又来了,之前生成的那么多障碍物使用的就是sharedMaterial,为什么还能显示那么多种颜色呢?以下是源码:
// 渲染障碍物颜色 Renderer obstacleRenderer = newObstacle.GetComponent<Renderer>(); Material obstacleMaterial = new Material(obstacleRenderer.sharedMaterial); // 使用material属性可能会造成内存泄漏 float colourPercent = randomCoord.y / (float)currentMap.mapSize.y; // 提前转化为float防止int整除 obstacleMaterial.color = Color.Lerp(currentMap.foregroundColour, currentMap.backgroundColour, colourPercent); obstacleRenderer.sharedMaterial = obstacleMaterial;
虽然中间new了新的材质,但是最后也是直接将sharedMaterial换成新的obstacleMaterial,按道理来说所有的障碍物也应该会变成同一种颜色?尝试将代码改成如下,发现所有障碍物都变成同一种颜色了。
Renderer obstacleRenderer = newObstacle.GetComponent<Renderer>(); Material obstacleMaterial = new Material(obstacleRenderer.sharedMaterial); // 使用material属性可能会造成内存泄漏 float colourPercent = randomCoord.y / (float)currentMap.mapSize.y; // 提前转化为float防止int整除 Color color = Color.Lerp(currentMap.foregroundColour, currentMap.backgroundColour, colourPercent); obstacleRenderer.sharedMaterial.color = color;
两者区别在于,第一种直接将物体的sharedMaterial换成才new出来的新的Matrial,而第二种是将sharedMaterial的color属性换成新的Color。
我首相尝试输出sharedMaterial变化前后的内存地址,但是发现两种代码输出的地址都发生了变化,那么从此角度寻求答案无果。C#获取引用类型地址:
// 需要引入System.Runtime.InteropServices; GCHandle h = GCHandle.Alloc(o, GCHandleType.Pinned); IntPtr addr = h.AddrOfPinnedObject(); Debug.Log("0x" + addr.ToString("x");
后来直接模拟了一下。首先创建三个Cube:cube1、cube2、cube3,两个Material:mat1、mat2。将mat1(绿色)附加给cube1,mat2(黄色)附加给cube2和cube3,如图所示。
首先尝试代码:
Material newMat = new Material(cube1.GetComponent<Renderer>().sharedMaterial); cube2.GetComponent<Renderer>().sharedMaterial.color = newMat.color;
运行后cube2和cube3都变成了绿色,其挂载的Material仍然是mat2,且停止运行后mat2仍然是绿色。这符合之前所述的sharedMaterial介绍。
手动恢复mat2的颜色,然后改变代码:
Material newMat = new Material(cube1.GetComponent<Renderer>().sharedMaterial); //cube2.GetComponent<Renderer>().sharedMaterial.color = newMat.color; cube2.GetComponent<Renderer>().sharedMaterial = newMat;
此时运行后发现,cube2的颜色变成了绿色,mat2未改变,cube3颜色没变。观察Inspector面板进一步发现,cube2的Material变成了mat1,cube3的Material仍然是mat2。结束游戏运行之后,cube2又变回了黄色,Material为mat2。
到此为止就很明显了,文章开头两种代码,第一种使用xxx.shareMaterial == yyy直接改变了该组件对原Material的指引,指向了新的Material,此时改变其Color属性并不会影响原来的Material,所以挂载原来Material的物体仍然是原来那个色。第二种方法仍然指向原来的Material,但是改变了其Color属性,这样一来所有挂载这个Material的物体也就全部变色了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!