Unity3D开发(一):NGUI Sprite效果变灰(按钮禁用状态)的解决方案

博主逛csdn有几年的时间了,但每每都是遇到问题时,来寻找解决方案的情况居多,并未开通过博客,也未发过任何文章,随着自己从网上得到的帮助越来越多,本着回馈与共同进步的想法,遂开通博客,并将自己在日常开发中遇到的问题记录下来,供广大网友们参考与讨论。

本人14年底因为项目需要随开始接触Unity3D引擎,博主一开始是用cocos2dx引擎开发手游,接触U3D后,发现确实U3D才是一套完整的手游开发解决方案,比起2dx上手也容易很多,加上NGUI,做游戏的UI确实比cocos2dx快了很多,因为博主身在一个创业公司,团队里面人手有限,因此现在项目的整个UI开发完全由博主一人承担,在此过程中确实遇到了很多问题,还好通过各种途径都解决了,我会在后期的博客中陆续将遇到的问题以及解决方案公布出来,供大家参考。

下面进入正题,因策划需求将无法点击的按钮以及一些可点击图标设置为禁用状态,即灰化,其实实现这个不算什么难事,最简单的就是让美术将所有需要灰化的图标出一份资源,然后程序动态换图就ok了。
然而这种解决方案最大的弊端就是,如果游戏内有大量需要灰化的图标,那么势必会导致最后包体变大,我们游戏正好就是此种情况,于是便选择了另一种解决方式,即通过程序写shader来解决此种问题。

在网上也查了一些文章,也有一些现成的shader例如这个shader
fixed4 frag (v2f i) : COLOR
{
fixed4 col;
if(i.color.r < 0.01 && i.color.g < 0.01 && i.color.b < 0.01 )
{
col = tex2D(_MainTex, i.texcoord);
float grey = dot(col.rgb, float3(0.299, 0.587, 0.114));
col.rgb = float3(grey, grey, grey);
}
else
{
col = tex2D(_MainTex, i.texcoord)* i.color;
}
return col;
}

这个shader就是在片段阶段时通过float grey = dot(col.rgb, float3(0.299, 0.587, 0.114)); 这句语句来实现灰化的,就是将顶点的像素与一个值进行点成,来影响每个顶点的像素来呈现出整体灰化的效果,这个shader最大的问题就在于外面的if判断,NGUI对每个Sprite的顶点进行渲染时,每次都要进行一次if else判断的话,会非常影响效率,尤其是如果你需要置灰的Sprite非常多的话,那么渲染就会非常耗时与影响效率,参考将Scrollview下放置超过100个Sprite再滑动,就可以大致了解了(这个问题在后期会给出解决方案)。
下面给出我的解决方案,首先先贴出我的shader:
Shader “UI/Sprite/Gray” {

Properties
{
    _MainTex ("Base (RGB), Alpha (A)", 2D) = "black" {}
}

SubShader
{
    LOD 200

    Tags
    {
        "Queue" = "Transparent"
        "IgnoreProjector" = "True"
        "RenderType" = "Transparent"
    }

    Pass
    {
        Cull Off
        Lighting Off
        ZWrite Off
        Fog { Mode Off }
        Offset -1, -1
        Blend SrcAlpha OneMinusSrcAlpha

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag           
        #include "UnityCG.cginc"

        sampler2D _MainTex;
        float4 _MainTex_ST;

        struct appdata_t
        {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
            fixed4 color : COLOR;
        };

        struct v2f
        {
            float4 vertex : SV_POSITION;
            half2 texcoord : TEXCOORD0;
            fixed4 color : COLOR;
        };

        v2f o;

        v2f vert (appdata_t v)
        {
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            o.texcoord = v.texcoord;
            o.color = v.color;

            return o;
        }

        fixed4 frag (v2f i) : COLOR
        {
            fixed4 col;   
            col = tex2D(_MainTex, i.texcoord);   
            col.rgb = dot(col.rgb, fixed3(.222,.707,.071));  

            return col;  
            }

        ENDCG
    }
}

shader就不详细介绍了,以后会专门开个专题来讲讲shader,这个shader里面核心代码只有一句 col.rgb = dot(col.rgb, fixed3(.222,.707,.071)); 其实跟网上的shader没什么区别,只是去掉了if else而已,这时有人可能会问了,你这样那我如何将sprite恢复成normal状态,别急,下面才是肉戏。我们知道NGUI的Sprite是通过图集来存储资源的,那么我如何来给单个Sprite换shader而又不影响图集里面其他的资源呢?就是通过动态给UISprite换材质的方法来实现,首先我们需要创建一个类,这里我创建了一个类叫XLSprite,它集成自UISprite,下面我将整个类贴上来:

using UnityEngine;
using System.Collections;
using System;

public class XLSprite : UISprite
{
protected GameObject panelObj = null;
protected Material GrayMaterial;

/// <summary>
/// ngui对Sprite进行渲染时候调用
/// </summary>
/// <value>The material.</value>
public override Material material  
{  
    get  
    {  
        Material mat = base.material;  

        if (mat == null)  
        {  
            mat = (mAtlas != null) ? mAtlas.spriteMaterial : null;  
        }  

        if (GrayMaterial !=null)  
        {  
            return GrayMaterial;  
        }  
        else  
        {  
            return mat;  
        }  
    }  
}

/// <summary>
/// 调用此方法可将Sprite变灰
/// </summary>
/// <value>The material.</value>
public void SetGray()  
{
    Material mat = new Material (Shader.Find("UI/Sprite/Gray"));
    mat.mainTexture = material.mainTexture;
    GrayMaterial = mat;

    RefreshPanel(gameObject); 
}

/// <summary>
/// 隐藏按钮,setActive能不用尽量少用,效率问题。
/// </summary>
/// <value>The material.</value>
public void SetVisible(bool isVisible)
{
    if (isVisible)
    {
        transform.localScale =  new Vector3(1,1,1);
    }
    else
    {
        transform.localScale = new Vector3(0,0,0);
    }

}

/// <summary>
/// 将按钮置为禁止点击状态,false为禁用状态
/// </summary>
/// <value>The material.</value>
public  void SetEnabled(bool isEnabled)
{
    if (isEnabled)
    {
        BoxCollider lisener = gameObject.GetComponent<BoxCollider> ();
        if (lisener)
        {
            lisener.enabled = true;
        }

        SetNormal();
    }
    else
    {
        BoxCollider lisener = gameObject.GetComponent<BoxCollider> ();
        if (lisener)
        {

            lisener.enabled = false;
        }

        SetGray();
    }
}

/// <summary>
/// 将GrayMaterial置为null,此时会调用默认材质,刷新panel才会重绘Sprite
/// </summary>
/// <value>The material.</value>
public void SetNormal()
{
    GrayMaterial = null;
    RefreshPanel(gameObject); 

}

///刷新panel,重绘Sprite
void RefreshPanel(GameObject go)
{
if (panelObj == null)
{
panelObj = NGUITools.FindInParents(go);
}

    if (panelObj != null)  
    {  
        panelObj.enabled = false;  
        panelObj.enabled = true;  
    }  
}

}

上面代码都有注释,就不多介绍了,注意material中的mat可能是私有变量,需要将其改为public。这样每次建立Sprite时候,直接添加XLSprite就ok,里面还有一些其他封装的方法,供大家参考,这里主要介绍下,如何实现的动态换材质,通过上面代码,大家可以大致看到是在SetGray()中,动态的创建了一个material并且赋值给Sprite,然后刷新panel时,在重绘这个Sprite时候,会调用material,这个时候,会返回我们创建好的GrayMaterial,在我们需要将Sprite恢复正常时候,仅需将我们的GrayMaterial置为null,就OK了。

下面是效果图
这里写图片描述

这里写图片描述
效果看来还是蛮ok的,今天就先介绍到这里,过段时间会介绍一下ScrollView下将按钮变灰的方法,上述方案只适用在非ScrollView下使用,在ScrollView下会出现变灰的Sprite无法正常遮罩的情况,那这种情况下具体的解决方案,会在下期的博客里讲解。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值