using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
namespace AibisDream.MiniGame.Language
{
///
/// CRT复古显示器效果控制器
/// 放在 LanguageParticleSystem 的最顶层 Image 上
///
[RequireComponent(typeof(Image))]
public class CRTOverlayController : MonoBehaviour
{
[Header("效果强度预设")]
[SerializeField] private CRTPreset currentPreset = CRTPreset.Retro;
[Header("自定义参数")]
[SerializeField, Range(0f, 1f)] private float scanlineIntensity = 0.3f;
[SerializeField, Range(50f, 800f)] private float scanlineCount = 300f;
[SerializeField, Range(0f, 5f)] private float scanlineSpeed = 0.5f;
[SerializeField, Range(0f, 1f)] private float noiseIntensity = 0.15f;
[SerializeField, Range(0f, 50f)] private float noiseSpeed = 15f;
[SerializeField, Range(1f, 100f)] private float noiseScale = 50f;
[SerializeField, Range(0f, 0.02f)] private float chromaticAberration = 0.003f;
[SerializeField, Range(0f, 1f)] private float vignetteIntensity = 0.3f;
[SerializeField, Range(0.01f, 1f)] private float vignetteSmoothness = 0.4f;
[SerializeField, Range(0f, 0.1f)] private float curvatureAmount = 0.02f;
[SerializeField, Range(0f, 0.1f)] private float flickerIntensity = 0.02f;
[SerializeField, Range(0f, 30f)] private float flickerSpeed = 8f;
[SerializeField, Range(0f, 1f)] private float glowIntensity = 0.1f;
[SerializeField] private Color glowColor = new Color(0.2f, 0.8f, 1f, 1f);
[Header("动态效果")]
[SerializeField] private bool enableDynamicNoise = true;
[SerializeField] private float noiseVariation = 0.05f;
[SerializeField] private float noiseVariationSpeed = 2f;
private Image overlayImage;
private Material material;
private Material instanceMaterial;
// Shader 属性 ID(缓存以提高性能)
private static readonly int ScanlineIntensityID = Shader.PropertyToID("_ScanlineIntensity");
private static readonly int ScanlineCountID = Shader.PropertyToID("_ScanlineCount");
private static readonly int ScanlineSpeedID = Shader.PropertyToID("_ScanlineSpeed");
private static readonly int NoiseIntensityID = Shader.PropertyToID("_NoiseIntensity");
private static readonly int NoiseSpeedID = Shader.PropertyToID("_NoiseSpeed");
private static readonly int NoiseScaleID = Shader.PropertyToID("_NoiseScale");
private static readonly int ChromaticAberrationID = Shader.PropertyToID("_ChromaticAberration");
private static readonly int VignetteIntensityID = Shader.PropertyToID("_VignetteIntensity");
private static readonly int VignetteSmoothnessID = Shader.PropertyToID("_VignetteSmoothness");
private static readonly int CurvatureAmountID = Shader.PropertyToID("_CurvatureAmount");
private static readonly int FlickerIntensityID = Shader.PropertyToID("_FlickerIntensity");
private static readonly int FlickerSpeedID = Shader.PropertyToID("_FlickerSpeed");
private static readonly int GlowIntensityID = Shader.PropertyToID("_GlowIntensity");
private static readonly int GlowColorID = Shader.PropertyToID("_GlowColor");
public enum CRTPreset
{
Custom, // 使用自定义参数
Subtle, // 轻微效果
Retro, // 复古效果(默认)
Damaged, // 受损/故障效果
Hologram // 全息投影效果
}
private void Awake()
{
overlayImage = GetComponent();
SetupMaterial();
}
private void Start()
{
ApplyPreset(currentPreset);
}
private void SetupMaterial()
{
// 查找 CRT Shader
Shader crtShader = Shader.Find("UI/CRTOverlayEffect");
if (crtShader == null)
{
Debug.LogError("CRTOverlayController: 找不到 UI/CRTOverlayEffect shader!");
return;
}
// 创建材质实例
instanceMaterial = new Material(crtShader);
instanceMaterial.name = "CRTOverlay_Instance";
overlayImage.material = instanceMaterial;
material = instanceMaterial;
}
private void Update()
{
if (material == null) return;
// 动态噪点变化
if (enableDynamicNoise)
{
float dynamicNoise = noiseIntensity +
Mathf.Sin(Time.time * noiseVariationSpeed) * noiseVariation;
material.SetFloat(NoiseIntensityID, dynamicNoise);
}
}
private void OnValidate()
{
if (Application.isPlaying && material != null)
{
if (currentPreset == CRTPreset.Custom)
{
ApplyCustomParameters();
}
else
{
ApplyPreset(currentPreset);
}
}
}
///
/// 应用预设效果
///
public void ApplyPreset(CRTPreset preset)
{
currentPreset = preset;
switch (preset)
{
case CRTPreset.Subtle:
SetSubtlePreset();
break;
case CRTPreset.Retro:
SetRetroPreset();
break;
case CRTPreset.Damaged:
SetDamagedPreset();
break;
case CRTPreset.Hologram:
SetHologramPreset();
break;
case CRTPreset.Custom:
ApplyCustomParameters();
break;
}
}
private void SetSubtlePreset()
{
scanlineIntensity = 0.15f;
scanlineCount = 200f;
scanlineSpeed = 0.3f;
noiseIntensity = 0.08f;
noiseSpeed = 10f;
noiseScale = 40f;
chromaticAberration = 0.001f;
vignetteIntensity = 0.2f;
vignetteSmoothness = 0.5f;
curvatureAmount = 0.01f;
flickerIntensity = 0.01f;
flickerSpeed = 5f;
glowIntensity = 0.05f;
glowColor = new Color(0.3f, 0.9f, 1f, 1f);
ApplyCustomParameters();
}
private void SetRetroPreset()
{
// 参考用户提供的图片 - 典型的复古CRT效果
scanlineIntensity = 0.35f;
scanlineCount = 300f;
scanlineSpeed = 0.5f;
noiseIntensity = 0.18f;
noiseSpeed = 15f;
noiseScale = 50f;
chromaticAberration = 0.004f;
vignetteIntensity = 0.35f;
vignetteSmoothness = 0.4f;
curvatureAmount = 0.025f;
flickerIntensity = 0.025f;
flickerSpeed = 8f;
glowIntensity = 0.12f;
glowColor = new Color(0.2f, 0.8f, 1f, 1f);
ApplyCustomParameters();
}
private void SetDamagedPreset()
{
scanlineIntensity = 0.5f;
scanlineCount = 250f;
scanlineSpeed = 1.5f;
noiseIntensity = 0.3f;
noiseSpeed = 25f;
noiseScale = 60f;
chromaticAberration = 0.01f;
vignetteIntensity = 0.5f;
vignetteSmoothness = 0.3f;
curvatureAmount = 0.04f;
flickerIntensity = 0.06f;
flickerSpeed = 15f;
glowIntensity = 0.08f;
glowColor = new Color(1f, 0.5f, 0.3f, 1f);
ApplyCustomParameters();
}
private void SetHologramPreset()
{
scanlineIntensity = 0.4f;
scanlineCount = 400f;
scanlineSpeed = 2f;
noiseIntensity = 0.12f;
noiseSpeed = 20f;
noiseScale = 30f;
chromaticAberration = 0.008f;
vignetteIntensity = 0.15f;
vignetteSmoothness = 0.6f;
curvatureAmount = 0f;
flickerIntensity = 0.04f;
flickerSpeed = 12f;
glowIntensity = 0.25f;
glowColor = new Color(0.3f, 1f, 0.8f, 1f);
ApplyCustomParameters();
}
private void ApplyCustomParameters()
{
if (material == null) return;
material.SetFloat(ScanlineIntensityID, scanlineIntensity);
material.SetFloat(ScanlineCountID, scanlineCount);
material.SetFloat(ScanlineSpeedID, scanlineSpeed);
material.SetFloat(NoiseIntensityID, noiseIntensity);
material.SetFloat(NoiseSpeedID, noiseSpeed);
material.SetFloat(NoiseScaleID, noiseScale);
material.SetFloat(ChromaticAberrationID, chromaticAberration);
material.SetFloat(VignetteIntensityID, vignetteIntensity);
material.SetFloat(VignetteSmoothnessID, vignetteSmoothness);
material.SetFloat(CurvatureAmountID, curvatureAmount);
material.SetFloat(FlickerIntensityID, flickerIntensity);
material.SetFloat(FlickerSpeedID, flickerSpeed);
material.SetFloat(GlowIntensityID, glowIntensity);
material.SetColor(GlowColorID, glowColor);
}
///
/// 设置效果强度(0-1,整体缩放所有效果)
///
public void SetEffectIntensity(float intensity)
{
intensity = Mathf.Clamp01(intensity);
if (material == null) return;
// 根据 intensity 缩放所有效果
material.SetFloat(ScanlineIntensityID, scanlineIntensity * intensity);
material.SetFloat(NoiseIntensityID, noiseIntensity * intensity);
material.SetFloat(ChromaticAberrationID, chromaticAberration * intensity);
material.SetFloat(VignetteIntensityID, vignetteIntensity * intensity);
material.SetFloat(CurvatureAmountID, curvatureAmount * intensity);
material.SetFloat(FlickerIntensityID, flickerIntensity * intensity);
material.SetFloat(GlowIntensityID, glowIntensity * intensity);
}
///
/// 动画过渡到指定强度
///
public Tween TweenEffectIntensity(float targetIntensity, float duration)
{
float currentIntensity = 1f;
return DOTween.To(
() => currentIntensity,
x => {
currentIntensity = x;
SetEffectIntensity(x);
},
targetIntensity,
duration
);
}
///
/// 触发故障效果(短暂的强烈干扰)
///
public void TriggerGlitch(float duration = 0.3f)
{
if (material == null) return;
// 保存当前值
float originalNoise = noiseIntensity;
float originalChroma = chromaticAberration;
float originalFlicker = flickerIntensity;
// 设置故障效果
material.SetFloat(NoiseIntensityID, 0.5f);
material.SetFloat(ChromaticAberrationID, 0.015f);
material.SetFloat(FlickerIntensityID, 0.1f);
// 恢复原始值
DOVirtual.DelayedCall(duration, () =>
{
if (material != null)
{
material.SetFloat(NoiseIntensityID, originalNoise);
material.SetFloat(ChromaticAberrationID, originalChroma);
material.SetFloat(FlickerIntensityID, originalFlicker);
}
});
}
///
/// 设置噪点强度
///
public void SetNoiseIntensity(float intensity)
{
noiseIntensity = Mathf.Clamp01(intensity);
if (material != null)
{
material.SetFloat(NoiseIntensityID, noiseIntensity);
}
}
///
/// 设置扫描线强度
///
public void SetScanlineIntensity(float intensity)
{
scanlineIntensity = Mathf.Clamp01(intensity);
if (material != null)
{
material.SetFloat(ScanlineIntensityID, scanlineIntensity);
}
}
///
/// 设置发光颜色
///
public void SetGlowColor(Color color)
{
glowColor = color;
if (material != null)
{
material.SetColor(GlowColorID, glowColor);
}
}
private void OnDestroy()
{
// 清理材质实例
if (instanceMaterial != null)
{
if (Application.isPlaying)
{
Destroy(instanceMaterial);
}
else
{
DestroyImmediate(instanceMaterial);
}
}
}
}
}