备份,准备做双端
This commit is contained in:
@@ -98,6 +98,8 @@ public class settlementController : MonoBehaviour
|
||||
public Text earlyHitCount_Text;
|
||||
public Text lateHitCount_Text;
|
||||
public Text avgOffset_Text;
|
||||
[Tooltip("显示中位数偏移 + 校准建议的文本(可选)")]
|
||||
public Text offsetSuggestion_Text;
|
||||
|
||||
[Header("Inspector")]
|
||||
[Tooltip("Settlement background music clip used during the settlement flow.")]
|
||||
@@ -671,6 +673,8 @@ public class settlementController : MonoBehaviour
|
||||
avgOffset_Text.text = targetAvgOffsetMs.ToString("F2") + " ms";
|
||||
}
|
||||
|
||||
UpdateOffsetSuggestion(sm);
|
||||
|
||||
targetGameNoteRateText = gameNote_rate != null ? gameNote_rate.text : string.Empty;
|
||||
|
||||
TrySubmitArenaRoomScore();
|
||||
@@ -1259,82 +1263,169 @@ public class settlementController : MonoBehaviour
|
||||
// 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
|
||||
// Main settlement blocks enter together instead of waiting for each part in sequence.
|
||||
bool hasMainEntryAnimations = false;
|
||||
if (rightBottomRt != null)
|
||||
yield return StartCoroutine(AnimateAnchoredXAndFade(rightBottomRt, 1799f, 1677.75f, introMoveDuration, false));
|
||||
|
||||
// left/pics/quhuiImage flash
|
||||
{
|
||||
StartCoroutine(AnimateAnchoredXAndFade(rightBottomRt, 1799f, 1677.75f, introMoveDuration, false));
|
||||
hasMainEntryAnimations = true;
|
||||
}
|
||||
if (quhuiImageTr != null)
|
||||
yield return StartCoroutine(FlashInTransform(quhuiImageTr, introFlashCount, introFlashDuration));
|
||||
yield return StartCoroutine(FlashInTransform(thisSong_backPic != null ? thisSong_backPic.transform : null, 1, introStaticElementDuration));
|
||||
yield return StartCoroutine(FlashInTransform(songName_Text != null ? songName_Text.transform : null, 1, introStaticElementDuration));
|
||||
|
||||
// score_bottom + shenstars move in together
|
||||
Coroutine scoreBottomIn = null;
|
||||
Coroutine shenstarsIn = null;
|
||||
{
|
||||
StartCoroutine(FlashInTransform(quhuiImageTr, introFlashCount, introFlashDuration));
|
||||
hasMainEntryAnimations = true;
|
||||
}
|
||||
if (thisSong_backPic != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(thisSong_backPic.transform, 1, introStaticElementDuration));
|
||||
hasMainEntryAnimations = true;
|
||||
}
|
||||
if (songName_Text != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(songName_Text.transform, 1, introStaticElementDuration));
|
||||
hasMainEntryAnimations = true;
|
||||
}
|
||||
if (scoreBottomRt != null)
|
||||
scoreBottomIn = StartCoroutine(AnimateAnchoredXAndFade(scoreBottomRt, -1646.9f, 0f, introMoveDuration, true));
|
||||
{
|
||||
StartCoroutine(AnimateAnchoredXAndFade(scoreBottomRt, -1646.9f, 0f, introMoveDuration, true));
|
||||
hasMainEntryAnimations = 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
|
||||
{
|
||||
StartCoroutine(AnimateAnchoredXAndFade(shenstarsRt, -1602.1f, -437.5785f, introMoveDuration, true));
|
||||
hasMainEntryAnimations = true;
|
||||
}
|
||||
if (rewardRt != null)
|
||||
yield return StartCoroutine(AnimateAnchoredYAndFade(rewardRt, -494.3f, 0f, introMoveDuration));
|
||||
{
|
||||
StartCoroutine(AnimateAnchoredYAndFade(rewardRt, -494.3f, 0f, introMoveDuration));
|
||||
hasMainEntryAnimations = true;
|
||||
}
|
||||
if (hasMainEntryAnimations)
|
||||
yield return WaitRealtime(Mathf.Max(introMoveDuration, introFlashDuration, introStaticElementDuration) + 0.02f);
|
||||
|
||||
// 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, "F1"));
|
||||
bool hasScoreAnimations = false;
|
||||
if (pmScoreSum_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateIntText(pmScoreSum_Text, targetPmScore, introNumberDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (idolScoreSum_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateIntText(idolScoreSum_Text, targetIdolScore, introNumberDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (accuracy_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateFloatPercentText(accuracy_Text, targetAccuracyPercent, introNumberDuration, "F1"));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (finalScore_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateIntText(finalScore_Text, targetTotalScore, introNumberDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (thisLevel_currentPercentage_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateFloatPercentText(thisLevel_currentPercentage_Text, targetTotalPercent, introNumberDuration, "F2"));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (thisLevel_progressBar_Image != null)
|
||||
{
|
||||
StartCoroutine(AnimateImageFill(thisLevel_progressBar_Image, (float)targetTotalScore / Mathf.Max(1, _1000000), introBarFillDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (finalLevel_img != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(finalLevel_img.transform, 1, introStaticElementDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (personalRecord_Text != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(personalRecord_Text.transform, 1, introStaticElementDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (gameNote_rate != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(gameNote_rate.transform, 1, introStaticElementDuration));
|
||||
hasScoreAnimations = true;
|
||||
}
|
||||
if (hasScoreAnimations)
|
||||
yield return WaitRealtime(Mathf.Max(introNumberDuration, introBarFillDuration, introStaticElementDuration) + 0.02f);
|
||||
|
||||
// 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));
|
||||
yield return StartCoroutine(AnimateImageFill(thisLevel_progressBar_Image, (float)targetTotalScore / Mathf.Max(1, _1000000), introBarFillDuration));
|
||||
yield return StartCoroutine(FlashInTransform(finalLevel_img != null ? finalLevel_img.transform : null, 1, introStaticElementDuration));
|
||||
yield return StartCoroutine(FlashInTransform(personalRecord_Text != null ? personalRecord_Text.transform : null, 1, introStaticElementDuration));
|
||||
yield return StartCoroutine(FlashInTransform(gameNote_rate != null ? gameNote_rate.transform : null, 1, introStaticElementDuration));
|
||||
bool hasScoreFlashAnimations = false;
|
||||
if (finalScore_Text != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(finalScore_Text.transform, introFlashCount, introFlashDuration * 0.9f));
|
||||
hasScoreFlashAnimations = true;
|
||||
}
|
||||
if (thisLevel_currentPercentage_Text != null)
|
||||
{
|
||||
StartCoroutine(FlashInTransform(thisLevel_currentPercentage_Text.transform, introFlashCount, introFlashDuration * 0.9f));
|
||||
hasScoreFlashAnimations = true;
|
||||
}
|
||||
if (hasScoreFlashAnimations)
|
||||
yield return WaitRealtime(introFlashDuration * 0.9f + 0.02f);
|
||||
|
||||
// hitStatus/status groups: flash in and count up numbers
|
||||
// hitStatus/status groups: flash in and count up numbers together.
|
||||
int noteCountForBars = Mathf.Max(1, targetPerfectCount + targetGreatCount + targetGoodCount + targetMissCount);
|
||||
yield return StartCoroutine(PlayStatusGroupIn(perfectGroup, perfectHitCount_Text, targetPerfectCount, perfectHitPercent_Text, targetPerfectPercent, perfect_barFill_Image, (float)targetPerfectCount / noteCountForBars));
|
||||
yield return StartCoroutine(PlayStatusGroupIn(greatGroup, greatHitCount_Text, targetGreatCount, greatHitPercent_Text, targetGreatPercent, great_barFill_Image, (float)targetGreatCount / noteCountForBars));
|
||||
yield return StartCoroutine(PlayStatusGroupIn(goodGroup, goodHitCount_Text, targetGoodCount, goodHitPercent_Text, targetGoodPercent, good_barFill_Image, (float)targetGoodCount / noteCountForBars));
|
||||
yield return StartCoroutine(PlayStatusGroupIn(missGroup, missHitCount_Text, targetMissCount, missHitPercent_Text, targetMissPercent, miss_barFill_Image, (float)targetMissCount / noteCountForBars));
|
||||
bool hasStatusAnimations = false;
|
||||
StartCoroutine(PlayStatusGroupIn(perfectGroup, perfectHitCount_Text, targetPerfectCount, perfectHitPercent_Text, targetPerfectPercent, perfect_barFill_Image, (float)targetPerfectCount / noteCountForBars));
|
||||
hasStatusAnimations = true;
|
||||
StartCoroutine(PlayStatusGroupIn(greatGroup, greatHitCount_Text, targetGreatCount, greatHitPercent_Text, targetGreatPercent, great_barFill_Image, (float)targetGreatCount / noteCountForBars));
|
||||
hasStatusAnimations = true;
|
||||
StartCoroutine(PlayStatusGroupIn(goodGroup, goodHitCount_Text, targetGoodCount, goodHitPercent_Text, targetGoodPercent, good_barFill_Image, (float)targetGoodCount / noteCountForBars));
|
||||
hasStatusAnimations = true;
|
||||
StartCoroutine(PlayStatusGroupIn(missGroup, missHitCount_Text, targetMissCount, missHitPercent_Text, targetMissPercent, miss_barFill_Image, (float)targetMissCount / noteCountForBars));
|
||||
hasStatusAnimations = true;
|
||||
if (earlyHitCount_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateIntText(earlyHitCount_Text, targetEarlyCount, introNumberDuration * 0.7f));
|
||||
hasStatusAnimations = true;
|
||||
}
|
||||
if (lateHitCount_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateIntText(lateHitCount_Text, targetLateCount, introNumberDuration * 0.7f));
|
||||
hasStatusAnimations = true;
|
||||
}
|
||||
if (avgOffset_Text != null)
|
||||
{
|
||||
StartCoroutine(AnimateOffsetText(avgOffset_Text, targetAvgOffsetMs, introNumberDuration * 0.7f));
|
||||
hasStatusAnimations = true;
|
||||
}
|
||||
if (hasStatusAnimations)
|
||||
yield return WaitRealtime(Mathf.Max(introFlashDuration + introNumberDuration, introFlashDuration + introBarFillDuration, introNumberDuration * 0.7f) + 0.04f);
|
||||
|
||||
yield return StartCoroutine(AnimateIntText(earlyHitCount_Text, targetEarlyCount, introNumberDuration * 0.7f));
|
||||
yield return StartCoroutine(AnimateIntText(lateHitCount_Text, targetLateCount, introNumberDuration * 0.7f));
|
||||
yield return StartCoroutine(AnimateOffsetText(avgOffset_Text, targetAvgOffsetMs, introNumberDuration * 0.7f));
|
||||
|
||||
// ally cards enter before MASK.
|
||||
// Team, MVP and buttons enter together after the score/status area is visible.
|
||||
bool hasFinalEntryAnimations = false;
|
||||
if (hasAllyCardEntry)
|
||||
yield return StartCoroutine(settlementTeamLoader.PlayAllySlotEntryBeforeMask());
|
||||
|
||||
// heroDApic/MASK/lihui flash
|
||||
{
|
||||
StartCoroutine(settlementTeamLoader.PlayAllySlotEntryBeforeMask());
|
||||
hasFinalEntryAnimations = true;
|
||||
}
|
||||
if (lihuiTr != null)
|
||||
yield return StartCoroutine(FlashInTransform(lihuiTr, introFlashCount, introFlashDuration));
|
||||
|
||||
// MVP shows after MASK.
|
||||
{
|
||||
StartCoroutine(FlashInTransform(lihuiTr, introFlashCount, introFlashDuration));
|
||||
hasFinalEntryAnimations = true;
|
||||
}
|
||||
if (shouldAnimateMvp)
|
||||
yield return StartCoroutine(AnimateMvpEntryAfterMask(mvpTr, mvpBaseLocalPos));
|
||||
|
||||
// Fallback for scenes still using container slide-in instead of per-slot tween.
|
||||
{
|
||||
StartCoroutine(AnimateMvpEntryAfterMask(mvpTr, mvpBaseLocalPos));
|
||||
hasFinalEntryAnimations = true;
|
||||
}
|
||||
if (!hasAllyCardEntry && teamDisplayingRt != null)
|
||||
yield return StartCoroutine(AnimateAnchoredXAndFade(teamDisplayingRt, 2265f, 1490.09f, introMoveDuration, true));
|
||||
|
||||
// right/buttons: each button flashes in sequence
|
||||
{
|
||||
StartCoroutine(AnimateAnchoredXAndFade(teamDisplayingRt, 2265f, 1490.09f, introMoveDuration, true));
|
||||
hasFinalEntryAnimations = true;
|
||||
}
|
||||
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);
|
||||
StartCoroutine(FlashInTransform(btn, introFlashCount, introFlashDuration));
|
||||
hasFinalEntryAnimations = true;
|
||||
}
|
||||
if (hasFinalEntryAnimations)
|
||||
yield return WaitRealtime(Mathf.Max(introMoveDuration, introFlashDuration, introMvpEnterDuration) + Mathf.Max(0.1f, introStepGap));
|
||||
|
||||
ApplySettlementIntroFinalState();
|
||||
settlementCanvasGroup.alpha = 1f;
|
||||
@@ -1462,6 +1553,7 @@ public class settlementController : MonoBehaviour
|
||||
if (earlyHitCount_Text != null) { earlyHitCount_Text.text = targetEarlyCount.ToString(); SetCanvasAlpha(earlyHitCount_Text.transform, 1f); }
|
||||
if (lateHitCount_Text != null) { lateHitCount_Text.text = targetLateCount.ToString(); SetCanvasAlpha(lateHitCount_Text.transform, 1f); }
|
||||
if (avgOffset_Text != null) { avgOffset_Text.text = targetAvgOffsetMs.ToString("F2") + " ms"; SetCanvasAlpha(avgOffset_Text.transform, 1f); }
|
||||
if (offsetSuggestion_Text != null) SetCanvasAlpha(offsetSuggestion_Text.transform, 1f);
|
||||
|
||||
if (mvpTr != null && mvpTr.gameObject.activeInHierarchy)
|
||||
{
|
||||
@@ -1780,6 +1872,37 @@ public class settlementController : MonoBehaviour
|
||||
target.text = toValue.ToString(format) + "%";
|
||||
}
|
||||
|
||||
// 结算页校准建议:用中位数(抗离群点)而非平均值判断玩家系统性偏差,
|
||||
// 并给出 UserGlobalDelaySeconds 的建议值(仅显示,不自动写入,保持业务逻辑等效)。
|
||||
private void UpdateOffsetSuggestion(ScoreManager sm)
|
||||
{
|
||||
if (offsetSuggestion_Text == null) return;
|
||||
if (sm == null || sm.offsetCount <= 0)
|
||||
{
|
||||
offsetSuggestion_Text.text = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
// 正数 = 偏早(Early),负数 = 偏晚(Late)。见 ScoreManager.RecordOffset。
|
||||
float medianMs = sm.GetMedianOffsetMs();
|
||||
float currentDelay = PlayerPrefs.GetFloat("UserGlobalDelaySeconds", 0f);
|
||||
|
||||
// 偏早 -> 音符应更早到达判定线 -> 减小延迟;偏晚 -> 增大延迟。
|
||||
float suggestedDelay = currentDelay - medianMs / 1000f;
|
||||
|
||||
// 小于阈值视为已对齐,避免噪声导致反复微调。
|
||||
const float alignedThresholdMs = 8f;
|
||||
if (Mathf.Abs(medianMs) < alignedThresholdMs)
|
||||
{
|
||||
offsetSuggestion_Text.text = $"中位偏移 {medianMs:F1} ms · 时机良好";
|
||||
return;
|
||||
}
|
||||
|
||||
string dir = medianMs > 0f ? "偏早" : "偏晚";
|
||||
offsetSuggestion_Text.text =
|
||||
$"中位偏移 {medianMs:F1} ms({dir})· 建议全局延迟 {currentDelay * 1000f:F0} → {suggestedDelay * 1000f:F0} ms";
|
||||
}
|
||||
|
||||
private IEnumerator AnimateOffsetText(Text target, float toValue, float duration)
|
||||
{
|
||||
if (target == null) yield break;
|
||||
|
||||
Reference in New Issue
Block a user