NGUI具有流光效果的UISprite
之前做过一个流光效果(http://www.cnblogs.com/jietian331/p/4748644.html)。
现将其改进一下,与NGUI结合起来,提供一个具有流光效果的组件:UIWalkLightSprite。
效果如图:
首先是此组件的源码,如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System; 2 using UnityEngine; 3 4 /// <summary> 5 /// 具有流光效果的 UISprite。 6 /// 注意: 7 /// 1. 流光图片需命名为"Special_WalkLight"(待改善) 8 /// 2. 流光的 UISprite 暂不支持 UIPanel 剪裁(待改善) 9 /// 3. 设置 UISprite 的 color 将无效 10 /// </summary> 11 public class UIWalkLightSprite : UISprite 12 { 13 const string WalkLightShaderName = "Bleach/Walk Light Colored"; 14 15 float m_duration = 2f; 16 float m_widthLight2Sprite; 17 float m_timer; 18 float m_t1; 19 bool m_initedMat = true; 20 Vector2 m_lightURange; 21 22 void Awake() 23 { 24 // replace shader 25 if (base.atlas.spriteMaterial.shader.name != WalkLightShaderName) 26 { 27 string tempAtlasName = string.Format("[WalkLightTempAtlas_{0}]", atlas.name); 28 Transform t = transform.Find(tempAtlasName); 29 if (t == null) 30 t = ((GameObject)GameObject.Instantiate(atlas.gameObject)).transform; 31 32 t.gameObject.SetActive(false); 33 t.name = tempAtlasName; 34 t.parent = transform; 35 36 var tempAtlas = t.GetComponent<UIAtlas>(); 37 if (tempAtlas.spriteMaterial.name != WalkLightShaderName) 38 { 39 Material mat = (Material)GameObject.Instantiate(atlas.spriteMaterial); 40 mat.shader = Shader.Find(WalkLightShaderName); 41 tempAtlas.spriteMaterial = mat; 42 } 43 44 base.atlas = tempAtlas; 45 } 46 } 47 48 public override void OnFill(BetterList<Vector3> verts, BetterList<Vector2> uvs, BetterList<Color32> cols) 49 { 50 base.OnFill(verts, uvs, cols); 51 52 // set uv2 to vertex 53 var lightSprite = atlas.GetSprite("Special_WalkLight"); 54 Rect lightOuter = new Rect(lightSprite.x, lightSprite.y, lightSprite.width, lightSprite.height); 55 lightOuter = NGUIMath.ConvertToTexCoords(lightOuter, mainTexture.width, mainTexture.height); 56 57 // 用 color 把流光图片的 uv 地址传到 shader 中,故会导致设置 color 无效 58 cols.Clear(); 59 cols.Add(new Color(lightOuter.xMin, lightOuter.yMin, 0)); 60 cols.Add(new Color(lightOuter.xMin, lightOuter.yMax, 0)); 61 cols.Add(new Color(lightOuter.xMax, lightOuter.yMax, 0)); 62 cols.Add(new Color(lightOuter.xMax, lightOuter.yMin, 0)); 63 64 // data set to shader 65 m_initedMat = false; 66 m_lightURange = new Vector2(lightOuter.xMin, lightOuter.xMax); 67 68 Rect spriteOuter = new Rect(mSprite.x, mSprite.y, mSprite.width, mSprite.height); 69 spriteOuter = NGUIMath.ConvertToTexCoords(spriteOuter, mainTexture.width, mainTexture.height); 70 m_widthLight2Sprite = (lightOuter.width * spriteOuter.height) / (lightOuter.height * spriteOuter.width); 71 m_t1 = (1 - m_widthLight2Sprite) * m_duration; 72 } 73 74 protected override void OnUpdate() 75 { 76 base.OnUpdate(); 77 78 if (RendererMat != null) 79 { 80 if (!m_initedMat) 81 { 82 m_initedMat = true; 83 84 // init 85 RendererMat.SetFloat("_LightWidthToMain", m_widthLight2Sprite); 86 RendererMat.SetVector("_LightURange", m_lightURange); 87 } 88 89 // 驱动流光动画 90 RendererMat.SetFloat("_TimeRate", m_timer / m_duration); 91 92 m_timer += Time.deltaTime; 93 94 if (m_timer > m_duration) 95 m_timer = 0; 96 else if (m_timer > m_t1) 97 RendererMat.SetFloat("_ReachBoundary", 1); 98 else 99 RendererMat.SetFloat("_ReachBoundary", -1); 100 } 101 } 102 103 Material RendererMat 104 { 105 get { return base.Renderer != null ? base.Renderer.sharedMaterial : null; } 106 } 107 108 public float Duration 109 { 110 set 111 { 112 if (value <= 0) 113 throw new ArgumentException("value <= 0"); 114 115 m_duration = value; 116 m_t1 = (1 - m_widthLight2Sprite) * m_duration; 117 } 118 } 119 }
对应的 shader 如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 Shader "Bleach/Walk Light Colored" 2 { 3 Properties 4 { 5 _MainTex ("RGB", 2D) = "black" {} 6 _AlphaTex ("Alpha", 2D) = "black" {} 7 } 8 9 SubShader 10 { 11 LOD 200 12 13 Tags 14 { 15 "Queue" = "Transparent" 16 "IgnoreProjector" = "True" 17 "RenderType" = "Transparent" 18 } 19 20 Pass 21 { 22 Cull Off 23 Lighting Off 24 ZWrite Off 25 Fog { Mode Off } 26 Offset -1, -1 27 Blend SrcAlpha OneMinusSrcAlpha 28 29 CGPROGRAM 30 #pragma vertex vert 31 #pragma fragment frag 32 33 sampler2D _MainTex; 34 sampler2D _AlphaTex; 35 36 uniform fixed2 _LightURange; // 光图片在 u 上的范围 37 uniform fixed _LightWidthToMain; // 光图片宽度占主图片宽度的比例 38 uniform fixed _ReachBoundary; // 小于0表示未到边界,大于0表示到达边界 39 uniform fixed _TimeRate; // 时间/周期 40 41 struct appdata_t 42 { 43 float4 vertex : POSITION; 44 float2 texcoord : TEXCOORD0; 45 fixed4 color : COLOR; 46 }; 47 48 struct v2f 49 { 50 float4 vertex : SV_POSITION; 51 half2 texcoord : TEXCOORD0; 52 half2 uv2 : TEXCOORD1; // 流光图的 uv 坐标 53 }; 54 55 v2f vert (appdata_t v) 56 { 57 v2f o; 58 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex); 59 o.texcoord = v.texcoord; 60 fixed fixedU=(v.color.x - _LightURange.x) / (_LightURange.y - _LightURange.x); // 把 u 坐标转到[0,1]范围 61 o.uv2 = fixed2(fixedU,v.color.y); 62 return o; 63 } 64 65 fixed4 frag (v2f IN) : COLOR 66 { 67 fixed4 main; 68 main.rgb = tex2D(_MainTex, IN.texcoord).rgb; 69 main.a = tex2D(_AlphaTex, IN.texcoord).a; 70 71 // 流光 72 fixed x = _ReachBoundary > 0 && IN.uv2.x < _LightWidthToMain ? IN.uv2.x+1 : IN.uv2.x; 73 fixed lightU = (x - _TimeRate) / _LightWidthToMain; 74 lightU = (_LightURange.y - _LightURange.x) * lightU + _LightURange.x; // 把 u 坐标从[0,1]范围转回来 75 lightU = clamp(lightU, _LightURange.x, _LightURange.y); 76 fixed2 lightUV = fixed2(lightU, IN.uv2.y); 77 fixed lightA = tex2D(_AlphaTex, lightUV).a; 78 79 // 融合 80 fixed3 col = main.rgb * (1 + lightA * 1.5); 81 return fixed4(col, main.a); 82 } 83 ENDCG 84 } 85 } 86 87 SubShader 88 { 89 LOD 100 90 91 Tags 92 { 93 "Queue" = "Transparent" 94 "IgnoreProjector" = "True" 95 "RenderType" = "Transparent" 96 } 97 98 Pass 99 { 100 Cull Off 101 Lighting Off 102 ZWrite Off 103 Fog { Mode Off } 104 Offset -1, -1 105 ColorMask RGB 106 Blend SrcAlpha OneMinusSrcAlpha 107 ColorMaterial AmbientAndDiffuse 108 109 SetTexture [_MainTex] 110 { 111 Combine Texture * Primary 112 } 113 114 SetTexture [_AlphaTex] 115 { 116 Combine previous, texture * primary 117 } 118 } 119 } 120 }