Files
2026-07-10 10:24:29 +08:00

325 lines
10 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UObject = UnityEngine.Object;
namespace XGame
{
public class CharacterSwitchController : MonoBehaviour
{
private const string ConfigResourcePath = "Config/CharacterConfig";
private const string SelectedCharacterKey = "CharacterSwitch.SelectedCharacterId";
private const string PrefabPath = "Assets/Game/Art/UI/Prefab/UI_CharacterSwitch.prefab";
private CharacterConfigData config;
private CharacterConfigItem currentConfig;
private GameObject currentCharacter;
private Transform characterRoot;
private string errorText;
// UGUI 引用(替换原 IMGUI 绘制)
private GameObject uiPanel; // 实例化的 UI 根
private GameObject selectPanel; // Panel_Select
private Transform listChars; // List_Chars
private GameObject itemTemplate; // Item_Char
private TMP_Text errorLabel; // Txt_Error
private TMP_Text toggleLabel; // Txt_Toggle
private readonly List<Button> itemButtons = new List<Button>();
private readonly List<string> itemIds = new List<string>();
public string CurrentCharacterId
{
get { return currentConfig == null ? string.Empty : currentConfig.id; }
}
private void Start()
{
LoadConfig();
EnsureRoot();
LoadInitialSelection();
StartCoroutine(LoadUIAsync());
}
public void SwitchTo(string characterId)
{
if (config == null)
{
LoadConfig();
}
CharacterConfigItem next = config == null ? null : config.Find(characterId);
if (next == null)
{
errorText = "Character not found: " + characterId;
Debug.LogWarning(errorText);
return;
}
SwitchTo(next);
}
private void LoadConfig()
{
TextAsset textAsset = Resources.Load<TextAsset>(ConfigResourcePath);
if (textAsset == null)
{
errorText = "Missing character config: Resources/" + ConfigResourcePath + ".json";
Debug.LogError(errorText);
return;
}
try
{
config = JsonUtility.FromJson<CharacterConfigData>(textAsset.text);
}
catch (Exception ex)
{
errorText = "Parse character config failed: " + ex.Message;
Debug.LogError(errorText);
config = null;
}
if (config == null || config.characters == null || config.characters.Count == 0)
{
errorText = "Character config is empty.";
Debug.LogError(errorText);
config = null;
}
}
private void EnsureRoot()
{
if (characterRoot != null)
{
return;
}
GameObject rootObject = GameObject.Find("CharacterRoot");
if (rootObject == null)
{
rootObject = new GameObject("CharacterRoot");
}
characterRoot = rootObject.transform;
if (characterRoot.parent != transform)
{
characterRoot.SetParent(transform, true);
}
}
private void LoadInitialSelection()
{
if (config == null)
{
return;
}
string selectedId = PlayerPrefs.GetString(SelectedCharacterKey, string.Empty);
CharacterConfigItem item = config.Find(selectedId) ?? config.GetDefault();
if (item != null)
{
currentConfig = item;
PlayerPrefs.SetString(SelectedCharacterKey, item.id);
PlayerPrefs.Save();
}
}
private void SwitchTo(CharacterConfigItem item)
{
if (currentCharacter != null)
{
Destroy(currentCharacter);
currentCharacter = null;
}
currentConfig = item;
errorText = string.Empty;
PlayerPrefs.SetString(SelectedCharacterKey, item.id);
PlayerPrefs.Save();
RefreshLobbyAvatar();
}
private void RefreshLobbyAvatar()
{
LobbyWorldController lobby = FindObjectOfType<LobbyWorldController>();
if (lobby != null)
{
lobby.RefreshSelectedFigure();
}
}
// ---------------- UGUI ----------------
// 异步加载 UI 预设:按需从 CDN 下载,避免同步加载读到旧 APK 底包。
private IEnumerator LoadUIAsync()
{
string prefabPath = PrefabPath;
if (prefabPath.StartsWith("Assets/"))
{
prefabPath = prefabPath.Substring(7);
}
UObject asset = null;
yield return XResLoader.coLoadRes(prefabPath, typeof(GameObject), o => asset = o);
GameObject prefab = asset as GameObject;
if (prefab == null)
{
Debug.LogError("Load UI_CharacterSwitch failed: " + PrefabPath);
yield break;
}
uiPanel = Instantiate(prefab);
// 角色切换是普通功能界面 → 挂到 UIRoot/Normal 层(见 UI 挂载规则)
UIManager.AttachTo(UILayer.Normal, uiPanel);
CacheUI();
BuildCharacterButtons();
}
private void CacheUI()
{
if (uiPanel == null)
{
return;
}
Transform sel = uiPanel.transform.FindTransform("Panel_Select");
selectPanel = sel == null ? null : sel.gameObject;
listChars = uiPanel.transform.FindTransform("List_Chars");
Transform item = uiPanel.transform.FindTransform("Item_Char");
itemTemplate = item == null ? null : item.gameObject;
Transform err = uiPanel.transform.FindTransform("Txt_Error");
errorLabel = err == null ? null : err.GetComponent<TMP_Text>();
Transform tog = uiPanel.transform.FindTransform("Txt_Toggle");
toggleLabel = tog == null ? null : tog.GetComponent<TMP_Text>();
Transform toggleBtn = uiPanel.transform.FindTransform("Btn_Toggle");
if (toggleBtn != null)
{
Button b = toggleBtn.GetComponent<Button>();
if (b != null)
{
b.onClick.RemoveListener(OnToggleClicked);
b.onClick.AddListener(OnToggleClicked);
}
}
if (selectPanel != null)
{
selectPanel.SetActive(false);
}
UpdateToggleLabel();
}
private void BuildCharacterButtons()
{
itemButtons.Clear();
itemIds.Clear();
if (itemTemplate == null || listChars == null || config == null || config.characters == null)
{
return;
}
for (int i = 0; i < config.characters.Count; i++)
{
CharacterConfigItem item = config.characters[i];
if (item == null)
{
continue;
}
GameObject row = Instantiate(itemTemplate, listChars);
row.name = "Item_Char_" + item.id;
row.SetActive(true);
Transform label = row.transform.FindTransform("Txt_Char");
if (label != null)
{
TMP_Text t = label.GetComponent<TMP_Text>();
if (t != null)
{
t.text = string.IsNullOrEmpty(item.displayName) ? item.id : item.displayName;
}
}
Button btn = row.GetComponent<Button>();
if (btn != null)
{
string captured = item.id;
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => OnCharClicked(captured));
itemButtons.Add(btn);
itemIds.Add(item.id);
}
}
RefreshSelection();
RefreshError();
}
private void OnToggleClicked()
{
if (selectPanel == null)
{
return;
}
selectPanel.SetActive(!selectPanel.activeSelf);
}
private void OnCharClicked(string characterId)
{
SwitchTo(characterId);
RefreshSelection();
RefreshError();
UpdateToggleLabel();
if (selectPanel != null)
{
selectPanel.SetActive(false);
}
}
// 高亮当前选中项(当前项绿色加粗,其余白色)
private void RefreshSelection()
{
for (int i = 0; i < itemButtons.Count; i++)
{
Button btn = itemButtons[i];
if (btn == null)
{
continue;
}
bool selected = currentConfig != null && currentConfig.id == itemIds[i];
TMP_Text t = btn.GetComponentInChildren<TMP_Text>(true);
if (t != null)
{
t.color = selected ? new Color(0.34f, 0.84f, 0.55f, 1f) : new Color(0.96f, 0.97f, 1f, 1f);
t.fontStyle = selected ? FontStyles.Bold : FontStyles.Normal;
}
}
}
private void RefreshError()
{
if (errorLabel == null)
{
return;
}
errorLabel.text = errorText ?? string.Empty;
errorLabel.gameObject.SetActive(!string.IsNullOrEmpty(errorText));
}
private void UpdateToggleLabel()
{
if (toggleLabel == null)
{
return;
}
toggleLabel.text = string.IsNullOrEmpty(CurrentCharacterId) ? "角色" : "角色: " + CurrentCharacterId;
}
}
}