429 lines
15 KiB
C#
429 lines
15 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
public class dlcButton : MonoBehaviour
|
|
{
|
|
[Header("基本套件")]
|
|
public Image dlc_profileImgae;
|
|
public Text dlcName;
|
|
public Text dlcID;
|
|
public Text dlcProducer;
|
|
public Text dlcPubliushDate;
|
|
public Image dlc_nowSelectedImageBoarder;
|
|
[Header("介绍")]
|
|
public Text dlcDescription;
|
|
|
|
[Header("DLC SO (可在 Inspector 指定)")]
|
|
public dlcData dlcDataSO;
|
|
|
|
[Header("DLC 内容 (运行时会由脚本填充)")]
|
|
public List<SongData> dlcContent = new List<SongData>();
|
|
|
|
[Header("搜索路径 (编辑器/运行时)")]
|
|
public string editorSearchFolder = "Assets/Resources/dlc_dlcIndex";
|
|
public string runtimeResourcesFolder = "Resources/dlc_dlcIndex";
|
|
|
|
private Button _btn;
|
|
|
|
// Selection management (shared across all dlcButton instances)
|
|
private static List<dlcButton> allButtons = new List<dlcButton>();
|
|
private static int selectedIndex = -1;
|
|
private const string PlayerPrefsKey = "dlc_selected_index";
|
|
|
|
// instance index in registration order
|
|
private int instanceIndex = -1;
|
|
|
|
void Awake()
|
|
{
|
|
_btn = GetComponent<Button>();
|
|
if (_btn != null)
|
|
{
|
|
_btn.onClick.RemoveAllListeners();
|
|
_btn.onClick.AddListener(OnButtonClicked_ShowSongs);
|
|
}
|
|
|
|
// register this button for centralized selection handling
|
|
RegisterInstance();
|
|
|
|
// ensure border initial state
|
|
if (dlc_nowSelectedImageBoarder != null)
|
|
{
|
|
var c = dlc_nowSelectedImageBoarder.color;
|
|
c.a = 0f;
|
|
dlc_nowSelectedImageBoarder.color = c;
|
|
dlc_nowSelectedImageBoarder.gameObject.SetActive(false);
|
|
}
|
|
|
|
Debug.Log($"dlcButton.Awake: registered '{gameObject.name}' as index {instanceIndex}");
|
|
}
|
|
|
|
private void RegisterInstance()
|
|
{
|
|
if (!allButtons.Contains(this))
|
|
{
|
|
instanceIndex = allButtons.Count;
|
|
allButtons.Add(this);
|
|
|
|
// If no selectedIndex loaded yet, load from PlayerPrefs default 0
|
|
if (selectedIndex == -1)
|
|
{
|
|
selectedIndex = PlayerPrefs.GetInt(PlayerPrefsKey, 0);
|
|
}
|
|
|
|
// If this instance matches the persisted selection, apply selection
|
|
if (instanceIndex == selectedIndex)
|
|
{
|
|
// ensure other buttons are deselected
|
|
ApplySelectionToAll(instanceIndex);
|
|
}
|
|
else
|
|
{
|
|
// ensure unselected visual
|
|
SetSelectedVisual(false, immediate: true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UnregisterInstance()
|
|
{
|
|
if (allButtons.Contains(this))
|
|
{
|
|
int idx = allButtons.IndexOf(this);
|
|
allButtons.Remove(this);
|
|
|
|
// if removed index was before selectedIndex, adjust saved index
|
|
if (idx >= 0 && idx < instanceIndex)
|
|
{
|
|
// nothing: instanceIndex only used at registration time
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
UnregisterInstance();
|
|
}
|
|
|
|
// Ensure only one button is selected and persist the index
|
|
public void Select()
|
|
{
|
|
ApplySelectionToAll(instanceIndex);
|
|
selectedIndex = instanceIndex;
|
|
PlayerPrefs.SetInt(PlayerPrefsKey, selectedIndex);
|
|
PlayerPrefs.Save();
|
|
Debug.Log($"dlcButton.Select: '{gameObject.name}' selected (index {instanceIndex}) and saved to PlayerPrefs");
|
|
}
|
|
|
|
private static void ApplySelectionToAll(int selectIdx)
|
|
{
|
|
for (int i = 0; i < allButtons.Count; i++)
|
|
{
|
|
var btn = allButtons[i];
|
|
if (btn == null) continue;
|
|
bool shouldSelect = (i == selectIdx);
|
|
btn.SetSelectedVisual(shouldSelect);
|
|
}
|
|
}
|
|
|
|
// fades the border in or out; if immediate, set without coroutine
|
|
private Coroutine borderFadeCoroutine = null;
|
|
private void SetSelectedVisual(bool selected, bool immediate = false)
|
|
{
|
|
if (dlc_nowSelectedImageBoarder == null) return;
|
|
|
|
if (borderFadeCoroutine != null)
|
|
{
|
|
StopCoroutine(borderFadeCoroutine);
|
|
borderFadeCoroutine = null;
|
|
}
|
|
|
|
if (immediate)
|
|
{
|
|
dlc_nowSelectedImageBoarder.gameObject.SetActive(selected);
|
|
var c = dlc_nowSelectedImageBoarder.color;
|
|
c.a = selected ? 1f : 0f;
|
|
dlc_nowSelectedImageBoarder.color = c;
|
|
}
|
|
else
|
|
{
|
|
dlc_nowSelectedImageBoarder.gameObject.SetActive(true);
|
|
borderFadeCoroutine = StartCoroutine(FadeBorder(dlc_nowSelectedImageBoarder, selected ? 1f : 0f, 0.25f));
|
|
}
|
|
}
|
|
|
|
private IEnumerator<YieldInstruction> FadeBorder(Image img, float targetAlpha, float duration)
|
|
{
|
|
if (img == null) yield break;
|
|
float startAlpha = img.color.a;
|
|
float t = 0f;
|
|
// if fading out, ensure we start from current alpha
|
|
while (t < duration)
|
|
{
|
|
t += Time.unscaledDeltaTime;
|
|
float a = Mathf.Lerp(startAlpha, targetAlpha, Mathf.Clamp01(t / duration));
|
|
var c = img.color;
|
|
c.a = a;
|
|
img.color = c;
|
|
yield return null;
|
|
}
|
|
var final = img.color;
|
|
final.a = targetAlpha;
|
|
img.color = final;
|
|
if (Mathf.Approximately(targetAlpha, 0f))
|
|
{
|
|
img.gameObject.SetActive(false);
|
|
}
|
|
borderFadeCoroutine = null;
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
// Do not auto-resolve here anymore. Resolution will be triggered externally after instantiation of all buttons.
|
|
}
|
|
|
|
public void OnButtonClicked_ShowSongs()
|
|
{
|
|
// set current DLC key for SongSelectUI to allow per-DLC song selection restore
|
|
string dlcKey = null;
|
|
if (dlcDataSO != null)
|
|
{
|
|
dlcKey = "dlc_" + dlcDataSO.dlcID;
|
|
}
|
|
else if (dlcID != null && int.TryParse(dlcID.text, out int parsedId))
|
|
{
|
|
dlcKey = "dlc_" + parsedId;
|
|
}
|
|
else
|
|
{
|
|
dlcKey = "dlc_instance_" + instanceIndex;
|
|
}
|
|
|
|
// assign to SongSelectUI static key
|
|
try { SongSelectUI.CurrentDlcKey = dlcKey; } catch { }
|
|
|
|
// mark selection first
|
|
Select();
|
|
|
|
var s = GameObject.FindObjectOfType<SongSelectUI>();
|
|
if (s != null)
|
|
{
|
|
s.DisplaySongsFromList(dlcContent);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("dlcButton: SongSelectUI not found in scene to display songs");
|
|
}
|
|
}
|
|
|
|
// Try to find a dlcData SO by id and apply its songList to dlcContent
|
|
public void LoadDlcDataById(int id)
|
|
{
|
|
Debug.Log($"dlcButton.LoadDlcDataById: searching for id={id} (editorFolder='{editorSearchFolder}', runtimeFolder='{runtimeResourcesFolder}') on '{gameObject.name}'");
|
|
#if UNITY_EDITOR
|
|
// Editor search: search recursively for asset files under editorSearchFolder
|
|
if (!string.IsNullOrEmpty(editorSearchFolder) && Directory.Exists(editorSearchFolder))
|
|
{
|
|
try
|
|
{
|
|
var files = Directory.GetFiles(editorSearchFolder, "*.asset", SearchOption.AllDirectories);
|
|
Debug.Log($"dlcButton.LoadDlcDataById: found {files.Length} asset files under {editorSearchFolder}");
|
|
foreach (var path in files)
|
|
{
|
|
string fileName = Path.GetFileNameWithoutExtension(path);
|
|
if (fileName.StartsWith(id.ToString()))
|
|
{
|
|
string assetPath = path.Replace("\\", "/");
|
|
Debug.Log($"dlcButton.LoadDlcDataById: candidate assetPath={assetPath}");
|
|
var so = UnityEditor.AssetDatabase.LoadAssetAtPath<dlcData>(assetPath);
|
|
if (so != null)
|
|
{
|
|
Debug.Log($"dlcButton.LoadDlcDataById: loaded dlcData SO '{so.name}' for id={id}");
|
|
dlcDataSO = so;
|
|
ApplyDlcData(so);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogWarning("dlcButton LoadDlcDataById (Editor) failed: " + e.Message);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.Log($"dlcButton.LoadDlcDataById: editorSearchFolder '{editorSearchFolder}' does not exist or not set");
|
|
}
|
|
#endif
|
|
// Runtime: search Resources (load all dlcData then filter by name prefix)
|
|
try
|
|
{
|
|
var arr = Resources.LoadAll<dlcData>(runtimeResourcesFolder);
|
|
Debug.Log($"dlcButton.LoadDlcDataById: Resources.LoadAll('{runtimeResourcesFolder}') returned {(arr != null ? arr.Length : 0)} items");
|
|
if (arr != null)
|
|
{
|
|
foreach (var so in arr)
|
|
{
|
|
if (so == null) continue;
|
|
if (so.name.StartsWith(id.ToString()))
|
|
{
|
|
Debug.Log($"dlcButton.LoadDlcDataById: found runtime dlcData '{so.name}' for id={id}");
|
|
dlcDataSO = so;
|
|
ApplyDlcData(so);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogWarning($"dlcButton.LoadDlcDataById: Resources search failed: {e.Message}");
|
|
}
|
|
|
|
// fallback: search all Resources
|
|
var all = Resources.LoadAll<dlcData>("");
|
|
Debug.Log("dlcButton.LoadDlcDataById: Resources.LoadAll(\"\") returned " + (all != null ? all.Length : 0) + " items");
|
|
if (all != null)
|
|
{
|
|
foreach (var so in all)
|
|
{
|
|
if (so == null) continue;
|
|
if (so.name.StartsWith(id.ToString()))
|
|
{
|
|
Debug.Log($"dlcButton.LoadDlcDataById: found fallback dlcData '{so.name}' for id={id}");
|
|
dlcDataSO = so;
|
|
ApplyDlcData(so);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if no dlcData found, keep dlcContent empty but attempt old behavior: search SongData by id prefix
|
|
Debug.Log($"dlcButton.LoadDlcDataById: no dlcData found for id={id}, falling back to song prefix search");
|
|
LoadContentBySongIdPrefix(id);
|
|
}
|
|
|
|
// Populate dlcContent from dlcData SO and update UI texts/images
|
|
private void ApplyDlcData(dlcData so)
|
|
{
|
|
if (so == null) return;
|
|
dlcContent = new List<SongData>();
|
|
if (so.songList != null)
|
|
{
|
|
foreach (var s in so.songList)
|
|
if (s != null) dlcContent.Add(s);
|
|
}
|
|
|
|
if (dlcName != null) dlcName.text = so.dlcName ?? dlcName.text;
|
|
if (dlcProducer != null) dlcProducer.text = so.dlcProducer ?? dlcProducer.text;
|
|
if (dlcDescription != null) dlcDescription.text = so.dlcDescription ?? dlcDescription.text;
|
|
if (dlcPubliushDate != null) dlcPubliushDate.text = so.dlcPublishDate ?? dlcPubliushDate.text;
|
|
if (dlc_profileImgae != null)
|
|
{
|
|
dlc_profileImgae.sprite = so.dlc_image;
|
|
dlc_profileImgae.enabled = so.dlc_image != null;
|
|
}
|
|
|
|
Debug.Log($"dlcButton.ApplyDlcData: applied dlcData '{so.name}' with {dlcContent.Count} songs to button '{gameObject.name}'");
|
|
}
|
|
|
|
// Legacy: if no dlcData SO, search SongData assets by id prefix and fill dlcContent
|
|
public void LoadContentBySongIdPrefix(int id)
|
|
{
|
|
dlcContent.Clear();
|
|
#if UNITY_EDITOR
|
|
if (!string.IsNullOrEmpty(editorSearchFolder) && Directory.Exists(editorSearchFolder))
|
|
{
|
|
try
|
|
{
|
|
var files = Directory.GetFiles(editorSearchFolder, "*.asset", SearchOption.AllDirectories);
|
|
foreach (var path in files)
|
|
{
|
|
var fileName = Path.GetFileNameWithoutExtension(path);
|
|
if (fileName.StartsWith(id.ToString()))
|
|
{
|
|
var assetPath = path.Replace("\\", "/");
|
|
var so = UnityEditor.AssetDatabase.LoadAssetAtPath<SongData>(assetPath);
|
|
if (so != null) dlcContent.Add(so);
|
|
}
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogWarning("dlcButton LoadContentBySongIdPrefix (Editor) failed: " + e.Message);
|
|
}
|
|
}
|
|
#endif
|
|
if (!string.IsNullOrEmpty(runtimeResourcesFolder))
|
|
{
|
|
var arr = Resources.LoadAll<SongData>(runtimeResourcesFolder);
|
|
if (arr != null)
|
|
{
|
|
foreach (var so in arr)
|
|
{
|
|
if (so == null) continue;
|
|
if (so.name.StartsWith(id.ToString())) dlcContent.Add(so);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dlcContent.Count == 0)
|
|
{
|
|
var arr2 = Resources.LoadAll<SongData>("");
|
|
if (arr2 != null)
|
|
{
|
|
foreach (var so in arr2)
|
|
{
|
|
if (so == null) continue;
|
|
if (so.name.StartsWith(id.ToString())) dlcContent.Add(so);
|
|
}
|
|
}
|
|
}
|
|
|
|
Debug.Log($"dlcButton: Loaded {dlcContent.Count} SongData entries for DLC id={id} (fallback search)");
|
|
}
|
|
|
|
// Resolve dlcData after all buttons have been instantiated
|
|
public void ResolveDlcData()
|
|
{
|
|
Debug.Log($"dlcButton.ResolveDlcData called on '{gameObject.name}'");
|
|
|
|
if (dlcDataSO != null)
|
|
{
|
|
Debug.Log($"dlcButton: dlcDataSO already assigned on '{gameObject.name}' -> {dlcDataSO.name}");
|
|
ApplyDlcData(dlcDataSO);
|
|
return;
|
|
}
|
|
|
|
int id = 0;
|
|
if (dlcID != null && int.TryParse(dlcID.text, out id))
|
|
{
|
|
Debug.Log($"dlcButton: Resolving by dlcID text {id} for '{gameObject.name}'");
|
|
LoadDlcDataById(id);
|
|
return;
|
|
}
|
|
|
|
string goName = gameObject.name;
|
|
if (!string.IsNullOrEmpty(goName))
|
|
{
|
|
var parts = goName.Split('_');
|
|
int parsed = 0;
|
|
if (parts.Length > 0 && int.TryParse(parts[0], out parsed))
|
|
{
|
|
Debug.Log($"dlcButton: Resolving by GameObject name prefix {parsed} for '{gameObject.name}'");
|
|
LoadDlcDataById(parsed);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"dlcButton.ResolveDlcData: could not parse id from GameObject name '{goName}'");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"dlcButton.ResolveDlcData: no dlcID text and no name to parse for '{gameObject.name}'");
|
|
}
|
|
}
|
|
}
|