using System.Collections; using AibisDream.Utility; using DG.Tweening; using UnityEngine; namespace AibisDream { internal sealed class KitePresentation { private const string KiteSpritePicName = "D2S风筝1_风筝层"; private const string KiteBasePicName = "D2S风筝1_底图"; private const float KiteMaxHeight = 13f; private readonly SpriteRenderer _referenceRenderer; private readonly Vector3 _baseLocalPosition; private readonly Quaternion _baseLocalRotation; private readonly Vector3 _baseLocalScale; private readonly KiteWindSettings _windSettings; private readonly KiteCameraSettings _cameraSettings; private readonly KiteFocusSettings _focusSettings; private Material _focusMaterial; private Transform _cameraNode; private Transform _rootPivot; private Transform _swayPivot; private SpriteRenderer _kiteRenderer; private SpriteRenderer _baseRenderer; private SpriteMask _mask; private KiteRigDriver _driver; private Sequence _accentSequence; private bool _kiteSpriteLoaded; private bool _baseSpriteLoaded; public bool IsShowing => _cameraNode != null && _cameraNode.gameObject.activeSelf; public float TargetHeight => _driver != null ? _driver.TargetHeight : 0f; public int CurrentWind => _driver != null ? _driver.CurrentWind : 0; public float Turbulence => _driver != null ? _driver.Turbulence : 0f; public KitePresentation( SpriteRenderer referenceRenderer, Material focusMaterial, KiteWindSettings windSettings, KiteCameraSettings cameraSettings, KiteFocusSettings focusSettings) { _referenceRenderer = referenceRenderer; _focusMaterial = focusMaterial; _windSettings = windSettings ?? new KiteWindSettings(); _cameraSettings = cameraSettings ?? new KiteCameraSettings(); _focusSettings = focusSettings ?? new KiteFocusSettings(); if (_referenceRenderer == null) return; var transform = _referenceRenderer.transform; _baseLocalPosition = transform.localPosition; _baseLocalRotation = transform.localRotation; _baseLocalScale = transform.localScale; } public IEnumerator Show(float height, int wind, float duration = 0.35f) { EnsureRig(); if (_kiteRenderer == null || _driver == null) yield break; height = Mathf.Clamp(height, 0f, KiteMaxHeight); if (IsShowing) { if (_driver.IsRunning) { SetState(height, wind, duration); } else { _kiteRenderer.transform.localPosition = Vector3.zero; _kiteRenderer.transform.localRotation = Quaternion.identity; _kiteRenderer.SetAlpha(KiteRigDriver.Alpha(height)); _driver.Wake(height, wind); } yield break; } KillTweens(); _cameraNode.gameObject.SetActive(true); _baseRenderer.gameObject.SetActive(true); if (!_kiteSpriteLoaded) { var loaded = false; yield return Day2SleepSpriteLoader.LoadInto( _kiteRenderer, KiteSpritePicName, value => loaded = value); _kiteSpriteLoaded = loaded; EnsureFocusMaterial(); if (_focusMaterial != null) _kiteRenderer.material = _focusMaterial; } if (!_baseSpriteLoaded) { var loaded = false; yield return Day2SleepSpriteLoader.LoadInto( _baseRenderer, KiteBasePicName, value => loaded = value); _baseSpriteLoaded = loaded; } if (!_kiteSpriteLoaded || !_baseSpriteLoaded) { ClearImmediate(); yield break; } _mask.sprite = _baseRenderer.sprite; _mask.frontSortingLayerID = _kiteRenderer.sortingLayerID; _mask.backSortingLayerID = _kiteRenderer.sortingLayerID; _mask.frontSortingOrder = _referenceRenderer.sortingOrder + 2; _mask.backSortingOrder = _referenceRenderer.sortingOrder; _mask.enabled = true; _driver.Wake(height, wind); _baseRenderer.SetAlpha(1f); _kiteRenderer.SetAlpha(KiteRigDriver.Alpha(height)); } public void SetState(float height, int wind, float duration = 0.9f) { if (!IsShowing || _driver == null) return; _driver.SetHeight(Mathf.Clamp(height, 0f, KiteMaxHeight), duration); _driver.SetWind(wind); } public void SetWind(int wind, float turbulence = -1f) { if (!IsShowing || _driver == null) return; var value = turbulence >= 0f ? Mathf.Clamp01(turbulence) : 0f; _driver.SetWind(wind, value); } public void PlayAccent(string profile) { if (!IsShowing || _kiteRenderer == null) return; _accentSequence?.Kill(); _accentSequence = null; var transform = _kiteRenderer.transform; var sequence = DOTween.Sequence(); switch (NormalizeProfile(profile)) { case "lift": sequence.Append(transform.DOLocalPath(new[] { new Vector3(0.03f, 0.07f, 0f), new Vector3(0.11f, 0.17f, 0f), new Vector3(0.12f, 0.24f, 0f), }, 0.75f, PathType.CatmullRom).SetEase(Ease.InOutSine)); sequence.Join(transform.DOLocalRotate(new Vector3(2f, -1.5f, -2f), 0.62f) .SetEase(Ease.InOutSine)); break; case "sink": sequence.Append(transform.DOLocalPath(new[] { new Vector3(0.03f, -0.06f, 0f), new Vector3(-0.02f, -0.17f, 0f), new Vector3(-0.08f, -0.24f, 0f), }, 0.82f, PathType.CatmullRom).SetEase(Ease.InOutSine)); sequence.Join(transform.DOLocalRotate(new Vector3(-2f, 1.5f, 2f), 0.68f) .SetEase(Ease.InOutSine)); break; case "taut": sequence.Append(transform.DOLocalPath(new[] { new Vector3(0.08f, 0.01f, 0f), new Vector3(0.2f, 0.07f, 0f), new Vector3(0.26f, 0.05f, 0f), }, 0.62f, PathType.CatmullRom).SetEase(Ease.OutSine)); sequence.Join(transform.DOLocalRotate(new Vector3(0f, -2f, -3f), 0.62f) .SetEase(Ease.OutSine)); sequence.AppendInterval(0.32f); break; case "drop": sequence.Append(transform.DOLocalPath(new[] { new Vector3(0.06f, -0.08f, 0f), new Vector3(0.08f, -0.24f, 0f), new Vector3(0.01f, -0.36f, 0f), }, 0.92f, PathType.CatmullRom).SetEase(Ease.InSine)); sequence.Join(transform.DOLocalRotate(new Vector3(-2.5f, 1f, 3f), 0.86f) .SetEase(Ease.InOutSine)); break; case "wrench": sequence.Append(transform.DOLocalPath(new[] { new Vector3(-0.06f, 0.03f, 0f), new Vector3(0.3f, 0.18f, 0f), new Vector3(0.64f, 0.12f, 0f), new Vector3(0.78f, 0.24f, 0f), }, 1.15f, PathType.CatmullRom).SetEase(Ease.InOutSine)); sequence.Join(transform.DOLocalRotate(new Vector3(1.5f, -3f, -6f), 0.92f) .SetEase(Ease.InOutSine)); sequence.AppendInterval(0.42f); break; case "flutter": var side = Random.value < 0.5f ? -1f : 1f; sequence.Append(transform.DOLocalPath(new[] { new Vector3(0.05f * side, 0.04f, 0f), new Vector3(0.16f * side, 0.11f, 0f), new Vector3(0.24f * side, 0.07f, 0f), }, 1.05f, PathType.CatmullRom).SetEase(Ease.InOutSine)); sequence.Join(transform.DOLocalRotate( new Vector3(1.5f, -2.5f * side, -3f * side), 0.9f) .SetEase(Ease.InOutSine)); sequence.AppendInterval(0.22f); break; default: sequence.Kill(); return; } sequence.Append(transform.DOLocalMove(Vector3.zero, 0.82f).SetEase(Ease.InOutSine)); sequence.Join(transform.DOLocalRotate(Vector3.zero, 0.82f).SetEase(Ease.InOutSine)); _accentSequence = sequence; } public IEnumerator FallOutOfFrame(float duration = 1.1f) { if (!IsShowing || _kiteRenderer == null || _driver == null) yield break; _accentSequence?.Kill(); _accentSequence = null; _driver.ResetAndSleep(); var transform = _kiteRenderer.transform; float frameHeight = _baseRenderer != null && _baseRenderer.sprite != null ? _baseRenderer.sprite.bounds.size.y : 6f; float fallDistance = Mathf.Max(frameHeight * 1.1f, 3f); Vector3 target = transform.localPosition + Vector3.down * fallDistance; float fallDuration = Mathf.Max(0.05f, duration); var sequence = DOTween.Sequence(); sequence.Append(transform.DOLocalMove(target, fallDuration) .SetEase(Ease.InQuad)); sequence.Join(transform.DOLocalRotate(new Vector3(-4f, 2f, 8f), fallDuration) .SetEase(Ease.InSine)); _accentSequence = sequence; yield return sequence.WaitForCompletion(); if (_accentSequence != sequence) yield break; _kiteRenderer.SetAlpha(0f); _accentSequence = null; } public IEnumerator Hide(float duration = 1f) { ClearImmediate(); yield break; } public void ClearImmediate() { KillTweens(); if (_kiteRenderer != null) { _kiteRenderer.SetAlpha(0f); _kiteRenderer.transform.localPosition = Vector3.zero; _kiteRenderer.transform.localRotation = Quaternion.identity; } if (_baseRenderer != null) { _baseRenderer.SetAlpha(0f); _baseRenderer.gameObject.SetActive(false); } if (_cameraNode != null) _cameraNode.gameObject.SetActive(false); if (_mask != null) _mask.enabled = false; } private void EnsureRig() { if (_rootPivot != null || _referenceRenderer == null) return; var parent = _referenceRenderer.transform.parent; var cameraObject = new GameObject("Kite Camera"); cameraObject.transform.SetParent(parent); cameraObject.transform.localPosition = _baseLocalPosition; cameraObject.transform.localRotation = Quaternion.identity; cameraObject.transform.localScale = Vector3.one; var rootObject = new GameObject("Kite Rig"); rootObject.transform.SetParent(cameraObject.transform, false); rootObject.transform.localRotation = _baseLocalRotation; rootObject.transform.localScale = _baseLocalScale; var swayObject = new GameObject("Kite Sway"); swayObject.transform.SetParent(rootObject.transform, false); var accentObject = new GameObject("Kite Accent"); accentObject.transform.SetParent(swayObject.transform, false); var baseObject = new GameObject("Kite Base"); baseObject.transform.SetParent(parent); baseObject.transform.localPosition = _baseLocalPosition; baseObject.transform.localRotation = _baseLocalRotation; baseObject.transform.localScale = _baseLocalScale; var maskObject = new GameObject("Kite Mask"); maskObject.transform.SetParent(parent); maskObject.transform.localPosition = _baseLocalPosition; maskObject.transform.localRotation = _baseLocalRotation; maskObject.transform.localScale = _baseLocalScale; _cameraNode = cameraObject.transform; _rootPivot = rootObject.transform; _swayPivot = swayObject.transform; _kiteRenderer = accentObject.AddComponent(); CopyRendererSettings(_kiteRenderer, 1, SpriteMaskInteraction.VisibleInsideMask); _baseRenderer = baseObject.AddComponent(); CopyRendererSettings(_baseRenderer, 0, SpriteMaskInteraction.None); _mask = maskObject.AddComponent(); _mask.alphaCutoff = 0.01f; _mask.isCustomRangeActive = true; _mask.enabled = false; EnsureFocusMaterial(); _driver = cameraObject.AddComponent(); _driver.Configure( _cameraNode, _rootPivot, _swayPivot, _kiteRenderer, _baseLocalPosition, _baseLocalRotation, _baseLocalScale, _windSettings, _cameraSettings, _focusSettings, _focusMaterial); _driver.enabled = false; cameraObject.SetActive(false); baseObject.SetActive(false); _kiteRenderer.SetAlpha(0f); _baseRenderer.SetAlpha(0f); } private void CopyRendererSettings( SpriteRenderer target, int sortingOrderOffset, SpriteMaskInteraction maskInteraction) { target.sharedMaterial = _referenceRenderer.sharedMaterial; target.sortingLayerID = _referenceRenderer.sortingLayerID; target.sortingOrder = _referenceRenderer.sortingOrder + sortingOrderOffset; target.drawMode = _referenceRenderer.drawMode; target.size = _referenceRenderer.size; target.flipX = _referenceRenderer.flipX; target.flipY = _referenceRenderer.flipY; target.maskInteraction = maskInteraction; } private void EnsureFocusMaterial() { if (_focusMaterial != null) return; var shader = Shader.Find("AIBIS/KiteFocusSprite"); if (shader == null) { Debug.LogWarning("[KitePresentation] 找不到 AIBIS/KiteFocusSprite,风筝虚焦不可用。"); return; } _focusMaterial = new Material(shader) { name = "KiteFocusSprite (Runtime)" }; } private void KillTweens() { _accentSequence?.Kill(); _accentSequence = null; _driver?.ResetAndSleep(); } private static string NormalizeProfile(string profile) { return string.IsNullOrEmpty(profile) ? string.Empty : profile.Trim().ToLowerInvariant(); } } }