diff --git a/client/Assets/Vanguard/Runtime/UI/BattleView.cs b/client/Assets/Vanguard/Runtime/UI/BattleView.cs index a41f5c5..569a7e0 100644 --- a/client/Assets/Vanguard/Runtime/UI/BattleView.cs +++ b/client/Assets/Vanguard/Runtime/UI/BattleView.cs @@ -70,6 +70,9 @@ namespace Vanguard.UI private readonly HashSet _highlightKeys = new HashSet(StringComparer.Ordinal); private int _pendingBoosterSlot = -1; private int? _triggerPowerSlot; + private readonly Dictionary _cardPanels = + new Dictionary(StringComparer.Ordinal); + private readonly List _cardAnimations = new List(); public BattleView(Transform parent, MetaService meta, Action onExit) { @@ -236,6 +239,7 @@ namespace Vanguard.UI UpdateClock(); UpdateFeedback(); + UpdateCardAnimations(); } private void ShowFeedback(string text, Color color, float duration = 1.25f, bool flash = false) @@ -294,6 +298,72 @@ namespace Vanguard.UI StartNextFeedback(); } + private void UpdateCardAnimations() + { + for (var i = _cardAnimations.Count - 1; i >= 0; i--) + { + var animation = _cardAnimations[i]; + if (animation.Target == null) + { + _cardAnimations.RemoveAt(i); + continue; + } + + animation.Time += Time.unscaledDeltaTime; + var t = Mathf.Clamp01(animation.Time / animation.Duration); + var eased = 1f - Mathf.Pow(1f - t, 3f); + var scale = Mathf.Lerp(animation.StartScale, 1f, eased); + animation.Target.localScale = new Vector3(scale, scale, 1f); + + if (t >= 1f) + { + animation.Target.localScale = Vector3.one; + _cardAnimations.RemoveAt(i); + } + } + } + + private void AnimateCardIn(RectTransform target) + { + if (target == null) + return; + + target.localScale = Vector3.one * 0.86f; + _cardAnimations.Add(new CardAnimation(target, 0.18f, 0.86f)); + } + + private void PulseCard(int player, int slot, float startScale) + { + if (!_cardPanels.TryGetValue(CardKey(player, slot), out var target) || target == null) + return; + + for (var i = _cardAnimations.Count - 1; i >= 0; i--) + { + if (_cardAnimations[i].Target == target) + _cardAnimations.RemoveAt(i); + } + + target.localScale = Vector3.one * startScale; + _cardAnimations.Add(new CardAnimation(target, 0.28f, startScale)); + } + + private static string CardKey(int player, int slot) => player + ":" + slot; + + private sealed class CardAnimation + { + public CardAnimation(RectTransform target, float duration, float startScale) + { + Target = target; + Duration = duration; + StartScale = startScale; + } + + public RectTransform Target { get; } + public float Duration { get; } + public float StartScale { get; } + public float Time { get; set; } + } + private void SkipFeedback() { _feedbackQueue.Clear(); @@ -723,6 +793,8 @@ namespace Vanguard.UI return; ClearChildren(_board); + _cardPanels.Clear(); + _cardAnimations.Clear(); var opponentIndex = _view.You == 0 ? 1 : 0; var opponent = _view.Players.FirstOrDefault(player => player.Index == opponentIndex) ?? (_view.Players.Count > opponentIndex ? _view.Players[opponentIndex] : null); @@ -733,6 +805,13 @@ namespace Vanguard.UI AddGuardZone(_view); AddSideField(self, true); AddHand(self); + + if (_view.Battle != null) + { + PulseCard(_view.Battle.AttackerPlayer, _view.Battle.AttackerSlot, 1.11f); + PulseCard(1 - _view.Battle.AttackerPlayer, _view.Battle.TargetSlot, 1.11f); + } + RebuildIntents(_view); UpdateClock(); @@ -993,6 +1072,10 @@ namespace Vanguard.UI outline.effectDistance = highlighted ? new Vector2(3f, -3f) : new Vector2(2f, -2f); + + _cardPanels[CardKey(playerIndex, slotIndex)] = panel; + if (card != null) + AnimateCardIn(panel); UiKit.AddVerticalLayout(panel, 4, 2); var slotText = UiKit.CreateText(