using DG.Tweening; using UnityEngine; using AibisDream.Utility; namespace AibisDream { /// /// Drives the unified EyeUnifiedSprite path for Peipei eye targets. /// public class EyeTargetVisual : MonoBehaviour { private static readonly int ColorRevealId = Shader.PropertyToID("_ColorReveal"); private static readonly int GrayAmountId = Shader.PropertyToID("_GrayAmount"); private static readonly int BlurAmountId = Shader.PropertyToID("_BlurAmount"); private static readonly int BlurSizeId = Shader.PropertyToID("_BlurSize"); private static readonly int DistortAmountId = Shader.PropertyToID("_DistortAmount"); private static readonly int WrongColorAmountId = Shader.PropertyToID("_WrongColorAmount"); private static readonly int SpriteFadeId = Shader.PropertyToID("_SpriteFade"); private static readonly int AsciiAmountId = Shader.PropertyToID("_AsciiAmount"); private static readonly int DreamAmountId = Shader.PropertyToID("_DreamAmount"); private static readonly int MemoryAmountId = Shader.PropertyToID("_MemoryAmount"); private static readonly int ScanlineAmountId = Shader.PropertyToID("_ScanlineAmount"); private static readonly int GlitchAmountId = Shader.PropertyToID("_GlitchAmount"); private static readonly int HsvShiftId = Shader.PropertyToID("_HsvShift"); private static readonly int HsvSaturationId = Shader.PropertyToID("_HsvSaturation"); private static readonly int RoundWaveStrengthId = Shader.PropertyToID("_RoundWaveStrength"); private static readonly int RevealWidthId = Shader.PropertyToID("_RevealWidth"); private static readonly int RevealRotationId = Shader.PropertyToID("_RevealRotation"); private static readonly int RevealNoiseId = Shader.PropertyToID("_RevealNoise"); private static readonly int WrongColorId = Shader.PropertyToID("_WrongColor"); [SerializeField] private SpriteRenderer spriteRenderer; [SerializeField] private SpriteRenderer imageSpriteRenderer; [SerializeField] private bool driveImageLayer = true; private Material material; private float activeBlurSize = 0.008f; public bool IsReady => material != null && material.HasProperty(ColorRevealId); public bool CanDriveImageLayer => driveImageLayer && imageSpriteRenderer != null; public Material Material => material; private void Awake() { EnsureMaterial(); } public bool EnsureMaterial() { if (spriteRenderer == null) { spriteRenderer = GetComponent(); } if (spriteRenderer == null) { return false; } material = Application.isPlaying ? spriteRenderer.material : spriteRenderer.sharedMaterial; return IsReady; } public void Configure(SpriteRenderer mainRenderer, SpriteRenderer imageRenderer) { spriteRenderer = mainRenderer; imageSpriteRenderer = imageRenderer; EnsureMaterial(); } public void ApplyTuning(EyeVisualSettings settings) { if (settings == null) { return; } activeBlurSize = settings.blurSize; if (!EnsureMaterial()) { return; } material.SetFloat(RevealWidthId, settings.revealWidth); material.SetFloat(RevealRotationId, settings.revealRotation); material.SetFloat(RevealNoiseId, settings.revealNoise); material.SetColor(WrongColorId, settings.wrongColor); SetFloatRaw(HsvSaturationId, settings.hsvSaturation); SyncBlurSizeToMaterial(); } public void SyncBlurSizeToMaterial() { if (!EnsureMaterial() || !material.HasProperty(BlurSizeId)) { return; } float blur = material.GetFloat(BlurAmountId); material.SetFloat(BlurSizeId, blur > 0.01f ? activeBlurSize : 0f); } public void KillActiveTweens() { if (material != null) { material.DOKill(); } if (driveImageLayer && imageSpriteRenderer != null) { imageSpriteRenderer.DOKill(); } ClearTransientEffects(null); } public void ClearTransientEffects(EyeVisualSettings settings) { if (!EnsureMaterial()) { return; } SetFloat(GlitchAmountId, 0f); SetFloat(DistortAmountId, 0f); SetFloat(ScanlineAmountId, 0f); SetFloatRaw(HsvShiftId, 0f); SetFloatRaw(RevealNoiseId, settings != null ? settings.revealNoise : 0f); } public void ResetVisual(float reveal) { if (!EnsureMaterial()) { return; } SetFloat(ColorRevealId, reveal); SetFloat(GrayAmountId, 1f - reveal); SetFloat(BlurAmountId, 1f); if (material.HasProperty(BlurSizeId)) { material.SetFloat(BlurSizeId, activeBlurSize); } SetFloat(DistortAmountId, 0f); SetFloat(WrongColorAmountId, 0f); SetFloat(SpriteFadeId, 1f); SetFloat(AsciiAmountId, 0f); SetFloat(DreamAmountId, 0f); SetFloat(MemoryAmountId, 0f); SetFloat(ScanlineAmountId, 0f); SetFloat(GlitchAmountId, 0f); SetFloat(HsvShiftId, 0f); SetFloat(HsvSaturationId, 1f); SetFloat(RoundWaveStrengthId, 0f); SetImageAlpha(0f); } public void SetColorReveal(float value) { SetFloat(ColorRevealId, value); SetFloat(GrayAmountId, 1f - value); } public float GetColorReveal() { if (!EnsureMaterial() || !material.HasProperty(ColorRevealId)) { return 0f; } return material.GetFloat(ColorRevealId); } public bool IsAtColorReveal(float target, float epsilon = 0.02f) { return Mathf.Abs(GetColorReveal() - target) <= epsilon; } public Tween TweenColorReveal(float value, float duration) { if (!EnsureMaterial()) { return null; } value = Mathf.Clamp01(value); Sequence sequence = DOTween.Sequence(); sequence.Join(material.DOFloat(value, ColorRevealId, duration)); sequence.Join(material.DOFloat(1f - value, GrayAmountId, duration)); return sequence; } public void SetBlur(float value) { SetFloat(BlurAmountId, value); if (EnsureMaterial() && material.HasProperty(BlurSizeId)) { material.SetFloat(BlurSizeId, value > 0.01f ? activeBlurSize : 0f); } } public Tween TweenBlur(float value, float duration) { if (!EnsureMaterial()) { return null; } Tween tween = material.DOFloat(value, BlurAmountId, duration); tween.OnUpdate(SyncBlurSizeToMaterial); tween.OnComplete(SyncBlurSizeToMaterial); return tween; } /// /// One focus transition: sharpen, brief defocus, settle. Total wall time equals . /// public Tween TweenBlurFocusSettle(EyeVisualSettings settings, float duration) { if (!EnsureMaterial() || settings == null || duration <= 0f) { return null; } float sharpBlur = settings.focusedBlurAmount; float bumpBlur = Mathf.Clamp( sharpBlur + settings.focusSettleDefocusBump, sharpBlur, settings.idleBlurAmount); Vector3 ratios = settings.focusSettlePhaseRatios; float ratioSum = ratios.x + ratios.y + ratios.z; if (ratioSum <= 0.001f) { ratios = new Vector3(0.5f, 0.22f, 0.28f); ratioSum = ratios.x + ratios.y + ratios.z; } float primaryDuration = duration * (ratios.x / ratioSum); float defocusDuration = duration * (ratios.y / ratioSum); float refocusDuration = duration * (ratios.z / ratioSum); Sequence sequence = DOTween.Sequence(); Tween primaryFocus = TweenBlur(sharpBlur, primaryDuration); if (primaryFocus != null) { primaryFocus.SetEase(Ease.InOutSine); sequence.Append(primaryFocus); } Tween microDefocus = TweenBlur(bumpBlur, defocusDuration); if (microDefocus != null) { microDefocus.SetEase(Ease.InOutSine); sequence.Append(microDefocus); } Tween refocus = TweenBlur(sharpBlur, refocusDuration); if (refocus != null) { refocus.SetEase(Ease.InOutSine); sequence.Append(refocus); } return sequence; } public void SetWrongColor(float value) { SetFloat(WrongColorAmountId, value); } public Tween TweenWrongColor(float value, float duration) { return TweenFloat(WrongColorAmountId, value, duration); } public Tween PlayColorSweepTransition(EyeVisualSettings settings, float duration, bool colorIn) { if (!EnsureMaterial() || settings == null || duration <= 0f) { return null; } duration = Mathf.Max(0.01f, duration); float chromaPeak = settings.colorSweepChromaPeak; float ramp = Mathf.Clamp(settings.colorSweepChromaRamp, 0.1f, 0.6f); float rampDuration = duration * ramp; float settleDuration = duration - rampDuration; float targetReveal = colorIn ? settings.colorRevealMax : settings.colorRevealMin; float targetBlur = colorIn ? settings.focusedBlurAmount : settings.idleBlurAmount; Ease sweepEase = colorIn ? Ease.InOutSine : Ease.InSine; ClearTransientEffects(null); SetFloatRaw(RevealNoiseId, 0f); Sequence sequence = DOTween.Sequence(); Tween colorTween = TweenColorReveal(targetReveal, duration); if (colorTween != null) { colorTween.SetEase(sweepEase); sequence.Join(colorTween); } Tween blurTween = TweenBlur(targetBlur, duration); if (blurTween != null) { blurTween.SetEase(sweepEase); sequence.Join(blurTween); } if (chromaPeak > 0.001f) { Sequence chromaEnvelope = DOTween.Sequence(); chromaEnvelope.Append(material.DOFloat(chromaPeak, GlitchAmountId, rampDuration) .SetEase(Ease.OutSine)); chromaEnvelope.Append(material.DOFloat(0f, GlitchAmountId, settleDuration) .SetEase(Ease.InSine)); sequence.Join(chromaEnvelope); } sequence.OnComplete(() => ClearTransientEffects(settings)); return sequence; } public void SetImageAlpha(float alpha) { if (!driveImageLayer || imageSpriteRenderer == null) { return; } alpha = Mathf.Clamp01(alpha); if (alpha > 0f) { imageSpriteRenderer.gameObject.SetActive(true); imageSpriteRenderer.enabled = true; } imageSpriteRenderer.SetAlpha(alpha); if (alpha <= 0f) { imageSpriteRenderer.gameObject.SetActive(false); } } public Tween TweenImageAlpha(float alpha, float duration) { if (!driveImageLayer || imageSpriteRenderer == null) { return null; } alpha = Mathf.Clamp01(alpha); if (alpha > 0f) { imageSpriteRenderer.gameObject.SetActive(true); imageSpriteRenderer.enabled = true; } return imageSpriteRenderer.DOFade(alpha, duration) .OnComplete(() => { if (alpha <= 0f && imageSpriteRenderer != null) { imageSpriteRenderer.gameObject.SetActive(false); } }); } public void SetUfWeights(float ascii, float dream, float memory) { SetFloat(AsciiAmountId, ascii); SetFloat(DreamAmountId, dream); SetFloat(MemoryAmountId, memory); SetFloat(ScanlineAmountId, Mathf.Max(ascii, memory)); } public Tween TweenUfWeights(float ascii, float dream, float memory, float duration) { if (!EnsureMaterial()) { return null; } Sequence sequence = DOTween.Sequence(); sequence.Join(material.DOFloat(Mathf.Clamp01(ascii), AsciiAmountId, duration)); sequence.Join(material.DOFloat(Mathf.Clamp01(dream), DreamAmountId, duration)); sequence.Join(material.DOFloat(Mathf.Clamp01(memory), MemoryAmountId, duration)); sequence.Join(material.DOFloat(Mathf.Clamp01(Mathf.Max(ascii, memory)), ScanlineAmountId, duration)); return sequence; } public void SetSaturation(float saturation) { SetFloatRaw(HsvSaturationId, Mathf.Clamp(saturation, 0f, 2f)); } public Tween TweenSaturation(float saturation, float duration) { return TweenFloat(HsvSaturationId, Mathf.Clamp(saturation, 0f, 2f), duration); } public Sequence StartGlitch(float duration = 1f) { if (!EnsureMaterial()) { return null; } duration = Mathf.Max(0.01f, duration); Sequence sequence = DOTween.Sequence(); sequence.Join(material.DOFloat(1f, GlitchAmountId, duration)); sequence.Join(material.DOFloat(0.02f, DistortAmountId, duration)); sequence.Join(material.DOFloat(360f, HsvShiftId, duration).SetLoops(-1, LoopType.Yoyo)); return sequence; } public void StopGlitch(float duration) { TweenFloat(GlitchAmountId, 0f, duration); TweenFloat(DistortAmountId, 0f, duration); TweenFloat(HsvShiftId, 0f, duration); } public Tween TweenRoundWave(float value, float duration) { return TweenFloat(RoundWaveStrengthId, value, duration); } private Tween TweenFloat(int propertyId, float value, float duration) { if (!EnsureMaterial() || !material.HasProperty(propertyId)) { return null; } return material.DOFloat(value, propertyId, duration); } private void SetFloat(int propertyId, float value) { if (!EnsureMaterial() || !material.HasProperty(propertyId)) { return; } material.SetFloat(propertyId, Mathf.Clamp01(value)); } private void SetFloatRaw(int propertyId, float value) { if (!EnsureMaterial() || !material.HasProperty(propertyId)) { return; } material.SetFloat(propertyId, value); } } }