【Unity、Cocos】使用“角度渐变” 实现跑马灯(流水灯)效果

实现效果:


实现分析:这其实是一个四色角度渐变图像,加了一个角度值的关键帧动画,上图是一个边框贴图,下图是个实体贴图:


Unity实现:

思路:写一个四色角度渐变的shader,shader公开一个角度属性用于控制渐变角度,通过代码不停变换角度即可实现,下面给出详细的步骤,Unity版本:2021.3.23f1c1

1.新建一个shader文件,粘贴以下代码保存:

Shader "Custom/FourColorAngleGradient"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}
        _Color0("Color 0", Color) = (1, 0, 0, 1)
        _Color90("Color 90", Color) = (0, 1, 0, 1)
        _Color180("Color 180", Color) = (0, 0, 1, 1)
        _Color270("Color 270", Color) = (1, 1, 0, 1)
        _GradientAngle("Gradient Angle", Range(-180, 180)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" }
        LOD 100

        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        Cull Off

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

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _Color0;
            float4 _Color90;
            float4 _Color180;
            float4 _Color270;
            float _GradientAngle;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                // Sample the texture
                float4 texColor = tex2D(_MainTex, i.uv);
                if (texColor.a == 0)
                    discard;

                // Calculate angle
                float2 coord = i.uv - 0.5; // Offset to center
                coord.y *= -1; // Invert y for correct angle calculation
                float angle = atan2(coord.y, coord.x) * (180.0 / 3.14159265);
                angle += 180.0; // Offset to make angle positive
                angle -= _GradientAngle; // Apply gradient angle offset
                if (angle < 0) angle += 360.0;
                if (angle >= 360) angle -= 360.0;

                // Determine color based on angle
                float4 color;
                if (angle < 90)
                {
                    color = lerp(_Color0, _Color90, angle / 90);
                }
                else if (angle < 180)
                {
                    color = lerp(_Color90, _Color180, (angle - 90) / 90);
                }
                else if (angle < 270)
                {
                    color = lerp(_Color180, _Color270, (angle - 180) / 90);
                }
                else
                {
                    color = lerp(_Color270, _Color0, (angle - 270) / 90);
                }

                // Combine texture and gradient color
                return color * texColor;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

2.通过shader创建一个材质、并将材质主贴图设定为 “边框贴图”  

① 通过shader创建材质:

② 准备一张类似这样的边框png贴图:

 ③ 贴图设定 ”Alpha is Transparency“

④ 将材质的贴图设置为边框png贴图


 3.在UGUI中实现:

① 创建一个RawImage,将材质赋值给它

② 编写控制材质角度属性的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class NewBehaviourScript : MonoBehaviour
{
    private Material thisMat;//RawImage的Material
    const string angleProName = "_GradientAngle";//材质角度属性的名字
    [SerializeField] float speed = 7.2f;//每秒数值增量为:50 * speed,7.2为一圈每秒

    // Start is called before the first frame update
    void Start()
    {
        thisMat = GetComponent<RawImage>().material;
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        var nowValue = thisMat.GetFloat(angleProName);
        if ((nowValue += speed) > 180)
        {
            nowValue = -180;
        }

        thisMat.SetFloat(angleProName, nowValue);
    }
}

 ③ 代码挂载到RawImage上,运行场景即可


 4.通过修改四种颜色,可实现不同种类的流水线条

↓↓↓↓↓↓

 ↓↓↓↓↓↓

总结:重点就是实现 “角度渐变shader” ,关于shader实现思路,其实博主也不太懂~ shader是由chatgpt生成的,会用它就行了哈哈

项目资源:Unity跑马灯(流水灯)效果资源-CSDN文库icon-default.png?t=N7T8https://download.csdn.net/download/njiyue/88886999

 ———————————————————————————————

CocosCreator 3.8.1 实现:

1.新建一个着色器(Effect),粘贴以下代码保存:

// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
techniques:
  - passes:
      - vert: sprite-vs:vert
        frag: sprite-fs:frag
        depthStencilState:
          depthTest: false
          depthWrite: false
        blendState:
          targets:
            - blend: true
              blendSrc: src_alpha
              blendDst: one_minus_src_alpha
              blendDstAlpha: one_minus_src_alpha
        rasterizerState:
          cullMode: none
        properties:
          color0: { value: [1.0, 0.0, 0.0, 1.0], editor: { type: color } }
          color90: { value: [0.0, 1.0, 0.0, 1.0], editor: { type: color } }
          color180: { value: [0.0, 0.0, 1.0, 1.0], editor: { type: color } }
          color270: { value: [1.0, 1.0, 0.0, 1.0], editor: { type: color } }
          gradientAngle: { value: 0.0, editor: { type: number, range: [-180.0, 180.0] } }
          alphaThreshold: { value: 0.5 }

}%

CCProgram sprite-vs %{
precision highp float;
#include <builtin/uniforms/cc-global>
#if USE_LOCAL
  #include <builtin/uniforms/cc-local>
#endif
#if SAMPLE_FROM_RT
  #include <common/common-define>
#endif
in vec3 a_position;
in vec2 a_texCoord;
in vec4 a_color;

out vec4 color;
out vec2 uv0;

vec4 vert() {
  vec4 pos = vec4(a_position, 1);
  
  #if USE_LOCAL
    pos = cc_matWorld * pos;
  #endif
  
  #if USE_PIXEL_ALIGNMENT
    pos = cc_matView * pos;
    pos.xyz = floor(pos.xyz);
    pos = cc_matProj * pos;
  #else
    pos = cc_matViewProj * pos;
  #endif
  
  uv0 = a_texCoord;
  #if SAMPLE_FROM_RT
    CC_HANDLE_RT_SAMPLE_FLIP(uv0);
  #endif
  color = a_color;
  
  return pos;
}
}%

CCProgram sprite-fs %{
precision highp float;
#include <builtin/internal/embedded-alpha>
#include <builtin/internal/alpha-test>

in vec4 color;

#if USE_TEXTURE
  in vec2 uv0;
  #pragma builtin(local)
  layout(set = 2, binding = 12)uniform sampler2D cc_spriteTexture;
#endif

uniform pro {
  vec4 color0;
  vec4 color90;
  vec4 color180;
  vec4 color270;
  float gradientAngle;
};

vec4 frag() {
  vec4 texColor = vec4(1, 1, 1, 1);
   
  #if USE_TEXTURE
    texColor *= CCSampleWithAlphaSeparated(cc_spriteTexture, uv0);
    
    vec2 coord = uv0 - 0.5;
    coord.y *= -1.0;
    float angle = atan(coord.y, coord.x) * (180.0 / 3.14159265);
    angle += 180.0;
    angle -= gradientAngle;
    if (angle < 0.0) angle += 360.0;
    if (angle >= 360.0) angle -= 360.0;

    vec4 color;
    if (angle < 90.0) {
      color = mix(color0, color90, angle / 90.0);
    } else if (angle < 180.0) {
      color = mix(color90, color180, (angle - 90.0) / 90.0);
    } else if (angle < 270.0) {
      color = mix(color180, color270, (angle - 180.0) / 90.0);
    } else {
      color = mix(color270, color0, (angle - 270.0) / 90.0);
    }

    return color * texColor;
    
    // #if IS_GRAY
    //   float gray = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
    //   o.r = o.g = o.b = gray;
    // #endif
  #endif
  
  //o *= color;
  //ALPHA_TEST(texColor);
  //return texColor;
}
}%

2.创建一个材质,设定着色器、设置参数,保存

3.使用2D对象 sprite 来实现:

① 创建一个sprite(精灵)对象,将 材质 和 准备好的 透明底 边框 贴图 放至对应属性上。

② 注意这里有个坑,边框贴图的 Packable 要 取消打勾 ,否则会导致uv显示不正确。 ③ 写一个脚本挂载到 sprit对象上即可 (与unity同样的shader逻辑,但在cocos上它的流动是逆时针的,所以加了一个isForward属性控制转动方向),脚本内容:

import { _decorator, color, Component, Material, Node, Sprite } from 'cc';
const { ccclass, property } = _decorator;

@ccclass('NewComponent')
export class NewComponent extends Component {

    private mat: Material;
    private readonly angleProName = "gradientAngle";//材质角度属性的名字
    @property private speed = 360;///每秒一圈(360度)
    @property private isForward = true;//是否为正向(顺时针)


    start() {
        this.mat = this.getComponent(Sprite).material;
    }
    update(deltaTime: number) {
        let nowValue = this.mat.getProperty(this.angleProName) as number;
        if (this.isForward) {
            if ((nowValue -= this.speed * deltaTime) < -180) {
                nowValue = 180;
            }
        }
        else
            if ((nowValue += this.speed * deltaTime) > 180) {
                nowValue = -180;
            }
        this.mat.setProperty(this.angleProName, nowValue);
    }
}

4. 实现效果:

项目资源:CocosCreator3.8.1跑马灯(流水灯)效果资源-CSDN文库icon-default.png?t=N7T8https://download.csdn.net/download/njiyue/88899587