Unity循环长箭头加强版

需求变更

我哩个天天,需求这东西就是你做完一个又回有新的一个,做了个普通的,就会让你做高级的。
这不之前做了循环长箭头,领导就说要分段显示,怎么个分段显示咧?能够显示两种颜色的箭头,一种是你还没有走完的路,一种是已经走完的。恩,需求很简单,容我思考下人生。

看来又要学习啦

对unity的shader一直没有深入研究,到目前为止都是停留在能用就行的阶段。复杂一点的shader基本也用可视化的编辑插件进行处理,手打的也就能写写固定功能的shader。可视化的插件很方便,所见即所得,缺点是需要微调的时候就麻烦了。固定着色器功能再多也有限,到头来写Shader还是离不开HLSL和CG语言直接写。当然在Unity中,unity为我们包装了ShaderLab的壳子为我们方便的实现需要的功能,同时不至于代码过于的冗余和晦涩。

同样的Unity提供了官方文档,内容嘛,老规矩全而不细,需要的朋友出门左转,可以翻阅一下:Unity ShaderLab 官方文档

先想想问题,别急着动手

当前的需求是分段显示两种不同颜色的箭头,用于区分哪一部分是已经走过的路线,哪一部分是为走过的路线。在这个需求中需要处理两个贴图,用来表现不同的状态。

方法一:
常规情况下可以直接制作两个箭头的组件,因为在第一版的基础下已经实现了从点A到点B的箭头。分段显示只要处理点A到中间点A'和A'到B两段就行。

灵魂画手A->B
灵魂画手A->A'->B

这样做表面上看起来没有任何问题,实际上也没有任何问题!就是有一点比较麻烦,首先,你需要处理两个箭头,其次,A'点在实际游戏中是移动的,因为人是不断移动的,你就需要不停的调整两个箭头的长度,也就是需要控制两个变量。这种虽然能有但是哪里又有点不对的感觉就好像...

对对对,特么就是这种感觉

方法二:
不就是显示两个材质嘛,直接shader处理,把中间点A'当做属性值传入到shader中,然后就行判断,未到A'点的显示材质一,A'点之后的显示材质二,这样就只需要维护一个箭头,同时移动的时候只需要改shader中传入的A'参数就可以了。恩,很完美,方便使用,一般用起来方便懒的方法,写起了都有些费劲。别问我怎么知道的~~

想好了就动手做

目测原来会的固定功能shader无法实现判断的功能,需要直接写顶点着色器和片段着色器,基础有限就有恶补。涉及到图形图像shader这方面的资料少之又少,中文的就更不多了。推荐大家两个不过的博客,两位大佬也都是出过书的,大家也可以看看:
浅墨的Unity3D
candycat

Shader的实现代码不算复杂,该有的东西也都有,注释也写的很清楚,算是个不错的入门作业,需要的同学可以拿走,记得顺手点个赞哦

Shader "Custom/双色Shader"
{
    //属性
    Properties
    {
        _MainTex("MainTex(RGBA)", 2D) = "white" {} //材质1
        _GrayTex("GrayTex(RGBA)",2D) = "white" {} //材质2
        _CurrentPos("CurrentPos",Range(0,1)) = 1 //当前位置
    }

        SubShader
        {
            LOD 100
            Cull Off Lighting Off ZWrite Off ZTest Always
            Blend SrcAlpha OneMinusSrcAlpha

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

            Pass{
            CGPROGRAM
            #pragma vertex vert  //定义顶点着色器处理单元
            #pragma fragment frag  //定义片段着色器处理单元

            #include "UnityCG.cginc"  
            //变量定义
            uniform sampler2D _MainTex;
            uniform sampler2D _GrayTex;
            uniform fixed  _CurrentPos;
            uniform fixed4 _MainTex_ST;

            //顶点输入结构
            struct vertexInput {
                fixed4 vertex : POSITION;
                fixed4 texcoord0 : TEXCOORD0;
            };

            //片段输入结构(顶点输出)
            struct fragmentInput {
                fixed4 position : SV_POSITION;
                fixed4 texcoord0 : TEXCOORD0;
                fixed2 uv:_MainTex_ST;
            };

            //顶点着色器处理单元
            fragmentInput vert(vertexInput i) {
                fragmentInput o;
                o.position = UnityObjectToClipPos(i.vertex); //转换物体空间到相机空间
                o.texcoord0 = i.texcoord0;
                o.uv = TRANSFORM_TEX(i.texcoord0, _MainTex); //应用材质的Tilling和offset属性
                return o;
            }

            //片段着色器处理单元
            fixed4 frag(fragmentInput i) : SV_Target{
                //判断
                if(i.texcoord0.y >= _CurrentPos)
                    return tex2D(_GrayTex, i.uv);
                else
                    return tex2D(_MainTex, i.uv);
            }
            ENDCG
        }
    }
}

为了优化性能,同时游戏里的需求也不高,所有精度都为fixed,具体项目中可以更具需要进行实际修改。

结语

该学的东西还是要学,总是无法回避的,谁让选择了程序员这条路,就是一条终生学习的路。路上的兄弟共勉之。加油。

推荐阅读更多精彩内容