Files
aibis-dream/Assets/Scripts/SceneManagement/Day2Sleep/KitePresentation.cs
T

382 lines
16 KiB
C#

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<SpriteRenderer>();
CopyRendererSettings(_kiteRenderer, 1, SpriteMaskInteraction.VisibleInsideMask);
_baseRenderer = baseObject.AddComponent<SpriteRenderer>();
CopyRendererSettings(_baseRenderer, 0, SpriteMaskInteraction.None);
_mask = maskObject.AddComponent<SpriteMask>();
_mask.alphaCutoff = 0.01f;
_mask.isCustomRangeActive = true;
_mask.enabled = false;
EnsureFocusMaterial();
_driver = cameraObject.AddComponent<KiteRigDriver>();
_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();
}
}
}