关于 Unity UGUI 中修改 Mask 组件下 Image 等子节点组件的材质无效的问题

摘要:
当各种MonoBehavior发生更改时,静态类MaskUtilities将向所有子节点发送模具状态更改消息。此函数将MaskableGraphic_ShouldRecalculateStencil中的m转换为true,以便在开始渲染时,每个组件都将被调用GetModifiedMaterial以返回适用于当前渲染的材质。当Imange m_When ShouldRecalculateStencil=true时,将在GetModifiedMaterial中返回支持Stencil的修改材质,该材质用于实现遮罩效果。因此,问题很明显。修改遮罩下的“图像”组件的原始材质不起作用,因为它不用于实际渲染。

前几天同事做了一个效果,希望在原本使用了遮罩组件 Mask 的技能图标(让技能图标变成圆形)上在添加一个置灰的功能,但问题来了:因为是动态根据游戏中玩家的条件才动态置灰,以修改 Mask 下子节点 Image 组件的材质来实现的,但是实际上怎么修改也不起作用,呈现出的效果都只停留在第一次运行时的样子。

一开始我也以为是 shader 的问题,修改的 property 没有生效,但是通过各种测试发现 shader 是已经修改成功的了。就是没有应用上,在查阅了各方资料无效果的情况下只能翻阅 unity 托管在 BitBucket 上的 UI 源码了(赞一个)。

先看看 Mask.cs,通过 StencilBuffer 实现遮罩,继承自IMaterialModifier,需要实现接口:Material GetModifiedMaterial(Material baseMaterial); 这个接口是用来修改获取的材质来实现遮罩效果。在各种 MonoBehaviour 改变时都会通过MaskUtilities 这个静态类来向所有的子节点发送 Stencil 状态改变的消息。

所以想知道为什么材质效果总是维持在第一次启动执行时,就看看Mask.OnEnable 里调用的MaskUtilities.NotifyStencilStateChanged(this); 做了什么。

1 public static voidNotifyStencilStateChanged(Component mask)
2 {
3     var components = ListPool<Component>.Get();
4 mask.GetComponentsInChildren(components);
5     for (var i = 0; i < components.Count; i++)
6 {
7         if (components[i] == null || components[i].gameObject ==mask.gameObject)
8             continue;
9 
10         var toNotify = components[i] asIMaskable;
11         if (toNotify != null)
12 toNotify.RecalculateMasking();
13 }
14     ListPool<Component>.Release(components);
15 }

看以上代码可以得知,Mask 会调用所有子节点中继承自 IMaskable 组件(Image 继承 MaskableGraphic,MaskableGraphic 继承自此)的 RecalculateMasking() 函数。该函数将

MaskableGraphic 中的m_ShouldRecalculateStencil 修改为 true,这样当开始渲染时,每个组件都会被调用GetModifiedMaterial 以返回一个适用于当前渲染的 材质(有可能会返回一个修改过的拷贝),当 Imange.m_ShouldRecalculateStencil = true 时,会在GetModifiedMaterial 中返回一个支持 Stencil 的修改过的材质,用于实现 Mask 遮罩效果,所以问题也很明了了,修改 Mask 下的 Image 组件原始的材质是不起作用的,因为实际渲染使用的不是它。

那么如何修改?只需要自己继承一个比如Image 组件,并重载GetModifiedMaterial 方法,将基类返回材质保存即可,这就是实际渲染使用的材质,当你想修改置灰时,使用这个材质即可。代码如下:

1 public classCustomImage : Image
2 {
3     public overrideMaterial GetModifiedMaterial(Material baseMaterial)
4 {
5         Material cModifiedMat = base.GetModifiedMaterial(baseMaterial);
6         //Do whatever you want with this "cModifiedMat"...
7         //You can also hold this and process it in your grayscale code.
8         //...
9         returncModifiedMat;
10 }
11 }

也可以去看看我在 Unity Answer 上对于该问题的回答:http://answers.unity3d.com/questions/1130203/ui-mask-override-my-shaders-custom-property.html

免责声明:文章转载自《关于 Unity UGUI 中修改 Mask 组件下 Image 等子节点组件的材质无效的问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【Golang笔记】Golang工具包Cobra安装记录tomcat安装出现问题及解决方法下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

[Shader]Unity裁剪四方形Image为圆形

<1>新建材质球和shader,shader请在官方下载内置shader(UI)      效果:      思路:               裁剪成圆,那就剔除大于圆半径的像素,很鸡儿简单     代码:              定义圆形半径 _Radius("半径",Range(0-0.5))=0.5              frag...

unity创建xml与加载xml

  public void CreateConfigFile (Transform cellParent) { XmlDocument xmlDoc = new XmlDocument (); string rootName = "ChessBoard"; XmlDeclaration xmldecl...

【Unity3D入门教程】Unity着色器基本使用方法

前言 “Shader即着色器,是一款运行在GPU上的程序,用来对三维物体进行着色处理、光与影的计算、纹理颜色的呈现等,从而将游戏引擎中的一个个作为抽象的几何数据存在的模型、场景和特效,以和真实世界类似的光与影的形式呈现于玩家的眼中。”这段话出自郭浩瑜老师所著《Unity3D ShaderLab开发实战详解(第2版)》。可见着色器是服务于显示阶段的,因此要想...

Unity 3D 如何修改新建脚本中的 C# 默认创建的 Script 脚本格式

  首先在Unity的安装路径下找到 Unity5EditorDataResourcesScriptTemplates路径的(81-C# Script-NewBehaviourScript.cs.txt)的模板文件,根据你的需要,把内容修改成自己要想的默认格式就好....

【Unity】伪线框渲染Shader的实现

线框Shader的渲染在游戏应用上还是有一定的需求,这次分享一个伪的线框渲染Shader。之所以称之为伪线框,是因为真正的线框应该渲染的是线,这在常规上是使用几何体着色器输出线段图元来实现。但是几何体着色器是DirectX 10的特性,所以针对移动平台,如果有少量线框渲染需求的,这个实现方法的门槛更低。 先说一下实现的原理:通过模型UV的边界来实现线框的渲...

一不注意,在Unity3D中DllImport 引起的Bug.

单要说这个Bug是很简单,但是得从头说起。  一些大型的网络游戏,或者加载比较多的一些场景时,如果要等待所有模型,贴图等各种资源文件加载完毕才能执行游戏,对用户将会是一个很头大的事情。所以就需要用到动态加载,即AssetBundles。 引用自:http://bbs.9ria.com/thread-214663-1-1.html AssetBundle...