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

342 lines
14 KiB
C#

using System.Collections;
using AibisDream.Utility;
using DG.Tweening;
using UnityEngine;
namespace AibisDream
{
internal sealed class ReelMotionPresenter
{
private const string BaseSuffix = "_底图";
private const string MotionSuffix = "_风筝层";
private const float MotionSpeedMultiplier = 2.3f;
private const float MotionAmplitude = 0.22f;
private readonly SpriteRenderer _baseRenderer;
private readonly Vector3 _baseLocalPosition;
private readonly Quaternion _baseLocalRotation;
private readonly Vector3 _baseLocalScale;
private SpriteRenderer _motionRenderer;
private SpriteMask _motionMask;
private Sequence _motionSequence;
private Vector3 _motionBaseOffset;
public bool IsShowing => _baseRenderer != null && _baseRenderer.gameObject.activeSelf;
public string CurrentPicName { get; private set; }
public string CurrentMotionProfile { get; private set; }
public ReelMotionPresenter(SpriteRenderer baseRenderer)
{
_baseRenderer = baseRenderer;
if (_baseRenderer == null) return;
var transform = _baseRenderer.transform;
_baseLocalPosition = transform.localPosition;
_baseLocalRotation = transform.localRotation;
_baseLocalScale = transform.localScale;
}
public IEnumerator Show(string picName, string motionProfile)
{
ClearImmediate();
if (_baseRenderer == null) yield break;
var normalizedName = Day2SleepSpriteLoader.NormalizeName(picName);
if (!IsReelPic(normalizedName))
{
Debug.LogWarning($"[ReelMotionPresenter] Unsupported reel sprite: {picName}");
yield break;
}
_motionBaseOffset = new Vector3(0f, -0.32f, 0f);
_baseRenderer.gameObject.SetActive(true);
var baseLoaded = false;
yield return Day2SleepSpriteLoader.LoadInto(
_baseRenderer,
normalizedName + BaseSuffix,
value => baseLoaded = value);
if (!baseLoaded)
{
ClearImmediate();
yield break;
}
EnsureMotionRenderer();
_motionRenderer.gameObject.SetActive(true);
CopyRendererSettings(_motionRenderer, 1, SpriteMaskInteraction.VisibleInsideMask);
ConfigureMotionMask();
ResetTransforms();
var motionLoaded = false;
yield return Day2SleepSpriteLoader.LoadInto(
_motionRenderer,
normalizedName + MotionSuffix,
value => motionLoaded = value);
if (!motionLoaded)
{
ClearImmediate();
yield break;
}
_baseRenderer.SetAlpha(1f);
_motionRenderer.SetAlpha(1f);
CurrentPicName = normalizedName;
PlayMotion(motionProfile);
}
public void PlayCurrentMotion(string motionProfile)
{
if (!IsShowing) return;
PlayMotion(motionProfile);
}
public void ClearImmediate()
{
KillTweenState();
ResetTransforms();
CurrentPicName = null;
CurrentMotionProfile = null;
if (_baseRenderer != null)
{
_baseRenderer.DOKill();
_baseRenderer.SetAlpha(0f);
_baseRenderer.gameObject.SetActive(false);
}
if (_motionRenderer != null)
{
_motionRenderer.DOKill();
_motionRenderer.SetAlpha(0f);
_motionRenderer.gameObject.SetActive(false);
_motionRenderer.maskInteraction = SpriteMaskInteraction.None;
}
if (_motionMask != null)
_motionMask.enabled = false;
}
private void EnsureMotionRenderer()
{
if (_motionRenderer != null || _baseRenderer == null) return;
var motionObject = new GameObject($"{_baseRenderer.name} Motion");
motionObject.transform.SetParent(_baseRenderer.transform.parent);
_motionRenderer = motionObject.AddComponent<SpriteRenderer>();
CopyRendererSettings(_motionRenderer, 1, SpriteMaskInteraction.VisibleInsideMask);
_motionRenderer.gameObject.SetActive(false);
_motionRenderer.SetAlpha(0f);
}
private void EnsureMotionMask()
{
if (_motionMask != null || _baseRenderer == null) return;
var maskObject = new GameObject($"{_baseRenderer.name} Motion Mask");
maskObject.transform.SetParent(_baseRenderer.transform.parent);
_motionMask = maskObject.AddComponent<SpriteMask>();
_motionMask.alphaCutoff = 0.01f;
_motionMask.isCustomRangeActive = true;
_motionMask.enabled = false;
}
private void CopyRendererSettings(
SpriteRenderer target,
int sortingOrderOffset,
SpriteMaskInteraction maskInteraction)
{
if (target == null || _baseRenderer == null) return;
target.sharedMaterial = _baseRenderer.sharedMaterial;
target.sortingLayerID = _baseRenderer.sortingLayerID;
target.sortingOrder = _baseRenderer.sortingOrder + sortingOrderOffset;
target.drawMode = _baseRenderer.drawMode;
target.size = _baseRenderer.size;
target.flipX = _baseRenderer.flipX;
target.flipY = _baseRenderer.flipY;
target.maskInteraction = maskInteraction;
}
private void ConfigureMotionMask()
{
EnsureMotionMask();
if (_motionMask == null) return;
_motionMask.sprite = _baseRenderer.sprite;
_motionMask.frontSortingLayerID = _baseRenderer.sortingLayerID;
_motionMask.backSortingLayerID = _baseRenderer.sortingLayerID;
_motionMask.frontSortingOrder = _baseRenderer.sortingOrder + 2;
_motionMask.backSortingOrder = _baseRenderer.sortingOrder;
var transform = _motionMask.transform;
transform.localPosition = _baseLocalPosition;
transform.localRotation = _baseLocalRotation;
transform.localScale = _baseLocalScale;
_motionMask.enabled = true;
}
private void ResetTransforms()
{
if (_baseRenderer == null) return;
var baseTransform = _baseRenderer.transform;
baseTransform.localPosition = _baseLocalPosition;
baseTransform.localRotation = _baseLocalRotation;
baseTransform.localScale = _baseLocalScale;
if (_motionRenderer == null) return;
var motionTransform = _motionRenderer.transform;
motionTransform.localPosition = _baseLocalPosition + _motionBaseOffset;
motionTransform.localRotation = _baseLocalRotation;
motionTransform.localScale = _baseLocalScale;
}
private static bool IsReelPic(string picName)
{
return picName == "D2S轱辘" || picName == "D2S轱辘没线";
}
private static string NormalizeProfile(string motionProfile)
{
return string.IsNullOrEmpty(motionProfile)
? string.Empty
: motionProfile.Trim().ToLowerInvariant();
}
private Vector3 BaseOffset(float x, float y)
{
return _baseLocalPosition + _motionBaseOffset + new Vector3(x, y, 0f);
}
private void PlayMotion(string motionProfile)
{
KillTweenState();
var profile = NormalizeProfile(motionProfile);
CurrentMotionProfile = profile;
if (string.IsNullOrEmpty(profile) || profile == "none") return;
if (profile != "reel_tension")
{
Debug.LogWarning($"[ReelMotionPresenter] Unknown motion profile: {motionProfile}");
return;
}
var target = _motionRenderer != null && _motionRenderer.gameObject.activeSelf
? _motionRenderer.transform
: _baseRenderer.transform;
var sequence = DOTween.Sequence();
sequence.Append(target.DOLocalMove(
BaseOffset(0.13f * MotionAmplitude, -0.32f * MotionAmplitude), 0.38f)
.SetEase(Ease.OutCubic));
sequence.Join(target.DOLocalRotate(
new Vector3(0f, 0f, -2.8f * MotionAmplitude), 0.38f)
.SetEase(Ease.OutCubic));
sequence.OnComplete(() => PlayReelHold(target));
sequence.timeScale = MotionSpeedMultiplier;
_motionSequence = sequence;
}
private void PlayReelHold(Transform transform, float strain = 0f)
{
_motionSequence?.Kill();
var amp = MotionAmplitude;
var deepX = strain * 0.03f;
var deepY = strain * 0.07f;
var speed = Mathf.Lerp(1f, 0.78f, strain);
var rotBias = strain * 0.35f;
var sequence = DOTween.Sequence();
var strainDuration = Random.Range(0.55f, 0.9f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.17f, 0.23f) + deepX) * amp,
(Random.Range(-0.42f, -0.34f) - deepY) * amp), strainDuration)
.SetEase(Ease.InOutSine));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-3.6f, -2.4f) - rotBias) * amp), strainDuration)
.SetEase(Ease.InOutSine));
if (Random.value < 0.4f + strain * 0.25f)
{
var yankDuration = Random.Range(0.18f, 0.28f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.25f, 0.34f) + deepX) * amp,
(Random.Range(-0.62f, -0.5f) - deepY) * amp), yankDuration)
.SetEase(Ease.OutCubic));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-4.8f, -3.4f) - rotBias) * amp), yankDuration)
.SetEase(Ease.OutCubic));
sequence.AppendInterval(Random.Range(0.1f, 0.18f) * speed);
var biteDuration = Random.Range(0.2f, 0.32f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.19f, 0.27f) + deepX) * amp,
(Random.Range(-0.48f, -0.4f) - deepY) * amp), biteDuration)
.SetEase(Ease.OutCubic));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-3.4f, -2.2f) - rotBias) * amp), biteDuration)
.SetEase(Ease.OutCubic));
}
var recoverDuration = Random.Range(0.28f, 0.42f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.12f, 0.16f) + deepX) * amp,
(Random.Range(-0.32f, -0.26f) - deepY * 0.55f) * amp), recoverDuration)
.SetEase(Ease.OutCubic));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-2.3f, -1.3f) - rotBias * 0.7f) * amp), recoverDuration)
.SetEase(Ease.OutCubic));
var slipDuration = Random.Range(0.16f, 0.26f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.2f, 0.27f) + deepX) * amp,
(Random.Range(-0.5f, -0.4f) - deepY) * amp), slipDuration)
.SetEase(Ease.OutQuad));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-4.1f, -2.8f) - rotBias) * amp), slipDuration)
.SetEase(Ease.OutQuad));
if (Random.value < 0.3f)
{
var dipDuration = Random.Range(0.14f, 0.22f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.21f, 0.28f) + deepX) * amp,
(Random.Range(-0.56f, -0.47f) - deepY) * amp), dipDuration)
.SetEase(Ease.OutQuad));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-4.4f, -3f) - rotBias) * amp), dipDuration)
.SetEase(Ease.OutQuad));
var braceDuration = Random.Range(0.18f, 0.28f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.16f, 0.22f) + deepX) * amp,
(Random.Range(-0.4f, -0.33f) - deepY) * amp), braceDuration)
.SetEase(Ease.OutCubic));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-3f, -1.8f) - rotBias) * amp), braceDuration)
.SetEase(Ease.OutCubic));
}
var pullDuration = Random.Range(0.4f, 0.58f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.13f, 0.18f) + deepX) * amp,
(Random.Range(-0.35f, -0.28f) - deepY * 0.75f) * amp), pullDuration)
.SetEase(Ease.InOutSine));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-2.8f, -1.6f) - rotBias) * amp), pullDuration)
.SetEase(Ease.InOutSine));
if (Random.value < 0.25f)
{
var sagDuration = Random.Range(0.2f, 0.32f) * speed;
sequence.Append(transform.DOLocalMove(
BaseOffset((Random.Range(0.18f, 0.24f) + deepX) * amp,
(Random.Range(-0.46f, -0.38f) - deepY) * amp), sagDuration)
.SetEase(Ease.InOutSine));
sequence.Join(transform.DOLocalRotate(
new Vector3(0f, 0f, (Random.Range(-3.5f, -2.2f) - rotBias) * amp), sagDuration)
.SetEase(Ease.InOutSine));
}
sequence.OnComplete(() => PlayReelHold(transform, Mathf.Min(1f, strain + 0.12f)));
sequence.timeScale = MotionSpeedMultiplier;
_motionSequence = sequence;
}
private void KillTweenState()
{
_motionSequence?.Kill();
_motionSequence = null;
}
}
}