修了很多bug和增加功能,优化不少问题

This commit is contained in:
FloatGaming
2026-02-28 06:59:22 +08:00
parent 508a40bba0
commit e11cc1da7a
345 changed files with 173803 additions and 69332 deletions
@@ -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");
}
}
}