原文地址:http://bbs.9ria.com/thread-434334-1-1.html
需求分析
- 直接调整Sprite或者Texture的Color值达不到美术所要的效果
- 节省资源,为了实现去色效果,美术不必提供另外一张PS处理过后的图集,只需要程序修改shader动态处理即可怎么做
- 修改NGUI使用的Shaders(新添加四个,为什么后文讲解)
- 制作XX_Gary图集
- 编写统一逻辑接口针对Sprite切换图集,针对UITexture切换Shader
注意下NGUI使用的默认shader,该例子使用的是NGUI3.6.6,NGUI默认使用的一系列shader为下图 1 2 3 结尾,稍微提前一点的版本shader下图(SoftClip)(AlphaClip)复制出来四个按照原来的规则修改名字,注意一定要前缀一致,因为NGUI内部实现裁剪是需要通过修改后缀找到相应的shader进行替换实现的,然后在修改四个shader的内容,找到每个Shader内容的frag函数,在最后都加上同一段代码即可(前面各种计算得到col,再返回前将最终的col结果在进行一次处理)
1
2
3
4
5
6
7
8
|
<font color=
"#000000"
>half4 frag (v2f IN) : COLOR
{
// ......
//For gray shader change rgb value
float
grey = dot(col.rgb, float3(0.299, 0.587, 0.114));
col.rgb = float3(grey, grey, grey);
return
col;
}</font>
|
游戏中针对需要置灰的元素图集需要重新制作一个额外的Gray图集,Normal图集和Gray图集使用同一个2DTexture,使用不同的材质
编写处理逻辑
统一的处理接口,基本原理是针对UISprite更换图集,针对UITexture更换Shader即可
具体的一种实现方式如下
<font color="#000000">using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class GrayUIController : MonoBehaviour
{
// UISprite : 更换图集(正常和置灰图集切换)
// UITexture:更换Shader为正常Shader和置灰使用的Shader
/
1.将同一个图集中的图片单独的变灰操作
2.将UITexture变灰去色
/
// 维护一个图集List
// 置灰图集
// 正常图集
public List grayAtlas;
public List normalAtlas;
public Hashtable shaderTable = new Hashtable();
public enum AtlasName
{
EM_AtlasName_None = -1,
EM_AtlasName_CommonAtlas,
}
private static GrayUIController instance = null;
public static GrayUIController GetInstance()
{
return instance;
}
void Awake()
{
instance = this;
}
///
/// 将制定GameObject存在的UITexture和UISprite去色,变灰
///
/// 目标物体
/// 是否变灰
/// 制定图集
public void ChangeObjectsToGray(GameObject targetObj, bool gray, AtlasName atlasName = AtlasName.EM_AtlasName_None)
{
UISprite[] sprites = targetObj.GetComponentsInChildren();
UITexture[] textures = targetObj.GetComponentsInChildren();
for (int i = 0; i < sprites.Length; i++)
ChangeUISpriteToGray(sprites, gray, atlasName);
for (int i = 0; i < textures.Length; i++)
ChangeUITextureToGary(textures, gray);
}
public bool ChangeUISpriteToGray(UISprite targetSprite, bool toGray, AtlasName atlasName = AtlasName.EM_AtlasName_None)
{
bool toGarySucess = false;
if (targetSprite != null)
{
List findAtlas = new List();
if (toGray)
findAtlas = grayAtlas;
else
findAtlas = normalAtlas;
UISpriteData findData = null;
string spriteName = targetSprite.spriteName;
// 如果指定图集,不用查找直接更换图集即可(高效方式)
if (atlasName != AtlasName.EM_AtlasName_None)
{
findData = findAtlas[(int)atlasName].GetSprite(spriteName);
if (findData != null)
{
targetSprite.atlas = findAtlas[(int)atlasName];
toGarySucess = true;
}
}
else
{
// 未指定图集,需要从现有的图集中查找对应的图集
for (int i = 0; i < findAtlas.Count; i++)
{
findData = findAtlas.GetSprite(spriteName);
if (findData != null)
{
targetSprite.atlas = findAtlas;
toGarySucess = true;
break;
}
}
}
}
return toGarySucess;
}
///
/// 将UITexture置灰操作
///
///
///
public void ChangeUITextureToGary(UITexture texture, bool toGray)
{
Shader newShader = null;
if (toGray)
{
// 保存UITexture原Shader
if (!shaderTable.ContainsKey(texture))
shaderTable[texture] = texture.shader;
newShader = Shader.Find("Unlit/GrayShader");
}
else
{
// 恢复原Shader
if (shaderTable.ContainsKey(texture))
newShader = (Shader)shaderTable[texture];
else
newShader = Shader.Find("Unlit/Transparent Colored");
}
texture.shader = newShader;
}
private bool changeToGray = true;
public GameObject targets;
public void TestChangeGray()
{
ChangeObjectsToGray(targets, changeToGray, AtlasName.EM_AtlasName_CommonAtlas);
changeToGray = !changeToGray;
}
}</font>
问题
为何要复制出来四个shader,如果只复制出来一个基本shader而没有复制另外三个处理裁剪的shader,当你在ScrollView等还有裁剪的Panel中存在变灰元素的时候就会提示"找不到xxshader"的错误,具体随着错误就知道在UIDrawCall中进行了裁剪过程的Shader替换(UIDrawCall中的CreateMaterial函数)
<font color=
"#000000"
>
void
CreateMaterial ()
{
// .....
if
(shaderName.StartsWith(
"Hidden/"
))
shaderName = shaderName.Substring(7);
// Legacy functionality
const
string
soft =
" (SoftClip)"
;
shaderName = shaderName.Replace(soft,
""
);
if
(mClipCount != 0)
{
shader = Shader.Find(
"Hidden/"
+ shaderName +
" "
+ mClipCount);
if
(shader ==
null
) Shader.Find(shaderName +
" "
+ mClipCount);
// Legacy functionality
if
(shader ==
null
&& mClipCount == 1)
{
mLegacyShader =
true
;
shader = Shader.Find(shaderName + soft);
}
}
else
shader = Shader.Find(shaderName);
// ......
}</font>
如果创建了一个新的图集,会不会造成内存存在两张贴图,答案是不会,因为两个图集使用的同一张贴图,只不过材质球不一样而已,你可以在运行的时候打开Profiler查看图集引用的贴图,然后比对下内存使用情况即可
效果演示
Demo下载
(导入的DEMO,的NGUI文件夹可能有问题,请删除,重新导入正确的NGUI)
Unity3D学习笔记——NGUI之UIScrollView