unity游戏开发之NGUI的UISprite染色



      游戏的UI开发中常常会遇到染色问题。比如button失效变灰的效果,同一个道具通过策划表配的颜色值染上红绿蓝紫等颜色,效果例如以下



       最笨最挫的方法当然是让美术多出几个资源图。这种一个缺点是浪费资源,在手游上资源的大小显得尤为重要。并且不好维护和复用。改动一个资源须要同一时候改动其它颜色的多个同类资源。一种比較好的解决方式是通过更换渲染的材质,用染色材质取代原来的材质。然后把原来材质的主纹理和透明纹理赋值给新的材质。这样就能够实现用程序动态切换颜色。并且仅仅须要一个基础资源。节省资源大小。easy维护。

以下给出这个解决方式的流程图。例如以下图所看到的



       以下写一个样例,通过按r键,g键。b键。y键来动态切换染红色,染绿色。染蓝色,灰化效果。项目的GameObject图例如以下


染色普通颜色的材质相应的shader例如以下

[plain]
  1. Shader "Winter/ChangeColor" {  
  2.     Properties {  
  3.         _MainTex ("Base (RGB)", 2D) = "white" {}  
  4.         _Color ("Main Color", Color) = (1,1,1,1)  
  5.           
  6.     }  
  7.     SubShader {  
  8.         Tags { "Queue" = "Transparent+10" }  
  9.         LOD 200  
  10.          Pass  
  11.          {  
  12.          ZWrite On  
  13.          ZTest Off  
  14.   
  15.   
  16.             Blend SrcAlpha OneMinusSrcAlpha  
  17.             Lighting Off  
  18.             //Cull Off  
  19.             CGPROGRAM  
  20.             #pragma vertex vert  
  21.             #pragma fragment frag  
  22.   
  23.             #include "UnityCG.cginc"  
  24.   
  25.             sampler2D _MainTex;  
  26.             fixed4 _Color;  
  27.             float _ColorCtrl;  
  28.   
  29.             struct v2f   
  30.             {  
  31.                 float4  pos : SV_POSITION;  
  32.                 float2  uv : TEXCOORD0;  
  33.             };  
  34.   
  35.             float4 _MainTex_ST;  
  36.   
  37.             v2f vert (appdata_base v)  
  38.             {  
  39.                 v2f o;  
  40.                 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);  
  41.                 o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);  
  42.                 return o;  
  43.             }  
  44.   
  45.             fixed4 frag (v2f i) : COLOR  
  46.             {  
  47.                 fixed4 texcol = tex2D (_MainTex, i.uv);  
  48.                 result = texcol * _Color;  
  49.                 result.a = texcol.a;  
  50.   
  51.                 return result;  
  52.             }  
  53.             ENDCG  
  54.          }  
  55.     }   
  56. }  

       不同颜色要创建不同的材质,而且设置其颜色值,例如以下

   

灰化效果比較特殊,颜色值不能弄成(0,0,0,1)或者(1,1,1,1)。

须要用到灰化函数

终于颜色的r = (原图r+原图g+原图b)*0.33

终于颜色的g = (原图r+原图g+原图b)*0.33 

终于颜色的b = (原图r+原图g+原图b)*0.33

终于颜色的透明值 = 原图的透明值

依据上面。有以下的灰化shader

[plain]
  1. Shader "Winter/Gray"   
  2. {  
  3.     Properties   
  4.     {  
  5.          _MainTex ("Base (RGB)", 2D) = "white" { }  
  6.     }  
  7.     SubShader  
  8.     {  
  9.   
  10.          Tags  
  11.          {  
  12.             "Queue" = "Transparent+10"  
  13.          }  
  14.          Pass  
  15.          {  
  16.             Lighting Off  
  17.             ZTest Off  
  18.             Cull Off  
  19.             Blend SrcAlpha OneMinusSrcAlpha  
  20.             CGPROGRAM  
  21.             #pragma vertex vert  
  22.             #pragma fragment frag  
  23.   
  24.             #include "UnityCG.cginc"  
  25.   
  26.             sampler2D _MainTex;  
  27.             sampler2D _AlphaTex;  
  28.             half4 _Color;  
  29.           
  30.             struct v2f   
  31.             {  
  32.                 float4  pos : SV_POSITION;  
  33.                 float2  uv : TEXCOORD0;  
  34.             };  
  35.   
  36.             half4 _MainTex_ST;  
  37.             half4 _AlphaTex_ST;  
  38.   
  39.             v2f vert (appdata_base v)  
  40.             {  
  41.                 v2f o;  
  42.                 o.pos = mul (UNITY_MATRIX_MVP, v.vertex);  
  43.                 o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);  
  44.                 return o;  
  45.             }  
  46.   
  47.             half4 frag (v2f i) : COLOR  
  48.             {  
  49.                 half4 texcol = tex2D (_MainTex, i.uv);  
  50.                 half4 result = half4((texcol.r + texcol.g + texcol.b) * 0.33f,(texcol.r + texcol.g + texcol.b) * 0.33f,(texcol.r + texcol.g + texcol.b) * 0.33f,texcol.a);  
  51.                 return result;  
  52.             }  
  53.             ENDCG  
  54.          }  
  55.     }  
  56. }   

       接着就是要改动Ngui的UISprite源代码。加入一个渲染的材质WinterMaterial, 在读取material值的时候。假设有自己定义的渲染材质,则须要读取自己定义渲染材质

[csharp]
  1. public override Material material  
  2.     {  
  3.         get  
  4.         {  
  5.             Material mat = base.material;  
  6.   
  7.             if (mat == null)  
  8.             {  
  9.                 mat = (mAtlas != null) ?

     mAtlas.spriteMaterial : null;  

  10.                 mSprite = null;  
  11.                 material = mat;  
  12.                 if (mat != null) UpdateUVs(true);  
  13.             }  
  14.               
  15.             if (WinterMaterial!=null)  
  16.             {  
  17.                 return WinterMaterial;  
  18.             }  
  19.             else  
  20.             {  
  21.                 return mat;  
  22.             }  
  23.         }  
  24.     }  

然后。加入下面几个染色函数

[csharp]
  1. public void ShowAsRed()  
  2. {  
  3.     ShowAsColor("file:///D:/u3dAB/WinterRedMat.assetbundle", WinterRedMat);  
  4. }  
  5.   
  6. public void ShowAsGreen()  
  7. {  
  8.     ShowAsColor("file:///D:/u3dAB/WinterGreenMat.assetbundle", WinterGreenMat);  
  9. }  
  10.   
  11. public void ShowAsBlue()  
  12. {  
  13.     ShowAsColor("file:///D:/u3dAB/WinterBlueMat.assetbundle", WinterBlueMat);  
  14. }//须要加入染色值的,则须要加入材质和染色函数  
  15. public void ShowAsGray()  
  16. {  
  17.     StartCoroutine(<span style="font-family: Arial, Helvetica, sans-serif;">//自己定义的www函数</span>  
[csharp]
  1.         IzUtils.LoadAB("file:///D:/u3dAB/WinterGrayMat.assetbundle", (w) =>  
  2.         {//assetbundle是打包好的材质  
  3.             Material mat = w.assetBundle.mainAsset as Material;  
  4.             mat.mainTexture = material.mainTexture;  
  5.             WinterMaterial = mat;  
  6.             w.assetBundle.Unload(false);  
  7.             RefreshPanel(gameObject);      
  8.         })  
  9.     );   
  10. }  
  11. private void ShowAsColor(string matName, Material colorMaterial)  
  12. {  
  13.     if (WinterMaterial == null || colorMaterial != WinterMaterial)  
  14.     {  
  15.         if (colorMaterial == null)  
  16.         {  
  17.             StartCoroutine(  
  18.                 IzUtils.LoadAB(matName, (w) =>  
  19.                 {  
  20.                     Material mat = w.assetBundle.mainAsset as Material;  
  21.              mat.mainTexture = material.mainTexture;  
  22.                     colorMaterial = mat;  
  23.                     WinterMaterial = mat;  
  24.                     w.assetBundle.Unload(false);      
  25.                     RefreshPanel(gameObject);  
  26.     })  
  27.             );  
  28.         }  
  29.         else  
  30.         {  
  31.             WinterMaterial = colorMaterial;  
  32.             RefreshPanel(gameObject);  
  33.         }  
  34.     }  
  35. }  
  36. GameObject GetMostClosePanel(Transform rootTrans)  
  37. {  
  38.     if (rootTrans.GetComponent<UIPanel>() != null)  
  39.     {  
  40.         return rootTrans.gameObject;  
  41.     }  
  42.     else if (rootTrans.parent == null)  
  43.     {  
  44.         return null;  
  45.     }  
  46.     else  
  47.     {  
  48.         return GetMostClosePanel(rootTrans.parent);  
  49.     }  
  50. }  
  51.   
  52. GameObject panelObj = null;  
  53. public bool selfRefresh = true;  
  54.   
  55. void RefreshPanel(GameObject go)  
  56. {  
  57.     if (!selfRefresh)  
  58.         return;  
  59.   
  60.     if (panelObj == null)  
  61.     {  
  62.         panelObj = GetMostClosePanel(go.transform);  
  63.     }  
  64.   
  65.     if (panelObj != null)  
  66.     {  
  67.         panelObj.GetComponent<UIPanel>().enabled = false;  
  68.         panelObj.GetComponent<UIPanel>().enabled = true;  
  69.         go.SetActive(false);  
  70.         go.SetActive(true);  
  71.     }  
  72. }  


主程序调用方法

[csharp]
  1. using UnityEngine;  
  2. using System.Collections;  
  3.   
  4. public class ChangeColorExample : MonoBehaviour {  
  5.         private UISprite m_kSprite;  
  6.     void Start ()   
[csharp]
  1.    {  
  2.         GameObject obj = GameObject.Find("Root/Camera/Anchor/Panel/Sprite");  
  3.         m_kSprite = obj.GetComponent<UISprite>();  
  4.   
  5.   
  6. void Update()  
  7. {  
  8.     if (Input.GetKeyUp(KeyCode.R))  
  9.     {  
  10.         m_kSprite.ShowAsRed();  
  11.     }  
  12.     else if (Input.GetKeyUp(KeyCode.G))  
  13.     {  
  14.         m_kSprite.ShowAsGreen();  
  15.     }  
  16.     else if (Input.GetKeyUp(KeyCode.B))  
  17.     {  
  18.         m_kSprite.ShowAsBlue();  
  19.     }  
  20.     else if (Input.GetKeyUp(KeyCode.Y))  
  21.     {  
  22.         m_kSprite.ShowAsGray();  
  23.     }  
  24. }  

核心的代码部分如上图所看到的,这样就能够通过按键rgby来切换染红绿蓝灰的效果。

效果如上面第一幅图所看到的。

       总结,用程序来实现动态染色能够高度复用资源,节省空间大小。

可是资源的划分须要注意的一点是,假设在一个UIPanel里面有两个不同图集须要用同一个材质进行染色,那么会出现当中的一个出现纹理错乱的现象。眼下的解决方式是做多一个颜色值同样的材质。不同的图集用不同的染色材质,这样能够解决上面说的纹理错乱现象。

还有一个方法是设法把不同的图集弄到一块。这样也能够避免这个问题。


原文地址:https://www.cnblogs.com/brucemengbm/p/6893801.html