
Shader
Shader "IceGlacier/URP/SpriteRadial_Fixed"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_Progress ("Progress", Range(0.0, 1.0)) = 1.0
_Smoothness ("Anti-Aliasing", Range(0.0, 0.1)) = 0.01
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"RenderPipeline"="UniversalPipeline"
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
Name "SpriteRadialFixed"
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
// Macro is safer than static const for older compilers
#define INV_TWO_PI 0.1591549
struct Attributes
{
float4 positionOS : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD0;
};
CBUFFER_START(UnityPerMaterial)
float4 _Color;
float _Progress;
float _Smoothness;
CBUFFER_END
TEXTURE2D(_MainTex);
SAMPLER(sampler_MainTex);
Varyings vert(Attributes input)
{
Varyings output;
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
output.uv = input.uv;
output.color = input.color * _Color;
return output;
}
float4 frag(Varyings input) : SV_Target
{
float4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
col *= input.color;
// Explicit float2 construction to avoid compiler ambiguity
float2 uv = input.uv - float2(0.5, 0.5);
// Standard Atan2 (Clockwise start from 12 o'clock)
// atan2(x,y) returns angle from Y axis.
// We multiply by 1/(2PI) to map to -0.5 ~ 0.5
// frac() handles the negative wrap-around automatically, resulting in 0.0 ~ 1.0
float angle = frac(atan2(uv.x, uv.y) * INV_TWO_PI);
// Branchless Mask Logic
// If angle > Progress, alpha becomes 0
// smoothstep handles the AA edge
float mask = smoothstep(angle - _Smoothness, angle + _Smoothness, _Progress);
col.a *= mask;
return col;
}
ENDHLSL
}
}
}
控制脚本
using UnityEngine;
namespace GameScripts
{
/// <summary>
/// Sprite 径向进度条控制器
/// 特性:支持 SRP Batcher,无材质实例化开销,Editor 实时预览
/// </summary>
[ExecuteAlways]
public class TaskBar : MonoBehaviour
{
// Shader 属性 ID 缓存 (Static Readonly 避免每帧 Hash 计算)
private static readonly int ProgressID = Shader.PropertyToID( "_Progress" );
private static readonly int SmoothnessID = Shader.PropertyToID( "_Smoothness" );
private static readonly int ColorID = Shader.PropertyToID( "_Color" );
[Header( "Settings" )]
[SerializeField, Range( 0f, 1f )] private float progress = 1.0f;
[SerializeField, Range( 0f, 0.1f )] private float smoothness = 0.01f;
[SerializeField] private Color shaderTint = Color.white;
// 运行时缓存
[SerializeField] private SpriteRenderer _renderer;
private MaterialPropertyBlock _mpb;
private bool _isDirty = true;
private void OnEnable( )
{
Initialize( );
_isDirty = true; // 启用时强制刷新一次
}
private void Initialize( )
{
if ( _renderer == null ) _renderer = GetComponent<SpriteRenderer>( );
if ( _mpb == null ) _mpb = new MaterialPropertyBlock( );
}
#if UNITY_EDITOR
private void OnValidate( )
{
// 编辑器下属性面板修改时立即刷新
_isDirty = true;
// 在编辑器非运行模式下,Update 不会每帧跑,手动触发
if ( !Application.isPlaying )
{
LateUpdate( );
}
}
#endif
private void LateUpdate( )
{
if ( _isDirty )
{
UpdateVisuals( );
_isDirty = false;
}
}
/// <summary>
/// 提交数据到 GPU
/// </summary>
private void UpdateVisuals( )
{
if ( _renderer == null ) Initialize( );
#if UNITY_EDITOR
if ( _mpb == null ) return;
#endif
// 必须先 Get,以防覆盖了该 Renderer 上其他的 MPB 属性
_renderer.GetPropertyBlock( _mpb );
_mpb.SetFloat( ProgressID, progress );
_mpb.SetFloat( SmoothnessID, smoothness );
_mpb.SetColor( ColorID, shaderTint );
_renderer.SetPropertyBlock( _mpb );
}
// --- Public API ---
/// <summary>
/// 设置进度 (0 ~ 1)
/// </summary>
public void SetProgress( float value )
{
float clamped = Mathf.Clamp01( value );
if ( !Mathf.Approximately( progress, clamped ) )
{
progress = clamped;
_isDirty = true;
}
}
/// <summary>
/// 设置抗锯齿程度 (0 ~ 0.1)
/// </summary>
public void SetSmoothness( float value )
{
float clamped = Mathf.Clamp( value, 0f, 0.1f );
if ( !Mathf.Approximately( smoothness, clamped ) )
{
smoothness = clamped;
_isDirty = true;
}
}
/// <summary>
/// 设置 Shader 叠加色
/// </summary>
public void SetTint( Color color )
{
if ( shaderTint != color )
{
shaderTint = color;
_isDirty = true;
}
}
}
}
