ui基本完毕,修了一大把的bug
This commit is contained in:
@@ -813,7 +813,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 312.3, y: 0}
|
||||
m_AnchoredPosition: {x: 312.3, y: 4}
|
||||
m_SizeDelta: {x: 200, y: 200}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &3397289974922444082
|
||||
@@ -1039,7 +1039,7 @@ RectTransform:
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: -44.5}
|
||||
m_AnchoredPosition: {x: 0, y: -47}
|
||||
m_SizeDelta: {x: 116.5648, y: 30}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4000680467974343741
|
||||
|
||||
@@ -12,6 +12,8 @@ using UnityEngine.SceneManagement;
|
||||
public class SongDetailsUI : MonoBehaviour
|
||||
{
|
||||
private const string BackSceneName = "selectYourSongFirst";
|
||||
private const float ProgressNumberMinX = -370f;
|
||||
private const float ProgressNumberMaxX = 362f;
|
||||
|
||||
[Header("this_song_SO")]
|
||||
public ScriptableObject this_song_SO;
|
||||
@@ -53,6 +55,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
public Image progressImage;
|
||||
public GameObject progressNumberObject;
|
||||
public TextMeshProUGUI progressPercentText;
|
||||
public Text progressPercentText2;
|
||||
|
||||
public Image rankLevel_img;
|
||||
public RankConfig rc;
|
||||
@@ -83,6 +86,21 @@ public class SongDetailsUI : MonoBehaviour
|
||||
private UnityAction difficultyHdAction;
|
||||
private UnityAction difficultyInAction;
|
||||
private UnityAction difficultyImAction;
|
||||
private RectTransform progressImageRect;
|
||||
private RectTransform progressNumberRect;
|
||||
private float progressImageFullWidth = -1f;
|
||||
private float progressImageLeftEdgeX;
|
||||
private float progressImageScaleX = 1f;
|
||||
private bool progressUsesMaskWidthMode;
|
||||
[SerializeField] private float progressTweenDuration = 0.35f;
|
||||
[SerializeField] private Ease progressTweenEase = Ease.OutCubic;
|
||||
|
||||
private Tween progressBarTween;
|
||||
private Tween progressNumberTween;
|
||||
private Tween progressTextTween;
|
||||
private float currentProgressVisual;
|
||||
private float currentProgressTextValue;
|
||||
private bool hasStarted;
|
||||
|
||||
private SongData ResolveCurrentSong()
|
||||
{
|
||||
@@ -109,6 +127,7 @@ public class SongDetailsUI : MonoBehaviour
|
||||
private void OnEnable()
|
||||
{
|
||||
currentSong = ResolveCurrentSong();
|
||||
CacheProgressUiGeometry(forceRefresh: false);
|
||||
|
||||
ArenaRoomService roomService = ArenaRoomService.Instance;
|
||||
if (roomService != null)
|
||||
@@ -143,7 +162,12 @@ public class SongDetailsUI : MonoBehaviour
|
||||
|
||||
BindDifficultyButtons();
|
||||
PrepareDifficultyButtons();
|
||||
SyncSongDisplay(true);
|
||||
|
||||
if (hasStarted)
|
||||
{
|
||||
ResetProgressVisualState();
|
||||
SyncSongDisplay(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
@@ -153,6 +177,8 @@ public class SongDetailsUI : MonoBehaviour
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
KillProgressTweens();
|
||||
|
||||
ArenaRoomService roomService = ArenaRoomService.Instance;
|
||||
if (roomService != null)
|
||||
{
|
||||
@@ -328,9 +354,12 @@ public class SongDetailsUI : MonoBehaviour
|
||||
|
||||
void Start()
|
||||
{
|
||||
hasStarted = true;
|
||||
picDetail.SetActive(false);
|
||||
CacheProgressUiGeometry(forceRefresh: false);
|
||||
BindDifficultyButtons();
|
||||
PrepareDifficultyButtons();
|
||||
ResetProgressVisualState();
|
||||
SyncSongDisplay(true);
|
||||
}
|
||||
|
||||
@@ -757,9 +786,6 @@ public class SongDetailsUI : MonoBehaviour
|
||||
if (difficultyIdText != null)
|
||||
difficultyIdText.text = entry.difficultyLEVEL.ToString("0.0");
|
||||
|
||||
if (progressImage != null)
|
||||
progressImage.fillAmount = entry.levelProgressForThisDifficulty;
|
||||
|
||||
UpdateProgressUI(entry.levelProgressForThisDifficulty);
|
||||
|
||||
// read values from SongData (SO)
|
||||
@@ -807,8 +833,6 @@ public class SongDetailsUI : MonoBehaviour
|
||||
difficultyText.text = difficulty.ToString();
|
||||
if (difficultyIdText != null)
|
||||
difficultyIdText.text = difficulty.ToString();
|
||||
if (progressImage != null)
|
||||
progressImage.fillAmount = 0f;
|
||||
UpdateProgressUI(0f);
|
||||
if (difficultyDesciptionText != null)
|
||||
difficultyDesciptionText.text = "";
|
||||
@@ -827,33 +851,169 @@ public class SongDetailsUI : MonoBehaviour
|
||||
|
||||
private void UpdateProgressUI(float progress)
|
||||
{
|
||||
// 1. Update Text (can exceed 100%)
|
||||
if (progressPercentText != null)
|
||||
{
|
||||
progressPercentText.text = Mathf.FloorToInt(progress * 100f).ToString() + "%";
|
||||
}
|
||||
CacheProgressUiGeometry(forceRefresh: false);
|
||||
|
||||
// 2. Clamp for visual elements (fillAmount and Object position)
|
||||
float clampedProgress = Mathf.Clamp01(progress);
|
||||
|
||||
// Update fillAmount again just in case, though handled in caller
|
||||
if (progressImage != null)
|
||||
AnimateProgressVisual(clampedProgress);
|
||||
AnimateProgressText(progress);
|
||||
}
|
||||
|
||||
private void CacheProgressUiGeometry(bool forceRefresh)
|
||||
{
|
||||
if (progressImage != null && (forceRefresh || progressImageRect == null))
|
||||
{
|
||||
progressImage.fillAmount = clampedProgress;
|
||||
progressImageRect = progressImage.rectTransform;
|
||||
}
|
||||
|
||||
// 3. Update Object Position (Linear transformation: 0% -> -785, 100% -> -195)
|
||||
if (progressNumberObject != null)
|
||||
if (progressNumberObject != null && (forceRefresh || progressNumberRect == null))
|
||||
{
|
||||
RectTransform rect = progressNumberObject.GetComponent<RectTransform>();
|
||||
if (rect != null)
|
||||
{
|
||||
float targetX = Mathf.Lerp(-785f, -195f, clampedProgress);
|
||||
Vector2 anchoredPos = rect.anchoredPosition;
|
||||
anchoredPos.x = targetX;
|
||||
rect.anchoredPosition = anchoredPos;
|
||||
}
|
||||
progressNumberRect = progressNumberObject.GetComponent<RectTransform>();
|
||||
}
|
||||
|
||||
if (progressImageRect == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (forceRefresh || progressImageFullWidth <= 0f)
|
||||
{
|
||||
progressImageFullWidth = progressImageRect.sizeDelta.x;
|
||||
progressImageScaleX = Mathf.Abs(progressImageRect.localScale.x);
|
||||
progressImageLeftEdgeX = progressImageRect.anchoredPosition.x - (progressImageFullWidth * progressImageScaleX * 0.5f);
|
||||
}
|
||||
|
||||
progressUsesMaskWidthMode = progressImage != null
|
||||
&& progressImage.GetComponent<Mask>() != null
|
||||
&& progressImage.transform.childCount > 0;
|
||||
}
|
||||
|
||||
private void ResetProgressVisualState()
|
||||
{
|
||||
CacheProgressUiGeometry(forceRefresh: false);
|
||||
KillProgressTweens();
|
||||
currentProgressVisual = 0f;
|
||||
currentProgressTextValue = 0f;
|
||||
ApplyProgressBarVisual(0f);
|
||||
ApplyProgressNumberPosition(0f);
|
||||
ApplyProgressTextValue(0f);
|
||||
}
|
||||
|
||||
private void KillProgressTweens()
|
||||
{
|
||||
if (progressBarTween != null && progressBarTween.IsActive())
|
||||
{
|
||||
progressBarTween.Kill();
|
||||
}
|
||||
|
||||
if (progressNumberTween != null && progressNumberTween.IsActive())
|
||||
{
|
||||
progressNumberTween.Kill();
|
||||
}
|
||||
|
||||
if (progressTextTween != null && progressTextTween.IsActive())
|
||||
{
|
||||
progressTextTween.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
private void AnimateProgressVisual(float clampedProgress)
|
||||
{
|
||||
KillProgressTweens();
|
||||
|
||||
progressBarTween = DOTween.To(
|
||||
() => currentProgressVisual,
|
||||
value =>
|
||||
{
|
||||
currentProgressVisual = value;
|
||||
ApplyProgressBarVisual(value);
|
||||
},
|
||||
clampedProgress,
|
||||
progressTweenDuration)
|
||||
.SetEase(progressTweenEase)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
currentProgressVisual = clampedProgress;
|
||||
ApplyProgressBarVisual(clampedProgress);
|
||||
});
|
||||
|
||||
if (progressNumberRect != null)
|
||||
{
|
||||
progressNumberTween = progressNumberRect
|
||||
.DOAnchorPosX(Mathf.Lerp(ProgressNumberMinX, ProgressNumberMaxX, clampedProgress), progressTweenDuration)
|
||||
.SetEase(progressTweenEase);
|
||||
}
|
||||
}
|
||||
|
||||
private void AnimateProgressText(float targetProgressValue)
|
||||
{
|
||||
float safeTargetValue = Mathf.Max(0f, targetProgressValue);
|
||||
|
||||
progressTextTween = DOTween.To(
|
||||
() => currentProgressTextValue,
|
||||
value =>
|
||||
{
|
||||
currentProgressTextValue = value;
|
||||
ApplyProgressTextValue(value);
|
||||
},
|
||||
safeTargetValue,
|
||||
progressTweenDuration)
|
||||
.SetEase(progressTweenEase)
|
||||
.OnComplete(() =>
|
||||
{
|
||||
currentProgressTextValue = safeTargetValue;
|
||||
ApplyProgressTextValue(safeTargetValue);
|
||||
});
|
||||
}
|
||||
|
||||
private void ApplyProgressNumberPosition(float clampedProgress)
|
||||
{
|
||||
if (progressNumberRect == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 anchoredPos = progressNumberRect.anchoredPosition;
|
||||
anchoredPos.x = Mathf.Lerp(ProgressNumberMinX, ProgressNumberMaxX, clampedProgress);
|
||||
progressNumberRect.anchoredPosition = anchoredPos;
|
||||
}
|
||||
|
||||
private void ApplyProgressTextValue(float progressValue)
|
||||
{
|
||||
int percentValue = Mathf.FloorToInt(Mathf.Max(0f, progressValue) * 100f);
|
||||
string textValue = percentValue.ToString() + "%";
|
||||
|
||||
if (progressPercentText != null)
|
||||
{
|
||||
progressPercentText.text = textValue;
|
||||
}
|
||||
|
||||
if (progressPercentText2 != null)
|
||||
{
|
||||
progressPercentText2.text = textValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyProgressBarVisual(float clampedProgress)
|
||||
{
|
||||
if (progressImage == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressUsesMaskWidthMode && progressImageRect != null && progressImageFullWidth > 0f)
|
||||
{
|
||||
float newWidth = progressImageFullWidth * clampedProgress;
|
||||
progressImageRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, newWidth);
|
||||
|
||||
Vector2 anchoredPos = progressImageRect.anchoredPosition;
|
||||
anchoredPos.x = progressImageLeftEdgeX + (newWidth * progressImageScaleX * 0.5f);
|
||||
progressImageRect.anchoredPosition = anchoredPos;
|
||||
return;
|
||||
}
|
||||
|
||||
progressImage.fillAmount = clampedProgress;
|
||||
}
|
||||
|
||||
private ChartFileEntry GetChartEntry(int difficulty)
|
||||
|
||||
@@ -42,6 +42,11 @@ public class dlcButton : MonoBehaviour, IPointerEnterHandler, IPointerExitHandle
|
||||
private static List<dlcButton> allButtons = new List<dlcButton>();
|
||||
private static int selectedIndex = -1;
|
||||
private const string PlayerPrefsKey = "dlc_selected_index";
|
||||
private const string SelectedDlcIdPrefsKey = "dlc_selected_id";
|
||||
// When true, Select() applies visual selection but does NOT persist to
|
||||
// PlayerPrefs. Used during restore so re-selecting (including fallback)
|
||||
// never overwrites the user's genuine last choice.
|
||||
public static bool SuppressSelectionPersist = false;
|
||||
|
||||
private int instanceIndex = -1;
|
||||
private Coroutine hoverCoroutine;
|
||||
@@ -202,11 +207,38 @@ public class dlcButton : MonoBehaviour, IPointerEnterHandler, IPointerExitHandle
|
||||
{
|
||||
ApplySelectionToAll(instanceIndex);
|
||||
selectedIndex = instanceIndex;
|
||||
if (SuppressSelectionPersist)
|
||||
{
|
||||
if (VerboseLogs) Debug.Log($"dlcButton.Select: '{gameObject.name}' selected (index {instanceIndex}) without persisting (restore in progress)");
|
||||
return;
|
||||
}
|
||||
PlayerPrefs.SetInt(PlayerPrefsKey, selectedIndex);
|
||||
int dlcId = ResolveOwnDlcId();
|
||||
if (dlcId > 0)
|
||||
{
|
||||
PlayerPrefs.SetInt(SelectedDlcIdPrefsKey, dlcId);
|
||||
}
|
||||
PlayerPrefs.Save();
|
||||
if (VerboseLogs) Debug.Log($"dlcButton.Select: '{gameObject.name}' selected (index {instanceIndex}) and saved to PlayerPrefs");
|
||||
}
|
||||
|
||||
public static string SelectedDlcIdKey => SelectedDlcIdPrefsKey;
|
||||
|
||||
public int ResolveOwnDlcId()
|
||||
{
|
||||
if (dlcDataSO != null && dlcDataSO.dlcID > 0)
|
||||
{
|
||||
return dlcDataSO.dlcID;
|
||||
}
|
||||
|
||||
if (dlcID != null && int.TryParse(dlcID.text, out int parsedId))
|
||||
{
|
||||
return parsedId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void ApplySelectionToAll(int selectIdx)
|
||||
{
|
||||
for (int i = 0; i < allButtons.Count; i++)
|
||||
|
||||
@@ -7,7 +7,7 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
{
|
||||
private static readonly bool VerboseLogs = false;
|
||||
private const string RuntimeInstalledColumnName = "Installed DLC";
|
||||
private const int DefaultFallbackDlcId = 66001;
|
||||
private const int DefaultFallbackDlcId = 66002;
|
||||
private const string DlcSelectedIndexPrefsKey = "dlc_selected_index";
|
||||
[System.Serializable]
|
||||
public class Column
|
||||
@@ -145,7 +145,19 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
}
|
||||
|
||||
SongSelectUI.ForceFirstSongOnNextRestore = forceFirstSong;
|
||||
targetButton.OnButtonClicked_ShowSongs();
|
||||
// Restore is read-only: applying the selection here (including the
|
||||
// fallback DLC) must not overwrite the user's genuine last choice.
|
||||
// Select() runs synchronously inside OnButtonClicked_ShowSongs, so the
|
||||
// suppression window only needs to span this call.
|
||||
dlcButton.SuppressSelectionPersist = true;
|
||||
try
|
||||
{
|
||||
targetButton.OnButtonClicked_ShowSongs();
|
||||
}
|
||||
finally
|
||||
{
|
||||
dlcButton.SuppressSelectionPersist = false;
|
||||
}
|
||||
|
||||
if (VerboseLogs)
|
||||
{
|
||||
@@ -164,6 +176,20 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prefer restoring by the saved DLC id, which is stable across
|
||||
// session-to-session ordering changes (e.g. runtime-installed DLCs).
|
||||
int savedDlcId = PlayerPrefs.GetInt(dlcButton.SelectedDlcIdKey, 0);
|
||||
if (savedDlcId > 0)
|
||||
{
|
||||
dlcButton savedById = FindButtonByDlcId(allButtons, savedDlcId);
|
||||
if (savedById != null && HasSavedSongSelection(savedById))
|
||||
{
|
||||
forceFirstSong = false;
|
||||
return savedById;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to the saved positional index for legacy saves.
|
||||
int savedIndex = PlayerPrefs.GetInt(DlcSelectedIndexPrefsKey, 0);
|
||||
if (savedIndex >= 0 && savedIndex < allButtons.Length)
|
||||
{
|
||||
@@ -221,14 +247,12 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only the songID key proves a genuine user click: SongButton.OnSongButtonClick
|
||||
// always writes it alongside the index. The index key alone is unreliable because
|
||||
// RestoreSelectedSongForCurrentDlc auto-writes it to 0 on mere display, so relying
|
||||
// on it would treat any browsed-but-not-selected DLC as "user selected".
|
||||
string songIdKey = SongButton.BuildSongSelectedIdPrefsKey(dlcKey);
|
||||
if (PlayerPrefs.HasKey(songIdKey) && PlayerPrefs.GetInt(songIdKey, 0) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
string songIndexKey = SongButton.BuildSongSelectedIndexPrefsKey(dlcKey);
|
||||
return PlayerPrefs.HasKey(songIndexKey);
|
||||
return PlayerPrefs.HasKey(songIdKey) && PlayerPrefs.GetInt(songIdKey, 0) > 0;
|
||||
}
|
||||
|
||||
private string BuildDlcKey(dlcButton button)
|
||||
@@ -244,22 +268,8 @@ public class loadDlcListPrefab : MonoBehaviour
|
||||
|
||||
private int GetDlcId(dlcButton button)
|
||||
{
|
||||
if (button == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (button.dlcDataSO != null && button.dlcDataSO.dlcID > 0)
|
||||
{
|
||||
return button.dlcDataSO.dlcID;
|
||||
}
|
||||
|
||||
if (button.dlcID != null && int.TryParse(button.dlcID.text, out int parsedId))
|
||||
{
|
||||
return parsedId;
|
||||
}
|
||||
|
||||
return 0;
|
||||
// Delegate to the button's own resolver to keep id resolution in one place.
|
||||
return button != null ? button.ResolveOwnDlcId() : 0;
|
||||
}
|
||||
|
||||
private List<Column> BuildDisplayColumns()
|
||||
|
||||
Reference in New Issue
Block a user