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); } } } } }