Skip to content

Commit 267aad2

Browse files
Add animated score change display with color flash and scaling effects
- Implemented `AnimateScoreChange` method in `ScoreManager` for visualizing score updates with counting tween, color flash, and scale punch. - Refactored money text formatting logic into dedicated helper methods (`FormatMainMoneyText`, `FormatShopMoneyText`) for code clarity. - Updated `TurnController` to integrate score animation during round transitions.
1 parent d19978b commit 267aad2

2 files changed

Lines changed: 135 additions & 19 deletions

File tree

Card Core/ScoreManager.cs

Lines changed: 130 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
using System.Collections;
12
using System.Collections.Generic;
23
using System.Linq;
34
using _project.Scripts.Core;
5+
using DG.Tweening;
46
using TMPro;
57
using UnityEngine;
68

@@ -44,27 +46,26 @@ public void ResetMoneys()
4446

4547
public static void UpdateMoneysText(int modifier = 0)
4648
{
47-
if (CardGameMaster.Instance.moneysText)
48-
{
49-
var turnController = CardGameMaster.Instance.turnController;
50-
var displayMoney = Moneys + modifier;
51-
52-
// Display different format based on game mode
53-
if (turnController.currentGameMode == GameMode.Campaign)
54-
{
55-
CardGameMaster.Instance.moneysText!.text =
56-
$"Money: ${displayMoney} Rent Due: ${turnController.moneyGoal}";
57-
}
58-
else
59-
{
60-
// Tutorial or Endless mode - original format
61-
CardGameMaster.Instance.moneysText!.text =
62-
"Moneys: " + "$" + displayMoney + "/" + turnController.moneyGoal;
63-
}
64-
}
49+
var displayMoney = Moneys + modifier;
50+
var turnController = CardGameMaster.Instance ? CardGameMaster.Instance.turnController : null;
51+
52+
if (CardGameMaster.Instance.moneysText && turnController)
53+
CardGameMaster.Instance.moneysText!.text = FormatMainMoneyText(displayMoney, turnController);
6554

6655
if (CardGameMaster.Instance.shopMoneyText)
67-
CardGameMaster.Instance.shopMoneyText!.text = "Moneys: " + "$" + (Moneys + modifier);
56+
CardGameMaster.Instance.shopMoneyText!.text = FormatShopMoneyText(displayMoney);
57+
}
58+
59+
private static string FormatMainMoneyText(int value, TurnController tc)
60+
{
61+
return tc.currentGameMode == GameMode.Campaign
62+
? $"Money: ${value} Rent Due: ${tc.moneyGoal}"
63+
: $"Moneys: ${value}/{tc.moneyGoal}";
64+
}
65+
66+
private static string FormatShopMoneyText(int value)
67+
{
68+
return $"Moneys: ${value}";
6869
}
6970

7071
public static void SetScore(int newScore)
@@ -187,5 +188,115 @@ private int CalculateBonuses()
187188
var totalBonus = bonuses.Sum(b => b.BonusValue);
188189
return totalBonus;
189190
}
191+
192+
#region Score Animation
193+
194+
private static readonly Color GainColor = new(0.2f, 0.9f, 0.3f);
195+
private static readonly Color LossColor = new(0.95f, 0.25f, 0.2f);
196+
private const float CountDuration = 1.0f;
197+
private const float PunchScale = 0.35f;
198+
private const float PunchDuration = 1.5f;
199+
200+
private Sequence _moneyAnimSequence;
201+
202+
private void SafeKillSequence()
203+
{
204+
if (_moneyAnimSequence != null && _moneyAnimSequence.IsActive())
205+
_moneyAnimSequence.Kill();
206+
_moneyAnimSequence = null;
207+
}
208+
209+
private void OnDestroy() => SafeKillSequence();
210+
211+
/// <summary>
212+
/// Animates the money display from <paramref name="previousScore" /> to <paramref name="finalScore" />
213+
/// with a counting tween, color flash, and scale punch.
214+
/// Call after <see cref="CalculateScore" /> which sets the canonical value.
215+
/// </summary>
216+
public IEnumerator AnimateScoreChange(int previousScore, int finalScore)
217+
{
218+
var delta = finalScore - previousScore;
219+
if (delta == 0) yield break;
220+
221+
SafeKillSequence();
222+
223+
var moneysText = CardGameMaster.Instance ? CardGameMaster.Instance.moneysText : null;
224+
var shopText = CardGameMaster.Instance ? CardGameMaster.Instance.shopMoneyText : null;
225+
226+
if (!moneysText && !shopText) yield break;
227+
228+
var flashColor = delta > 0 ? GainColor : LossColor;
229+
var displayValue = previousScore;
230+
231+
// Reset display to old value — CalculateScore already set the final value
232+
SetMoneyTextRaw(previousScore);
233+
234+
var seq = DOTween.Sequence();
235+
var origMainColor = moneysText ? moneysText.color : Color.white;
236+
var origShopColor = shopText ? shopText.color : Color.white;
237+
238+
// Set flash color immediately
239+
if (moneysText) moneysText.color = flashColor;
240+
if (shopText) shopText.color = flashColor;
241+
242+
// Count tween: animate displayValue from previous to final
243+
seq.Append(
244+
DOTween.To(() => displayValue, x =>
245+
{
246+
displayValue = x;
247+
SetMoneyTextRaw(x);
248+
}, finalScore, CountDuration)
249+
.SetEase(Ease.OutQuart)
250+
);
251+
252+
// Scale punch runs in parallel with count
253+
if (moneysText)
254+
seq.Join(moneysText.transform.DOPunchScale(Vector3.one * PunchScale, PunchDuration, 6, 0.7f));
255+
if (shopText)
256+
seq.Join(shopText.transform.DOPunchScale(Vector3.one * PunchScale, PunchDuration, 6, 0.7f));
257+
258+
// Color fades back to the original over the count duration
259+
if (moneysText)
260+
seq.Join(
261+
DOTween.To(() => moneysText.color, c => moneysText.color = c, origMainColor, CountDuration)
262+
.SetEase(Ease.InQuad)
263+
);
264+
if (shopText)
265+
seq.Join(
266+
DOTween.To(() => shopText.color, c => shopText.color = c, origShopColor, CountDuration)
267+
.SetEase(Ease.InQuad)
268+
);
269+
270+
// Ensure canonical final state
271+
var complete = false;
272+
seq.OnComplete(() =>
273+
{
274+
if (moneysText) moneysText.color = origMainColor;
275+
if (shopText) shopText.color = origShopColor;
276+
UpdateMoneysText();
277+
complete = true;
278+
});
279+
280+
seq.SetLink(gameObject, LinkBehaviour.KillOnDisable);
281+
_moneyAnimSequence = seq;
282+
283+
// Wait for the sequence to actually finish (robust against timing changes)
284+
yield return new WaitUntil(() => complete || _moneyAnimSequence == null);
285+
}
286+
287+
private static void SetMoneyTextRaw(int displayValue)
288+
{
289+
var moneysText = CardGameMaster.Instance ? CardGameMaster.Instance.moneysText : null;
290+
var shopText = CardGameMaster.Instance ? CardGameMaster.Instance.shopMoneyText : null;
291+
var turnController = CardGameMaster.Instance ? CardGameMaster.Instance.turnController : null;
292+
293+
if (moneysText && turnController)
294+
moneysText.text = FormatMainMoneyText(displayValue, turnController);
295+
296+
if (shopText)
297+
shopText.text = FormatShopMoneyText(displayValue);
298+
}
299+
300+
#endregion
190301
}
191302
}

Card Core/TurnController.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,11 @@ private IEnumerator EndRound(float delayTime = 2f, bool advanceTutorial = false)
721721
var score = _scoreManager.CalculateScore();
722722
if (_scoreManager) _scoreManager.treatmentCost = 0;
723723
var scoreDelta = score - scoreBeforeRound;
724+
725+
// Animate the money text change (count-up/down with color flash + scale punch)
726+
if (_scoreManager)
727+
yield return StartCoroutine(_scoreManager.AnimateScoreChange(scoreBeforeRound, score));
728+
724729
var roundVictory = !IsActiveTutorialStep && ScoreManager.GetMoneys() >= moneyGoal;
725730

726731
// Count plant health status and record round end analytics

0 commit comments

Comments
 (0)