欢迎来到unity学习、unity培训、unity企业培训教育专区,这里有很多U3D资源、U3D培训视频、U3D教程、U3D常见问题、U3D项目源码,我们致力于打造业内unity3d培训、学习第一品牌。
游戏的UI开发中经常会遇到染色问题。例如按钮失效变灰的效果,同一个道具通过策划表配的颜色值染上红绿蓝紫等颜色,效果如下
最笨最挫的方法当然是让美术多出几个资源图,这样的一个缺点是浪费资源,在手游上资源的大小显得尤为重要。而且不好维护和复用,修改一个资源需要同时修改其他颜色的多个同类资源。一种比较好的解决方案是通过更换渲染的材质,用染色材质代替原来的材质,然后把原来材质的主纹理和透明纹理赋值给新的材质。这样就可以实现用程序动态切换颜色,而且只需要一个基础资源,节省资源大小,容易维护。
下面给出这个解决方案的流程图,如下图所示
下面写一个例子,通过按r键,g键,b键,y键来动态切换染红色,染绿色,染蓝色,灰化效果。项目的GameObject图如下
染色普通颜色的材质对应的shader如下
[plain]
- Shader "Winter/ChangeColor" {
- Properties {
- _MainTex ("Base (RGB)", 2D) = "white" {}
- _Color ("Main Color", Color) = (1,1,1,1)
- }
- SubShader {
- Tags { "Queue" = "Transparent+10" }
- LOD 200
- Pass
- {
- ZWrite On
- ZTest Off
- Blend SrcAlpha OneMinusSrcAlpha
- Lighting Off
- //Cull Off
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- sampler2D _MainTex;
- fixed4 _Color;
- float _ColorCtrl;
- struct v2f
- {
- float4 pos : SV_POSITION;
- float2 uv : TEXCOORD0;
- };
- float4 _MainTex_ST;
- v2f vert (appdata_base v)
- {
- v2f o;
- o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
- o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
- return o;
- }
- fixed4 frag (v2f i) : COLOR
- {
- fixed4 texcol = tex2D (_MainTex, i.uv);
- result = texcol * _Color;
- result.a = texcol.a;
- return result;
- }
- ENDCG
- }
- }
- }
不同颜色要创建不同的材质,并且设置其颜色值,如下
灰化效果比较特殊,颜色值不能弄成(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]
- Shader "Winter/Gray"
- {
- Properties
- {
- _MainTex ("Base (RGB)", 2D) = "white" { }
- }
- SubShader
- {
- Tags
- {
- "Queue" = "Transparent+10"
- }
- Pass
- {
- Lighting Off
- ZTest Off
- Cull Off
- Blend SrcAlpha OneMinusSrcAlpha
- CGPROGRAM
- #pragma vertex vert
- #pragma fragment frag
- #include "UnityCG.cginc"
- sampler2D _MainTex;
- sampler2D _AlphaTex;
- half4 _Color;
- struct v2f
- {
- float4 pos : SV_POSITION;
- float2 uv : TEXCOORD0;
- };
- half4 _MainTex_ST;
- half4 _AlphaTex_ST;
- v2f vert (appdata_base v)
- {
- v2f o;
- o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
- o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
- return o;
- }
- half4 frag (v2f i) : COLOR
- {
- half4 texcol = tex2D (_MainTex, i.uv);
- 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);
- return result;
- }
- ENDCG
- }
- }
- }
接着就是要修改Ngui的UISprite源码,添加一个渲染的材质WinterMaterial, 在读取material值的时候,如果有自定义的渲染材质,则需要读取自定义渲染材质
[csharp]
- public override Material material
- {
- get
- {
- Material mat = base.material;
- if (mat == null)
- {
- mat = (mAtlas != null) ? mAtlas.spriteMaterial : null;
- mSprite = null;
- material = mat;
- if (mat != null) UpdateUVs(true);
- }
- if (WinterMaterial!=null)
- {
- return WinterMaterial;
- }
- else
- {
- return mat;
- }
- }
- }
然后,添加以下几个染色函数
[csharp]
- public void ShowAsRed()
- {
- ShowAsColor("file:///D:/u3dAB/WinterRedMat.assetbundle", WinterRedMat);
- }
- public void ShowAsGreen()
- {
- ShowAsColor("file:///D:/u3dAB/WinterGreenMat.assetbundle", WinterGreenMat);
- }
- public void ShowAsBlue()
- {
- ShowAsColor("file:///D:/u3dAB/WinterBlueMat.assetbundle", WinterBlueMat);
- }//需要添加染色值的,则需要添加材质和染色函数
- public void ShowAsGray()
- {
- StartCoroutine(<span style="font-family: Arial, Helvetica, sans-serif;">//自定义的www函数</span>
[csharp]
- IzUtils.LoadAB("file:///D:/u3dAB/WinterGrayMat.assetbundle", (w) =>
- {//assetbundle是打包好的材质
- Material mat = w.assetBundle.mainAsset as Material;
- mat.mainTexture = material.mainTexture;
- WinterMaterial = mat;
- w.assetBundle.Unload(false);
- RefreshPanel(gameObject);
- })
- );
- }
- private void ShowAsColor(string matName, Material colorMaterial)
- {
- if (WinterMaterial == null || colorMaterial != WinterMaterial)
- {
- if (colorMaterial == null)
- {
- StartCoroutine(
- IzUtils.LoadAB(matName, (w) =>
- {
- Material mat = w.assetBundle.mainAsset as Material;
- mat.mainTexture = material.mainTexture;
- colorMaterial = mat;
- WinterMaterial = mat;
- w.assetBundle.Unload(false);
- RefreshPanel(gameObject);
- })
- );
- }
- else
- {
- WinterMaterial = colorMaterial;
- RefreshPanel(gameObject);
- }
- }
- }
- GameObject GetMostClosePanel(Transform rootTrans)
- {
- if (rootTrans.GetComponent<UIPanel>() != null)
- {
- return rootTrans.gameObject;
- }
- else if (rootTrans.parent == null)
- {
- return null;
- }
- else
- {
- return GetMostClosePanel(rootTrans.parent);
- }
- }
- GameObject panelObj = null;
- public bool selfRefresh = true;
- void RefreshPanel(GameObject go)
- {
- if (!selfRefresh)
- return;
- if (panelObj == null)
- {
- panelObj = GetMostClosePanel(go.transform);
- }
- if (panelObj != null)
- {
- panelObj.GetComponent<UIPanel>().enabled = false;
- panelObj.GetComponent<UIPanel>().enabled = true;
- go.SetActive(false);
- go.SetActive(true);
- }
- }
主程序调用方法
[csharp]
- using UnityEngine;
- using System.Collections;
- public class ChangeColorExample : MonoBehaviour {
- private UISprite m_kSprite;
- void Start ()
[csharp]
- {
- GameObject obj = GameObject.Find("Root/Camera/Anchor/Panel/Sprite");
- m_kSprite = obj.GetComponent<UISprite>();
- void Update()
- {
- if (Input.GetKeyUp(KeyCode.R))
- {
- m_kSprite.ShowAsRed();
- }
- else if (Input.GetKeyUp(KeyCode.G))
- {
- m_kSprite.ShowAsGreen();
- }
- else if (Input.GetKeyUp(KeyCode.B))
- {
- m_kSprite.ShowAsBlue();
- }
- else if (Input.GetKeyUp(KeyCode.Y))
- {
- m_kSprite.ShowAsGray();
- }
- }
核心的代码部分如上图所示,这样就可以通过按键rgby来切换染红绿蓝灰的效果。效果如上面第一幅图所示。
总结,用程序来实现动态染色可以高度复用资源,节省空间大小。但是资源的划分需要注意的一点是,如果在一个UIPanel里面有两个不同图集需要用同一个材质进行染色,那么会出现其中的一个出现纹理错乱的现象。目前的解决方案是做多一个颜色值相同的材质,不同的图集用不同的染色材质,这样可以解决上面说的纹理错乱现象。另一个方法是设法把不同的图集弄到一块,这样也可以避免这个问题。
更多精彩请点击 http://www.gopedu.com/