358 lines
12 KiB
C#
358 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using DG.Tweening;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
public static class UI_OrderedEntryAnimator
|
|
{
|
|
public static Sequence PlaySingle(
|
|
GameObject target,
|
|
int orderIndex = 0,
|
|
float duration = 0.22f,
|
|
float stagger = 0.018f,
|
|
float fromYOffset = -18f,
|
|
float fromScale = 0.96f,
|
|
bool useUnscaledTime = true,
|
|
bool forceFadeOnly = false)
|
|
{
|
|
if (target == null || !target.activeInHierarchy) return null;
|
|
|
|
RectTransform rect = target.transform as RectTransform;
|
|
if (rect == null) rect = target.GetComponent<RectTransform>();
|
|
if (rect == null) return null;
|
|
|
|
DOTween.Kill(target);
|
|
|
|
CanvasGroup group = target.GetComponent<CanvasGroup>();
|
|
if (group == null) group = target.AddComponent<CanvasGroup>();
|
|
|
|
rect.DOKill();
|
|
group.DOKill();
|
|
|
|
float safeDuration = Mathf.Max(0.01f, duration);
|
|
float delay = Mathf.Max(0f, stagger) * Mathf.Max(0, orderIndex);
|
|
Vector2 basePos = rect.anchoredPosition;
|
|
Vector3 baseScale = rect.localScale;
|
|
float targetAlpha = group.alpha <= 0f ? 1f : group.alpha;
|
|
bool layoutControlled = forceFadeOnly || IsLayoutSensitive(rect);
|
|
|
|
if (!layoutControlled)
|
|
{
|
|
rect.anchoredPosition = basePos + new Vector2(0f, fromYOffset);
|
|
rect.localScale = baseScale * Mathf.Max(0.01f, fromScale);
|
|
}
|
|
group.alpha = 0f;
|
|
|
|
Sequence sequence = DOTween.Sequence().SetUpdate(useUnscaledTime).SetTarget(target);
|
|
sequence.SetDelay(delay);
|
|
if (!layoutControlled)
|
|
{
|
|
sequence.Join(rect.DOAnchorPos(basePos, safeDuration).SetEase(Ease.OutCubic).SetUpdate(useUnscaledTime));
|
|
sequence.Join(rect.DOScale(baseScale, safeDuration).SetEase(Ease.OutBack).SetUpdate(useUnscaledTime));
|
|
}
|
|
sequence.Join(group.DOFade(targetAlpha, safeDuration).SetEase(Ease.OutCubic).SetUpdate(useUnscaledTime));
|
|
sequence.OnKill(() => Restore(rect, group, basePos, baseScale, targetAlpha, !layoutControlled));
|
|
sequence.OnComplete(() => Restore(rect, group, basePos, baseScale, targetAlpha, !layoutControlled));
|
|
return sequence;
|
|
}
|
|
|
|
public static Sequence PlayRefresh(
|
|
GameObject target,
|
|
float duration = 0.18f,
|
|
float fromYOffset = -10f,
|
|
float fromScale = 0.985f,
|
|
bool useUnscaledTime = true)
|
|
{
|
|
return PlaySingle(target, 0, duration, 0f, fromYOffset, fromScale, useUnscaledTime);
|
|
}
|
|
|
|
public static Sequence PlayFadeOnly(
|
|
GameObject target,
|
|
int orderIndex = 0,
|
|
float duration = 0.18f,
|
|
float stagger = 0.012f,
|
|
bool useUnscaledTime = true)
|
|
{
|
|
return PlaySingle(target, orderIndex, duration, stagger, 0f, 1f, useUnscaledTime, true);
|
|
}
|
|
|
|
public static Sequence PlayRefresh(Component target, float duration = 0.18f, float fromYOffset = -10f, float fromScale = 0.985f, bool useUnscaledTime = true)
|
|
{
|
|
return target != null ? PlayRefresh(target.gameObject, duration, fromYOffset, fromScale, useUnscaledTime) : null;
|
|
}
|
|
|
|
public static Sequence Play(
|
|
Transform root,
|
|
float itemDuration,
|
|
float itemStagger,
|
|
Vector2 fromOffset,
|
|
bool useUnscaledTime,
|
|
Predicate<RectTransform> includePredicate = null)
|
|
{
|
|
if (root == null) return null;
|
|
|
|
List<RectTransform> targets = CollectTargets(root, includePredicate);
|
|
if (targets.Count == 0) return null;
|
|
|
|
targets.Sort(CompareByTopThenLeft);
|
|
|
|
float duration = Mathf.Max(0.01f, itemDuration);
|
|
float stagger = Mathf.Max(0f, itemStagger);
|
|
Sequence sequence = DOTween.Sequence().SetUpdate(useUnscaledTime);
|
|
List<AnimState> states = new List<AnimState>(targets.Count);
|
|
|
|
for (int i = 0; i < targets.Count; i++)
|
|
{
|
|
RectTransform rt = targets[i];
|
|
if (rt == null || !rt.gameObject.activeInHierarchy) continue;
|
|
|
|
CanvasGroup group = rt.GetComponent<CanvasGroup>();
|
|
if (group == null) group = rt.gameObject.AddComponent<CanvasGroup>();
|
|
|
|
rt.DOKill();
|
|
group.DOKill();
|
|
|
|
Vector2 basePos = rt.anchoredPosition;
|
|
Vector3 baseScale = rt.localScale;
|
|
float baseAlpha = group.alpha <= 0f ? 1f : group.alpha;
|
|
bool layoutControlled = IsLayoutSensitive(rt);
|
|
states.Add(new AnimState(rt, group, basePos, baseScale, baseAlpha, !layoutControlled));
|
|
|
|
if (!layoutControlled)
|
|
{
|
|
rt.anchoredPosition = basePos + fromOffset;
|
|
}
|
|
group.alpha = 0f;
|
|
|
|
float startAt = i * stagger;
|
|
if (!layoutControlled)
|
|
{
|
|
sequence.Insert(startAt, rt.DOAnchorPos(basePos, duration).SetEase(Ease.OutCubic).SetUpdate(useUnscaledTime));
|
|
}
|
|
sequence.Insert(startAt, group.DOFade(baseAlpha, duration).SetEase(Ease.OutCubic).SetUpdate(useUnscaledTime));
|
|
}
|
|
|
|
sequence.OnKill(() => RestoreAll(states));
|
|
sequence.OnComplete(() => RestoreAll(states));
|
|
return sequence;
|
|
}
|
|
|
|
private struct AnimState
|
|
{
|
|
public RectTransform Rect;
|
|
public CanvasGroup Group;
|
|
public Vector2 AnchoredPosition;
|
|
public Vector3 Scale;
|
|
public float Alpha;
|
|
public bool RestoreTransform;
|
|
|
|
public AnimState(RectTransform rect, CanvasGroup group, Vector2 anchoredPosition, Vector3 scale, float alpha, bool restoreTransform)
|
|
{
|
|
Rect = rect;
|
|
Group = group;
|
|
AnchoredPosition = anchoredPosition;
|
|
Scale = scale;
|
|
Alpha = alpha;
|
|
RestoreTransform = restoreTransform;
|
|
}
|
|
}
|
|
|
|
private static void RestoreAll(List<AnimState> states)
|
|
{
|
|
if (states == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < states.Count; i++)
|
|
{
|
|
AnimState state = states[i];
|
|
Restore(state.Rect, state.Group, state.AnchoredPosition, state.Scale, state.Alpha, state.RestoreTransform);
|
|
}
|
|
}
|
|
|
|
private static void Restore(RectTransform rect, CanvasGroup group, Vector2 anchoredPosition, Vector3 scale, float alpha, bool restoreTransform)
|
|
{
|
|
if (restoreTransform && rect != null)
|
|
{
|
|
rect.anchoredPosition = anchoredPosition;
|
|
rect.localScale = scale;
|
|
}
|
|
|
|
if (group != null)
|
|
{
|
|
group.alpha = alpha;
|
|
}
|
|
}
|
|
|
|
public static bool IsLayoutSensitive(RectTransform rect)
|
|
{
|
|
if (rect == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (rect.GetComponent<LayoutGroup>() != null || rect.GetComponent<ContentSizeFitter>() != null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Transform current = rect;
|
|
while (current != null && current.parent != null)
|
|
{
|
|
Transform parent = current.parent;
|
|
if (parent.GetComponent<LayoutGroup>() != null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
RectTransform currentRect = current as RectTransform;
|
|
if (currentRect != null && currentRect.GetComponent<LayoutElement>() != null && parent.GetComponent<ContentSizeFitter>() != null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
current = parent;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static Sequence PlayKeyDownFlash(
|
|
Transform keyDownRoot,
|
|
bool useUnscaledTime,
|
|
float fromYOffset = 36f,
|
|
float itemStagger = 0.03f)
|
|
{
|
|
if (keyDownRoot == null) return null;
|
|
|
|
List<RectTransform> targets = CollectKeyDownRects(keyDownRoot);
|
|
if (targets.Count == 0) return null;
|
|
|
|
targets.Sort(CompareByTopThenLeft);
|
|
Sequence sequence = DOTween.Sequence().SetUpdate(useUnscaledTime);
|
|
|
|
float moveDuration = 0.1f;
|
|
float stagger = Mathf.Max(0f, itemStagger);
|
|
float offsetY = Mathf.Abs(fromYOffset);
|
|
|
|
for (int i = 0; i < targets.Count; i++)
|
|
{
|
|
RectTransform rt = targets[i];
|
|
if (rt == null || !rt.gameObject.activeInHierarchy) continue;
|
|
|
|
CanvasGroup group = rt.GetComponent<CanvasGroup>();
|
|
if (group == null) group = rt.gameObject.AddComponent<CanvasGroup>();
|
|
|
|
rt.DOKill();
|
|
group.DOKill();
|
|
|
|
Vector2 basePos = rt.anchoredPosition;
|
|
rt.anchoredPosition = basePos - new Vector2(0f, offsetY);
|
|
group.alpha = 0f;
|
|
|
|
float startAt = i * stagger;
|
|
sequence.Insert(startAt, rt.DOAnchorPos(basePos, moveDuration).SetEase(Ease.OutCubic).SetUpdate(useUnscaledTime));
|
|
sequence.Insert(startAt, group.DOFade(1f, 0.028f).SetEase(Ease.Linear).SetUpdate(useUnscaledTime));
|
|
sequence.Insert(startAt + 0.028f, group.DOFade(0.2f, 0.026f).SetEase(Ease.Linear).SetUpdate(useUnscaledTime));
|
|
sequence.Insert(startAt + 0.054f, group.DOFade(1f, 0.04f).SetEase(Ease.OutCubic).SetUpdate(useUnscaledTime));
|
|
}
|
|
|
|
return sequence;
|
|
}
|
|
|
|
public static bool IsNamedOrChildOf(Transform target, string name)
|
|
{
|
|
if (target == null || string.IsNullOrEmpty(name)) return false;
|
|
Transform it = target;
|
|
while (it != null)
|
|
{
|
|
if (string.Equals(it.name, name, StringComparison.OrdinalIgnoreCase))
|
|
return true;
|
|
it = it.parent;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static List<RectTransform> CollectTargets(Transform root, Predicate<RectTransform> includePredicate)
|
|
{
|
|
List<RectTransform> list = new List<RectTransform>(128);
|
|
HashSet<RectTransform> seen = new HashSet<RectTransform>();
|
|
Stack<Transform> stack = new Stack<Transform>();
|
|
stack.Push(root);
|
|
|
|
while (stack.Count > 0)
|
|
{
|
|
Transform parent = stack.Pop();
|
|
if (parent == null) continue;
|
|
|
|
for (int i = 0; i < parent.childCount; i++)
|
|
{
|
|
Transform child = parent.GetChild(i);
|
|
if (child == null) continue;
|
|
|
|
RectTransform rt = child as RectTransform;
|
|
bool canConsider = rt != null && child.gameObject.activeInHierarchy;
|
|
bool included = false;
|
|
if (canConsider
|
|
&& (includePredicate == null || includePredicate(rt))
|
|
&& IsPanelLike(child)
|
|
&& seen.Add(rt))
|
|
{
|
|
list.Add(rt);
|
|
included = true;
|
|
}
|
|
|
|
if (!included || !IsLayoutSensitive(rt))
|
|
{
|
|
stack.Push(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
private static List<RectTransform> CollectKeyDownRects(Transform root)
|
|
{
|
|
List<RectTransform> list = new List<RectTransform>(16);
|
|
RectTransform[] all = root.GetComponentsInChildren<RectTransform>(true);
|
|
for (int i = 0; i < all.Length; i++)
|
|
{
|
|
RectTransform rt = all[i];
|
|
if (rt == null || !rt.gameObject.activeInHierarchy) continue;
|
|
if (rt == root) continue;
|
|
|
|
string n = rt.name ?? string.Empty;
|
|
if (n.IndexOf("sdw 3", StringComparison.OrdinalIgnoreCase) < 0) continue;
|
|
list.Add(rt);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
private static bool IsPanelLike(Transform tr)
|
|
{
|
|
if (tr == null) return false;
|
|
if (tr.GetComponent<Selectable>() != null) return true;
|
|
if (tr.GetComponent<Graphic>() != null) return true;
|
|
if (tr.GetComponent<LayoutGroup>() != null) return true;
|
|
if (tr.GetComponent<CanvasGroup>() != null) return true;
|
|
return tr.childCount > 0;
|
|
}
|
|
|
|
private static int CompareByTopThenLeft(RectTransform a, RectTransform b)
|
|
{
|
|
if (a == null && b == null) return 0;
|
|
if (a == null) return 1;
|
|
if (b == null) return -1;
|
|
|
|
Vector3 pa = a.position;
|
|
Vector3 pb = b.position;
|
|
|
|
int xOrder = pa.x.CompareTo(pb.x); // left -> right
|
|
if (xOrder != 0) return xOrder;
|
|
return -pa.y.CompareTo(pb.y); // top -> bottom
|
|
}
|
|
}
|