修了很多bug和增加功能,优化不少问题
This commit is contained in:
@@ -5,7 +5,6 @@ using UnityEngine.Serialization;
|
||||
using UnityEngine.SceneManagement;
|
||||
using System.Collections;
|
||||
using UnityEngine.Audio;
|
||||
using UnityEditor;
|
||||
|
||||
public class settlementController : MonoBehaviour
|
||||
{
|
||||
@@ -30,12 +29,17 @@ public class settlementController : MonoBehaviour
|
||||
[Tooltip("Documentation text normalized.")]
|
||||
public Text thisLevel_currentPercentage_Text;
|
||||
public Text finalScore_Text;
|
||||
public Image finalLevel_img;
|
||||
public Text pmScoreSum_Text;
|
||||
public Text idolScoreSum_Text;
|
||||
public Text accuracy_Text;
|
||||
public Text personalRecord_Text;
|
||||
|
||||
public Image thisLevel_progressBar_Image;
|
||||
|
||||
[Header("Rank Config")]
|
||||
public RankConfig rankConfig;
|
||||
|
||||
[Header("Accuracy Weights")]
|
||||
public float perfect_weight = 1f;
|
||||
public float great_weight = 0.6666667f;
|
||||
@@ -54,6 +58,12 @@ public class settlementController : MonoBehaviour
|
||||
[Header("Inspector")]
|
||||
[SerializeField] private long moneyToGive_thisLevel;
|
||||
|
||||
[Header("mvp")]
|
||||
public GameObject mvp_object;
|
||||
public Text mvp_score;
|
||||
public Image mvp_heroIcon;
|
||||
public Text mvp_heroName;
|
||||
|
||||
// Documentation text normalized.
|
||||
[Header("Inspector")]
|
||||
public Text perfectHitCount_Text;
|
||||
@@ -104,8 +114,32 @@ public class settlementController : MonoBehaviour
|
||||
|
||||
private Coroutine musicTransitionCoroutine;
|
||||
private Coroutine canvasFadeCoroutine;
|
||||
private Coroutine settlementIntroCoroutine;
|
||||
private bool settlementUiInitialized = false;
|
||||
|
||||
[Header("Settlement Intro Animation")]
|
||||
[SerializeField] private bool enableDetailedSettlementIntro = true;
|
||||
[SerializeField] private int introFlashCount = 2;
|
||||
[SerializeField] private float introFlashDuration = 0.24f;
|
||||
[SerializeField] private float introMoveDuration = 0.42f;
|
||||
[SerializeField] private float introNumberDuration = 0.48f;
|
||||
[SerializeField] private float introStepGap = 0.06f;
|
||||
|
||||
private int targetPmScore;
|
||||
private int targetIdolScore;
|
||||
private int targetTotalScore;
|
||||
private float targetAccuracyPercent;
|
||||
private float targetTotalPercent;
|
||||
|
||||
private int targetPerfectCount;
|
||||
private int targetGreatCount;
|
||||
private int targetGoodCount;
|
||||
private int targetMissCount;
|
||||
private float targetPerfectPercent;
|
||||
private float targetGreatPercent;
|
||||
private float targetGoodPercent;
|
||||
private float targetMissPercent;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Documentation text normalized.
|
||||
@@ -166,6 +200,12 @@ public class settlementController : MonoBehaviour
|
||||
StopCoroutine(canvasFadeCoroutine);
|
||||
canvasFadeCoroutine = null;
|
||||
}
|
||||
|
||||
if (settlementIntroCoroutine != null)
|
||||
{
|
||||
StopCoroutine(settlementIntroCoroutine);
|
||||
settlementIntroCoroutine = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
@@ -210,32 +250,62 @@ public class settlementController : MonoBehaviour
|
||||
songName_Text.text = bmm.parsedTitle;
|
||||
thisSong_backPic.sprite = bmm.assignedSongData.fullscreen_songPicture;
|
||||
|
||||
finalScore_Text.text = (sm.allSum_pmScore + sm.allSum_idolScore).ToString();
|
||||
pmScoreSum_Text.text = sm.allSum_pmScore.ToString();
|
||||
idolScoreSum_Text.text = sm.allSum_idolScore.ToString();
|
||||
thisLevel_currentPercentage_Text.text = ((float)(sm.allSum_pmScore + sm.allSum_idolScore) / _1000000 * 100).ToString("F2") + "%";
|
||||
thisLevel_progressBar_Image.fillAmount = (float)(sm.allSum_pmScore + sm.allSum_idolScore) / _1000000;
|
||||
targetPmScore = sm.allSum_pmScore;
|
||||
targetIdolScore = sm.allSum_idolScore;
|
||||
targetTotalScore = targetPmScore + targetIdolScore;
|
||||
targetTotalPercent = (float)targetTotalScore / Mathf.Max(1, _1000000) * 100f;
|
||||
|
||||
int noteCountSum = sm.countPerfect + sm.countGreat + sm.countGood + sm.countMiss;
|
||||
finalScore_Text.text = targetTotalScore.ToString();
|
||||
pmScoreSum_Text.text = targetPmScore.ToString();
|
||||
|
||||
perfectHitCount_Text.text = sm.countPerfect.ToString();
|
||||
greatHitCount_Text.text = sm.countGreat.ToString();
|
||||
goodHitCount_Text.text = sm.countGood.ToString();
|
||||
missHitCount_Text.text = sm.countMiss.ToString();
|
||||
// 根据总分从 rankConfig 获取等级图标并更新 finalLevel_img
|
||||
if (rankConfig != null && finalLevel_img != null)
|
||||
{
|
||||
finalLevel_img.sprite = rankConfig.GetRankSprite(targetTotalScore);
|
||||
Debug.Log($"[settlementController] 根据总分 {targetTotalScore} 更新等级图标");
|
||||
}
|
||||
|
||||
perfect_barFill_Image.fillAmount = (float)sm.countPerfect / noteCountSum;
|
||||
great_barFill_Image.fillAmount = (float)sm.countGreat / noteCountSum;
|
||||
good_barFill_Image.fillAmount = (float)sm.countGood / noteCountSum;
|
||||
miss_barFill_Image.fillAmount = (float)sm.countMiss / noteCountSum;
|
||||
idolScoreSum_Text.text = targetIdolScore.ToString();
|
||||
thisLevel_currentPercentage_Text.text = targetTotalPercent.ToString("F2") + "%";
|
||||
thisLevel_progressBar_Image.fillAmount = (float)targetTotalScore / Mathf.Max(1, _1000000);
|
||||
|
||||
gameNote_rate.text = "已完成 " + noteCountSum.ToString() + "/" + bmm.parsedNoteAmount.ToString();
|
||||
int noteCountRaw = sm.countPerfect + sm.countGreat + sm.countGood + sm.countMiss;
|
||||
int noteCountSum = Mathf.Max(1, noteCountRaw);
|
||||
|
||||
perfectHitPercent_Text.text = ((float)sm.countPerfect / noteCountSum * 100).ToString("F1") + "%";
|
||||
greatHitPercent_Text.text = ((float)sm.countGreat / noteCountSum * 100).ToString("F1") + "%";
|
||||
goodHitPercent_Text.text = ((float)sm.countGood / noteCountSum * 100).ToString("F1") + "%";
|
||||
missHitPercent_Text.text = ((float)sm.countMiss / noteCountSum * 100).ToString("F1") + "%";
|
||||
targetPerfectCount = sm.countPerfect;
|
||||
targetGreatCount = sm.countGreat;
|
||||
targetGoodCount = sm.countGood;
|
||||
targetMissCount = sm.countMiss;
|
||||
|
||||
accuracy_Text.text = ((float)(sm.countPerfect * perfect_weight + sm.countGreat * great_weight + sm.countGood * good_weight + sm.countMiss * miss_weight) / noteCountSum * 100).ToString("F3") + "%";
|
||||
targetPerfectPercent = (float)targetPerfectCount / noteCountSum * 100f;
|
||||
targetGreatPercent = (float)targetGreatCount / noteCountSum * 100f;
|
||||
targetGoodPercent = (float)targetGoodCount / noteCountSum * 100f;
|
||||
targetMissPercent = (float)targetMissCount / noteCountSum * 100f;
|
||||
|
||||
targetAccuracyPercent =
|
||||
((float)(targetPerfectCount * perfect_weight +
|
||||
targetGreatCount * great_weight +
|
||||
targetGoodCount * good_weight +
|
||||
targetMissCount * miss_weight) / noteCountSum * 100f);
|
||||
|
||||
perfectHitCount_Text.text = targetPerfectCount.ToString();
|
||||
greatHitCount_Text.text = targetGreatCount.ToString();
|
||||
goodHitCount_Text.text = targetGoodCount.ToString();
|
||||
missHitCount_Text.text = targetMissCount.ToString();
|
||||
|
||||
perfect_barFill_Image.fillAmount = (float)targetPerfectCount / noteCountSum;
|
||||
great_barFill_Image.fillAmount = (float)targetGreatCount / noteCountSum;
|
||||
good_barFill_Image.fillAmount = (float)targetGoodCount / noteCountSum;
|
||||
miss_barFill_Image.fillAmount = (float)targetMissCount / noteCountSum;
|
||||
|
||||
gameNote_rate.text = "已完成 " + noteCountRaw.ToString() + "/" + bmm.parsedNoteAmount.ToString();
|
||||
|
||||
perfectHitPercent_Text.text = targetPerfectPercent.ToString("F1") + "%";
|
||||
greatHitPercent_Text.text = targetGreatPercent.ToString("F1") + "%";
|
||||
goodHitPercent_Text.text = targetGoodPercent.ToString("F1") + "%";
|
||||
missHitPercent_Text.text = targetMissPercent.ToString("F1") + "%";
|
||||
|
||||
accuracy_Text.text = targetAccuracyPercent.ToString("F3") + "%";
|
||||
|
||||
// Timing Statistics
|
||||
if (earlyHitCount_Text != null) earlyHitCount_Text.text = sm.countEarly.ToString();
|
||||
@@ -249,7 +319,7 @@ public class settlementController : MonoBehaviour
|
||||
else Debug.LogError("score manager is null");
|
||||
|
||||
// --- Achievement System: Load and display achievements ---
|
||||
if (InGamePerformanceManager.Instance != null)
|
||||
if (InGamePerformanceManager.Instance != null && !GameConfig.autoPlayEnabled)
|
||||
{
|
||||
// Calculate total cure, damage and mana from teamUIController
|
||||
float totalCure = 0;
|
||||
@@ -299,13 +369,27 @@ public class settlementController : MonoBehaviour
|
||||
getThisSong_info(); // ensure thisSong_so set
|
||||
if (thisSong_so != null && bmm != null)
|
||||
{
|
||||
int oldPersonalRecord = thisSong_so.personalRecord;
|
||||
int diff = bmm.assignedDifficulty;
|
||||
if (diff >= 0)
|
||||
if (diff >= 0 && !GameConfig.autoPlayEnabled)
|
||||
{
|
||||
int pm = sm != null ? sm.allSum_pmScore : 0;
|
||||
int idol = sm != null ? sm.allSum_idolScore : 0;
|
||||
thisSong_so.ApplySettlementResult(diff, pm, idol, _1000000);
|
||||
}
|
||||
|
||||
if (personalRecord_Text != null)
|
||||
{
|
||||
int currentRecord = thisSong_so.personalRecord;
|
||||
personalRecord_Text.text = currentRecord.ToString();
|
||||
|
||||
// If current total score broke the overall personal record (all difficulties)
|
||||
// and we are NOT in autoplay (since we didn't save the result in autoplay)
|
||||
if (!GameConfig.autoPlayEnabled && targetTotalScore > oldPersonalRecord)
|
||||
{
|
||||
personalRecord_Text.color = new Color(0f, 0.392f, 0f); // Dark Green #006400
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate settlement team cards if a loader is assigned
|
||||
@@ -330,8 +414,177 @@ public class settlementController : MonoBehaviour
|
||||
SetMvpHeroImageFromTopScorer();
|
||||
StartMusicTransition();
|
||||
|
||||
// Documentation text normalized.
|
||||
StartCanvasFadeIn();
|
||||
// Play settlement entry animation flow.
|
||||
StartSettlementIntro();
|
||||
}
|
||||
|
||||
private void StartSettlementIntro()
|
||||
{
|
||||
if (!enableDetailedSettlementIntro)
|
||||
{
|
||||
StartCanvasFadeIn();
|
||||
return;
|
||||
}
|
||||
|
||||
if (settlementIntroCoroutine != null)
|
||||
{
|
||||
StopCoroutine(settlementIntroCoroutine);
|
||||
settlementIntroCoroutine = null;
|
||||
}
|
||||
|
||||
settlementIntroCoroutine = StartCoroutine(SettlementIntroRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator SettlementIntroRoutine()
|
||||
{
|
||||
if (settlementCanvasGroup == null)
|
||||
{
|
||||
StartCanvasFadeIn();
|
||||
yield break;
|
||||
}
|
||||
|
||||
settlementCanvasGroup.alpha = 0f;
|
||||
settlementCanvasGroup.interactable = false;
|
||||
settlementCanvasGroup.blocksRaycasts = false;
|
||||
|
||||
Transform settleRoot = settlementCanvasGroup.transform;
|
||||
Vector3 settleBaseScale = settleRoot != null ? settleRoot.localScale : Vector3.one;
|
||||
|
||||
Transform rightRoot = FindDescendantByName(settleRoot, "right");
|
||||
Transform leftRoot = FindDescendantByName(settleRoot, "left");
|
||||
Transform picsRoot = FindDescendantByName(leftRoot != null ? leftRoot : settleRoot, "pics");
|
||||
|
||||
RectTransform rightBottomRt = FindRectByName(rightRoot != null ? rightRoot : settleRoot, "right_bottom");
|
||||
Transform quhuiImageTr = FindDescendantByName(picsRoot != null ? picsRoot : settleRoot, "quhuiImage");
|
||||
RectTransform scoreBottomRt = FindRectByName(settleRoot, "score_bottom");
|
||||
RectTransform shenstarsRt = FindRectByName(settleRoot, "shenstars");
|
||||
RectTransform rewardRt = FindRectByName(settleRoot, "reward");
|
||||
|
||||
Transform heroDApicTr = FindDescendantByName(rightRoot != null ? rightRoot : settleRoot, "heroDApic");
|
||||
Transform heroMaskTr = FindDescendantByName(heroDApicTr != null ? heroDApicTr : settleRoot, "MASK");
|
||||
Transform lihuiTr = FindDescendantByName(heroMaskTr != null ? heroMaskTr : settleRoot, "lihui");
|
||||
RectTransform teamDisplayingRt = FindRectByName(rightRoot != null ? rightRoot : settleRoot, "teamDisplaying");
|
||||
|
||||
Transform buttonsRoot = FindDescendantByName(rightRoot != null ? rightRoot : settleRoot, "buttons");
|
||||
List<Transform> rightButtons = CollectDirectChildren(buttonsRoot);
|
||||
|
||||
Transform hitStatusTr = FindDescendantByName(settleRoot, "hitStatus");
|
||||
Transform statusTr = FindDescendantByName(hitStatusTr != null ? hitStatusTr : settleRoot, "status");
|
||||
List<Transform> statusGroups = CollectDirectChildren(statusTr);
|
||||
|
||||
Transform perfectGroup = FindGroupRoot(perfectHitCount_Text, perfectHitPercent_Text);
|
||||
Transform greatGroup = FindGroupRoot(greatHitCount_Text, greatHitPercent_Text);
|
||||
Transform goodGroup = FindGroupRoot(goodHitCount_Text, goodHitPercent_Text);
|
||||
Transform missGroup = FindGroupRoot(missHitCount_Text, missHitPercent_Text);
|
||||
List<Transform> orderedGroups = new List<Transform>();
|
||||
if (perfectGroup != null) orderedGroups.Add(perfectGroup);
|
||||
if (greatGroup != null) orderedGroups.Add(greatGroup);
|
||||
if (goodGroup != null) orderedGroups.Add(goodGroup);
|
||||
if (missGroup != null) orderedGroups.Add(missGroup);
|
||||
if (orderedGroups.Count == 0) orderedGroups = statusGroups;
|
||||
if (perfectGroup == null && orderedGroups.Count > 0) perfectGroup = orderedGroups[0];
|
||||
if (greatGroup == null && orderedGroups.Count > 1) greatGroup = orderedGroups[1];
|
||||
if (goodGroup == null && orderedGroups.Count > 2) goodGroup = orderedGroups[2];
|
||||
if (missGroup == null && orderedGroups.Count > 3) missGroup = orderedGroups[3];
|
||||
|
||||
// Initial hidden/setup state before entrance.
|
||||
if (rightBottomRt != null) SetAnchoredX(rightBottomRt, 1799f);
|
||||
|
||||
if (quhuiImageTr != null) SetCanvasAlpha(quhuiImageTr, 0f);
|
||||
if (scoreBottomRt != null) { SetAnchoredX(scoreBottomRt, -1646.9f); SetCanvasAlpha(scoreBottomRt, 0f); }
|
||||
if (shenstarsRt != null) { SetAnchoredX(shenstarsRt, -1602.1f); SetCanvasAlpha(shenstarsRt, 0f); }
|
||||
if (rewardRt != null) { SetAnchoredY(rewardRt, -494.3f); SetCanvasAlpha(rewardRt, 0f); }
|
||||
|
||||
if (lihuiTr != null) SetCanvasAlpha(lihuiTr, 0f);
|
||||
if (teamDisplayingRt != null) { SetAnchoredX(teamDisplayingRt, 2265f); SetCanvasAlpha(teamDisplayingRt, 0f); }
|
||||
|
||||
for (int i = 0; i < rightButtons.Count; i++)
|
||||
{
|
||||
if (rightButtons[i] != null) SetCanvasAlpha(rightButtons[i], 0f);
|
||||
}
|
||||
|
||||
for (int i = 0; i < orderedGroups.Count; i++)
|
||||
{
|
||||
if (orderedGroups[i] != null) SetCanvasAlpha(orderedGroups[i], 0f);
|
||||
}
|
||||
|
||||
PrepareTextForCountUp(pmScoreSum_Text, false, 0);
|
||||
PrepareTextForCountUp(idolScoreSum_Text, false, 0);
|
||||
PrepareTextForCountUp(accuracy_Text, true, 0f, "F3");
|
||||
PrepareTextForCountUp(finalScore_Text, false, 0);
|
||||
PrepareTextForCountUp(thisLevel_currentPercentage_Text, true, 0f, "F2");
|
||||
PrepareTextForCountUp(perfectHitCount_Text, false, 0);
|
||||
PrepareTextForCountUp(greatHitCount_Text, false, 0);
|
||||
PrepareTextForCountUp(goodHitCount_Text, false, 0);
|
||||
PrepareTextForCountUp(missHitCount_Text, false, 0);
|
||||
PrepareTextForCountUp(perfectHitPercent_Text, true, 0f, "F1");
|
||||
PrepareTextForCountUp(greatHitPercent_Text, true, 0f, "F1");
|
||||
PrepareTextForCountUp(goodHitPercent_Text, true, 0f, "F1");
|
||||
PrepareTextForCountUp(missHitPercent_Text, true, 0f, "F1");
|
||||
|
||||
// settlement: flash + scale 1.2 -> 1.0
|
||||
yield return StartCoroutine(FlashCanvasGroupWithScale(settlementCanvasGroup, settleRoot, settleBaseScale * 1.2f, settleBaseScale, introFlashCount, introFlashDuration));
|
||||
|
||||
// right/right_bottom: X 1799 -> 1677.75
|
||||
if (rightBottomRt != null)
|
||||
yield return StartCoroutine(AnimateAnchoredXAndFade(rightBottomRt, 1799f, 1677.75f, introMoveDuration, false));
|
||||
|
||||
// left/pics/quhuiImage flash
|
||||
if (quhuiImageTr != null)
|
||||
yield return StartCoroutine(FlashInTransform(quhuiImageTr, introFlashCount, introFlashDuration));
|
||||
|
||||
// score_bottom + shenstars move in together
|
||||
Coroutine scoreBottomIn = null;
|
||||
Coroutine shenstarsIn = null;
|
||||
if (scoreBottomRt != null)
|
||||
scoreBottomIn = StartCoroutine(AnimateAnchoredXAndFade(scoreBottomRt, -1646.9f, -432.4f, introMoveDuration, true));
|
||||
if (shenstarsRt != null)
|
||||
shenstarsIn = StartCoroutine(AnimateAnchoredXAndFade(shenstarsRt, -1602.1f, -437.5785f, introMoveDuration, true));
|
||||
if (scoreBottomIn != null || shenstarsIn != null)
|
||||
yield return WaitRealtime(introMoveDuration + 0.02f);
|
||||
|
||||
// reward: Y -494.3 -> 0 with fade
|
||||
if (rewardRt != null)
|
||||
yield return StartCoroutine(AnimateAnchoredYAndFade(rewardRt, -494.3f, 0f, introMoveDuration));
|
||||
|
||||
// score numbers: pm -> idol -> accuracy
|
||||
yield return StartCoroutine(AnimateIntText(pmScoreSum_Text, targetPmScore, introNumberDuration));
|
||||
yield return StartCoroutine(AnimateIntText(idolScoreSum_Text, targetIdolScore, introNumberDuration));
|
||||
yield return StartCoroutine(AnimateFloatPercentText(accuracy_Text, targetAccuracyPercent, introNumberDuration, "F3"));
|
||||
|
||||
// total + percent after above, and flash once completed
|
||||
Coroutine totalScoreIn = StartCoroutine(AnimateIntText(finalScore_Text, targetTotalScore, introNumberDuration));
|
||||
Coroutine totalPercentIn = StartCoroutine(AnimateFloatPercentText(thisLevel_currentPercentage_Text, targetTotalPercent, introNumberDuration, "F2"));
|
||||
if (totalScoreIn != null || totalPercentIn != null)
|
||||
yield return WaitRealtime(introNumberDuration + 0.02f);
|
||||
yield return StartCoroutine(FlashInTransform(finalScore_Text != null ? finalScore_Text.transform : null, introFlashCount, introFlashDuration * 0.9f));
|
||||
yield return StartCoroutine(FlashInTransform(thisLevel_currentPercentage_Text != null ? thisLevel_currentPercentage_Text.transform : null, introFlashCount, introFlashDuration * 0.9f));
|
||||
|
||||
// hitStatus/status groups: flash in and count up numbers
|
||||
yield return StartCoroutine(PlayStatusGroupIn(perfectGroup, perfectHitCount_Text, targetPerfectCount, perfectHitPercent_Text, targetPerfectPercent));
|
||||
yield return StartCoroutine(PlayStatusGroupIn(greatGroup, greatHitCount_Text, targetGreatCount, greatHitPercent_Text, targetGreatPercent));
|
||||
yield return StartCoroutine(PlayStatusGroupIn(goodGroup, goodHitCount_Text, targetGoodCount, goodHitPercent_Text, targetGoodPercent));
|
||||
yield return StartCoroutine(PlayStatusGroupIn(missGroup, missHitCount_Text, targetMissCount, missHitPercent_Text, targetMissPercent));
|
||||
|
||||
// heroDApic/MASK/lihui flash + teamDisplaying move/fade in
|
||||
if (lihuiTr != null)
|
||||
yield return StartCoroutine(FlashInTransform(lihuiTr, introFlashCount, introFlashDuration));
|
||||
if (teamDisplayingRt != null)
|
||||
yield return StartCoroutine(AnimateAnchoredXAndFade(teamDisplayingRt, 2265f, 1490.09f, introMoveDuration, true));
|
||||
|
||||
// right/buttons: each button flashes in sequence
|
||||
for (int i = 0; i < rightButtons.Count; i++)
|
||||
{
|
||||
Transform btn = rightButtons[i];
|
||||
if (btn == null) continue;
|
||||
yield return StartCoroutine(FlashInTransform(btn, introFlashCount, introFlashDuration));
|
||||
yield return WaitRealtime(introStepGap);
|
||||
}
|
||||
|
||||
settlementCanvasGroup.alpha = 1f;
|
||||
settlementCanvasGroup.interactable = true;
|
||||
settlementCanvasGroup.blocksRaycasts = true;
|
||||
settlementIntroCoroutine = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -556,6 +809,280 @@ public class settlementController : MonoBehaviour
|
||||
canvasFadeCoroutine = null;
|
||||
}
|
||||
|
||||
private IEnumerator PlayStatusGroupIn(Transform groupRoot, Text countText, int countTarget, Text percentText, float percentTarget)
|
||||
{
|
||||
if (groupRoot != null)
|
||||
yield return StartCoroutine(FlashInTransform(groupRoot, introFlashCount, introFlashDuration));
|
||||
|
||||
Coroutine c1 = null;
|
||||
Coroutine c2 = null;
|
||||
if (countText != null) c1 = StartCoroutine(AnimateIntText(countText, countTarget, introNumberDuration));
|
||||
if (percentText != null) c2 = StartCoroutine(AnimateFloatPercentText(percentText, percentTarget, introNumberDuration, "F1"));
|
||||
|
||||
if (c1 != null || c2 != null)
|
||||
yield return WaitRealtime(introNumberDuration + 0.02f);
|
||||
}
|
||||
|
||||
private IEnumerator AnimateIntText(Text target, int toValue, float duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
SetCanvasAlpha(target.transform, 1f);
|
||||
|
||||
float elapsed = 0f;
|
||||
float dur = Mathf.Max(0.01f, duration);
|
||||
|
||||
while (elapsed < dur)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = EaseOutCubic(Mathf.Clamp01(elapsed / dur));
|
||||
int value = Mathf.RoundToInt(Mathf.Lerp(0f, toValue, t));
|
||||
target.text = value.ToString();
|
||||
yield return null;
|
||||
}
|
||||
|
||||
target.text = toValue.ToString();
|
||||
}
|
||||
|
||||
private IEnumerator AnimateFloatPercentText(Text target, float toValue, float duration, string format)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
SetCanvasAlpha(target.transform, 1f);
|
||||
|
||||
float elapsed = 0f;
|
||||
float dur = Mathf.Max(0.01f, duration);
|
||||
|
||||
while (elapsed < dur)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = EaseOutCubic(Mathf.Clamp01(elapsed / dur));
|
||||
float value = Mathf.Lerp(0f, toValue, t);
|
||||
target.text = value.ToString(format) + "%";
|
||||
yield return null;
|
||||
}
|
||||
|
||||
target.text = toValue.ToString(format) + "%";
|
||||
}
|
||||
|
||||
private IEnumerator AnimateAnchoredXAndFade(RectTransform rt, float fromX, float toX, float duration, bool fadeIn)
|
||||
{
|
||||
if (rt == null) yield break;
|
||||
|
||||
SetAnchoredX(rt, fromX);
|
||||
if (fadeIn) SetCanvasAlpha(rt, 0f);
|
||||
|
||||
float elapsed = 0f;
|
||||
float dur = Mathf.Max(0.01f, duration);
|
||||
while (elapsed < dur)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = EaseOutCubic(Mathf.Clamp01(elapsed / dur));
|
||||
SetAnchoredX(rt, Mathf.LerpUnclamped(fromX, toX, t));
|
||||
if (fadeIn) SetCanvasAlpha(rt, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetAnchoredX(rt, toX);
|
||||
if (fadeIn) SetCanvasAlpha(rt, 1f);
|
||||
}
|
||||
|
||||
private IEnumerator AnimateAnchoredYAndFade(RectTransform rt, float fromY, float toY, float duration)
|
||||
{
|
||||
if (rt == null) yield break;
|
||||
|
||||
SetAnchoredY(rt, fromY);
|
||||
SetCanvasAlpha(rt, 0f);
|
||||
|
||||
float elapsed = 0f;
|
||||
float dur = Mathf.Max(0.01f, duration);
|
||||
while (elapsed < dur)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float t = EaseOutCubic(Mathf.Clamp01(elapsed / dur));
|
||||
SetAnchoredY(rt, Mathf.LerpUnclamped(fromY, toY, t));
|
||||
SetCanvasAlpha(rt, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
SetAnchoredY(rt, toY);
|
||||
SetCanvasAlpha(rt, 1f);
|
||||
}
|
||||
|
||||
private IEnumerator FlashCanvasGroupWithScale(CanvasGroup cg, Transform scaleTarget, Vector3 fromScale, Vector3 toScale, int flashes, float duration)
|
||||
{
|
||||
if (cg == null) yield break;
|
||||
int count = Mathf.Max(1, flashes);
|
||||
float total = Mathf.Max(0.05f, duration);
|
||||
float flashWindow = total / Mathf.Max(1, count * 2);
|
||||
|
||||
cg.alpha = 0f;
|
||||
if (scaleTarget != null) scaleTarget.localScale = fromScale;
|
||||
|
||||
float elapsed = 0f;
|
||||
while (elapsed < total)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
float p = Mathf.Clamp01(elapsed / total);
|
||||
|
||||
if (scaleTarget != null)
|
||||
{
|
||||
float k = EaseOutCubic(p);
|
||||
scaleTarget.localScale = Vector3.LerpUnclamped(fromScale, toScale, k);
|
||||
}
|
||||
|
||||
if (flashWindow > 0f)
|
||||
{
|
||||
int seg = Mathf.FloorToInt(elapsed / flashWindow);
|
||||
bool on = (seg % 2) == 0;
|
||||
cg.alpha = on ? 1f : 0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
cg.alpha = 1f;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
cg.alpha = 1f;
|
||||
if (scaleTarget != null) scaleTarget.localScale = toScale;
|
||||
}
|
||||
|
||||
private IEnumerator FlashInTransform(Transform target, int flashes, float duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
int count = Mathf.Max(1, flashes);
|
||||
float total = Mathf.Max(0.05f, duration);
|
||||
float step = total / Mathf.Max(1, count * 2);
|
||||
|
||||
SetCanvasAlpha(target, 0f);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
SetCanvasAlpha(target, 1f);
|
||||
yield return WaitRealtime(step);
|
||||
|
||||
if (i < count - 1)
|
||||
{
|
||||
SetCanvasAlpha(target, 0f);
|
||||
yield return WaitRealtime(step);
|
||||
}
|
||||
}
|
||||
|
||||
SetCanvasAlpha(target, 1f);
|
||||
}
|
||||
|
||||
private void PrepareTextForCountUp(Text t, bool isPercent, float initialValue, string format = "F1")
|
||||
{
|
||||
if (t == null) return;
|
||||
SetCanvasAlpha(t.transform, 0f);
|
||||
t.text = isPercent ? initialValue.ToString(format) + "%" : Mathf.RoundToInt(initialValue).ToString();
|
||||
}
|
||||
|
||||
private void PrepareTextForCountUp(Text t, bool isPercent, int initialValue)
|
||||
{
|
||||
if (t == null) return;
|
||||
SetCanvasAlpha(t.transform, 0f);
|
||||
t.text = isPercent ? initialValue.ToString("F1") + "%" : initialValue.ToString();
|
||||
}
|
||||
|
||||
private static float EaseOutCubic(float t)
|
||||
{
|
||||
float inv = 1f - t;
|
||||
return 1f - inv * inv * inv;
|
||||
}
|
||||
|
||||
private static IEnumerator WaitRealtime(float duration)
|
||||
{
|
||||
float elapsed = 0f;
|
||||
float dur = Mathf.Max(0f, duration);
|
||||
while (elapsed < dur)
|
||||
{
|
||||
elapsed += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetAnchoredX(RectTransform rt, float x)
|
||||
{
|
||||
if (rt == null) return;
|
||||
Vector2 p = rt.anchoredPosition;
|
||||
p.x = x;
|
||||
rt.anchoredPosition = p;
|
||||
}
|
||||
|
||||
private static void SetAnchoredY(RectTransform rt, float y)
|
||||
{
|
||||
if (rt == null) return;
|
||||
Vector2 p = rt.anchoredPosition;
|
||||
p.y = y;
|
||||
rt.anchoredPosition = p;
|
||||
}
|
||||
|
||||
private static List<Transform> CollectDirectChildren(Transform root)
|
||||
{
|
||||
List<Transform> list = new List<Transform>();
|
||||
if (root == null) return list;
|
||||
for (int i = 0; i < root.childCount; i++)
|
||||
{
|
||||
Transform c = root.GetChild(i);
|
||||
if (c != null) list.Add(c);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Transform FindDescendantByName(Transform root, string exactName)
|
||||
{
|
||||
if (root == null || string.IsNullOrEmpty(exactName)) return null;
|
||||
Transform[] all = root.GetComponentsInChildren<Transform>(true);
|
||||
for (int i = 0; i < all.Length; i++)
|
||||
{
|
||||
Transform t = all[i];
|
||||
if (t == null) continue;
|
||||
if (string.Equals(t.name, exactName, System.StringComparison.OrdinalIgnoreCase))
|
||||
return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static RectTransform FindRectByName(Transform root, string exactName)
|
||||
{
|
||||
Transform t = FindDescendantByName(root, exactName);
|
||||
return t as RectTransform;
|
||||
}
|
||||
|
||||
private static Transform FindGroupRoot(Text countText, Text percentText)
|
||||
{
|
||||
Transform c = countText != null ? countText.transform : null;
|
||||
Transform p = percentText != null ? percentText.transform : null;
|
||||
if (c == null && p == null) return null;
|
||||
if (c == null) return p != null ? p.parent : null;
|
||||
if (p == null) return c.parent;
|
||||
|
||||
Transform probe = c;
|
||||
while (probe != null)
|
||||
{
|
||||
if (p.IsChildOf(probe)) return probe;
|
||||
probe = probe.parent;
|
||||
}
|
||||
|
||||
return c.parent;
|
||||
}
|
||||
|
||||
private static CanvasGroup GetOrAddCanvasGroup(Transform root)
|
||||
{
|
||||
if (root == null) return null;
|
||||
CanvasGroup cg = root.GetComponent<CanvasGroup>();
|
||||
if (cg == null) cg = root.gameObject.AddComponent<CanvasGroup>();
|
||||
return cg;
|
||||
}
|
||||
|
||||
private static void SetCanvasAlpha(Transform root, float alpha)
|
||||
{
|
||||
if (root == null) return;
|
||||
CanvasGroup cg = GetOrAddCanvasGroup(root);
|
||||
if (cg == null) return;
|
||||
cg.alpha = Mathf.Clamp01(alpha);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Documentation text normalized.
|
||||
/// </summary>
|
||||
@@ -783,17 +1310,34 @@ public class settlementController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
if (heroSO != null && heroSO.ally_hero_HD_image != null)
|
||||
if (heroSO != null)
|
||||
{
|
||||
mvp_hero_hd_image.sprite = heroSO.ally_hero_HD_image;
|
||||
mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 1f);
|
||||
if (mvp_object != null) mvp_object.SetActive(true);
|
||||
|
||||
if (heroSO.ally_hero_HD_image != null && mvp_hero_hd_image != null)
|
||||
{
|
||||
mvp_hero_hd_image.sprite = heroSO.ally_hero_HD_image;
|
||||
mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 1f);
|
||||
}
|
||||
|
||||
// 更新 MVP 详情界面(名字、头像、分数)
|
||||
if (mvp_heroName != null) mvp_heroName.text = heroSO.ally_heroName;
|
||||
if (mvp_heroIcon != null) mvp_heroIcon.sprite = heroSO.ally_hero_squareProfile;
|
||||
if (mvp_score != null) mvp_score.text = max.ToString();
|
||||
|
||||
Debug.Log($"[SettlementController] MVP assigned from slot {topIndex+1} with score {max}: {heroSO.ally_heroName}");
|
||||
}
|
||||
else
|
||||
{
|
||||
mvp_hero_hd_image.sprite = null;
|
||||
mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 0f);
|
||||
if (mvp_object != null) mvp_object.SetActive(false);
|
||||
|
||||
if (mvp_hero_hd_image != null)
|
||||
{
|
||||
mvp_hero_hd_image.sprite = null;
|
||||
mvp_hero_hd_image.color = new Color(mvp_hero_hd_image.color.r, mvp_hero_hd_image.color.g, mvp_hero_hd_image.color.b, 0f);
|
||||
}
|
||||
Debug.LogWarning("[SettlementController] MVP SO or HD image not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user