ui基本完毕,修了一大把的bug

This commit is contained in:
2026-07-13 02:28:39 +08:00
parent 1e20d73e90
commit fd22501f71
958 changed files with 378289 additions and 41038 deletions
@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class character_detailInformation : MonoBehaviour
@@ -6,26 +7,32 @@ public class character_detailInformation : MonoBehaviour
[Header("Inspector")]
public Image character_HDImage;
public Image character_HDImage_shadow;
[Header("so")]
public ScriptableObject characterSO;
[Header("Inspector")]
public Text basic_infoText;
public List<Text> basic_infoTexts = new List<Text>(7);
public Text designation_name_combination;
[Header("Inspector")]
public basicInfoBars infoBars;
[Header("Inspector")]
public levelBar_controller levelBar;
[Header("Inspector")]
public RatioBar ratioBar;
[Header("shutdown")]
public Button shut_button;
[Header("Inspector")]
public Image mainTheme_fadeImage;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
// Documentation text normalized.
if (shut_button != null)
{
shut_button.onClick.AddListener(ShutDown);
@@ -33,25 +40,21 @@ public class character_detailInformation : MonoBehaviour
if (characterSO is AllyHero_SO heroSO)
{
// Documentation text normalized.
if (character_HDImage != null && heroSO.ally_hero_HD_image != null)
{
character_HDImage.sprite = heroSO.ally_hero_HD_image;
}
// Documentation text normalized.
if (character_HDImage_shadow != null && heroSO.ally_hero_HD_image != null)
{
character_HDImage_shadow.sprite = heroSO.ally_hero_HD_image;
}
// Documentation text normalized.
if (designation_name_combination != null)
{
designation_name_combination.text = $"{heroSO.ally_heroDesignation} {heroSO.ally_heroName}";
}
// Documentation text normalized.
var levelInfo = heroSO.GetEffectiveLevelForCurrentEXP();
if (levelInfo != null)
{
@@ -63,13 +66,20 @@ public class character_detailInformation : MonoBehaviour
float missHpLoss = levelInfo.missHpLossBase;
int skillSlotLimited = levelInfo.skill_slot_limited;
// Documentation text normalized.
if (basic_infoText != null)
{
basic_infoText.text = $"{attack}\n{maxHP}\n{damageResistance * 100}%\n{maxMana}\n{scoreEfficiency * 100}%\n{skillSlotLimited}\n{missHpLoss}";
}
// Documentation text normalized.
AssignBasicInfoTexts(
attack.ToString(),
maxHP.ToString(),
$"{damageResistance * 100}%",
maxMana.ToString(),
$"{scoreEfficiency * 100}%",
skillSlotLimited.ToString(),
missHpLoss.ToString());
if (infoBars != null)
{
infoBars.SetAttack(attack);
@@ -78,41 +88,70 @@ public class character_detailInformation : MonoBehaviour
infoBars.SetBasicMana(maxMana);
infoBars.SetScoreEfficiency(scoreEfficiency);
infoBars.SetSkillAmountLimit(skillSlotLimited);
infoBars.SetMissDamage((int)missHpLoss); // Documentation text normalized.
infoBars.SetMissDamage((int)missHpLoss);
}
// Documentation text normalized.
if (levelBar != null)
{
levelBar.SetLevelBar(heroSO);
}
// Documentation text normalized.
if (ratioBar != null)
{
ratioBar.SetManaAndDamage(heroSO);
}
}
// Documentation text normalized.
if (mainTheme_fadeImage != null)
{
Color themeColor = heroSO.ally_heroThemeColor;
themeColor.a = 1f; // Documentation text normalized.
themeColor.a = 1f;
mainTheme_fadeImage.color = themeColor;
}
}
}
// Update is called once per frame
void Update()
{
}
// Documentation text normalized.
private void ShutDown()
{
Destroy(gameObject);
}
private void AssignBasicInfoTexts(
string attackText,
string maxHpText,
string damageResistanceText,
string maxManaText,
string scoreEfficiencyText,
string skillSlotLimitedText,
string missHpLossText)
{
if (basic_infoTexts == null || basic_infoTexts.Count == 0)
{
return;
}
string[] values =
{
attackText,
maxHpText,
damageResistanceText,
maxManaText,
scoreEfficiencyText,
skillSlotLimitedText,
missHpLossText
};
int count = Mathf.Min(basic_infoTexts.Count, values.Length);
for (int i = 0; i < count; i++)
{
if (basic_infoTexts[i] != null)
{
basic_infoTexts[i].text = values[i];
}
}
}
}
@@ -35,8 +35,8 @@ 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: 0}
m_SizeDelta: {x: 226, y: 40}
m_AnchoredPosition: {x: 274.54, y: 0}
m_SizeDelta: {x: 550, y: 40}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2847136257128320079
CanvasRenderer:
@@ -67,7 +67,7 @@ MonoBehaviour:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 374deeecafefea14cb9c506944b98b08, type: 3}
m_Type: 0
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
@@ -111,7 +111,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 226, y: 40}
m_SizeDelta: {x: 36.777, y: 40}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &3566225389816842317
MonoBehaviour:
@@ -151,7 +151,7 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4201383612783777168}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
@@ -160,8 +160,8 @@ 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: -25.9662, y: 0}
m_SizeDelta: {x: 151.9325, y: 38.37}
m_AnchoredPosition: {x: -190.98, y: 0}
m_SizeDelta: {x: 168.04, y: 38.37}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &2063637685932881627
CanvasRenderer:
@@ -184,7 +184,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
@@ -193,15 +193,15 @@ MonoBehaviour:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: cc180dff846d13a4d88ddaed6f77e5cd, type: 3}
m_FontSize: 20
m_FontSize: 24
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 3
m_Alignment: 4
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: columnText
m_Text: "S\u7EA7\u522B\u8BA1\u5212"
@@ -1,24 +1,26 @@
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
public class loadSkillsSelect : MonoBehaviour
{
private const string HiddenSkillName = "未解锁技能";
private const string HiddenSkillDescription = "此技能尚未解锁。";
private const string HiddenSkillName = "\u672A\u89E3\u9501\u6280\u80FD";
private const string HiddenSkillDescription = "\u6B64\u6280\u80FD\u5C1A\u672A\u89E3\u9501\u3002";
public static loadSkillsSelect Instance { get; private set; }
public AllyHero_SO currentHeroSO { get; private set; }
[Header("Prefab / UI")]
public GameObject skillCardPrefab; // prefab must have slots_skillSlots component
public RectTransform contentParent; // container where skill cards will be instantiated
public GameObject columnHeaderPrefab; // optional prefab for column headers, e.g. Text or UI element with Text component
public GameObject skillCardPrefab;
public RectTransform contentParent;
public GameObject columnHeaderPrefab;
[Header("Column Layout Settings")]
public float columnWidth = 200f; // Documentation text normalized.
public float columnPosX = 0f; // Documentation text normalized.
public float columnVerticalSpacing = 10f; // Vertical spacing between header and skills in a column
public float columnWidth = 200f;
public float columnPosX = 0f;
public float columnVerticalSpacing = 10f;
[Header("Skill Group Container Settings")]
public Vector2 skillGroupSpacing = new Vector2(10f, 10f);
@@ -35,299 +37,448 @@ public class loadSkillsSelect : MonoBehaviour
public ContentSizeFitter.FitMode skillGroupVerticalFit = ContentSizeFitter.FitMode.PreferredSize;
[Header("Skill Card Layout Settings")]
public bool skillCardUseLayoutElement = true; // Whether to add LayoutElement to skill cards
public bool skillCardUseLayoutElement = true;
public bool skillCardControlPreferredWidth = false;
public float skillCardPreferredWidth = -1f; // Preferred width for skill cards (-1 to ignore)
public float skillCardPreferredWidth = -1f;
public bool skillCardControlPreferredHeight = false;
public float skillCardPreferredHeight = -1f; // Preferred height for skill cards (-1 to ignore)
public float skillCardPreferredHeight = -1f;
public bool skillCardControlFlexibleWidth = false;
public float skillCardFlexibleWidth = -1f; // Flexible width for skill cards (-1 to ignore)
public float skillCardFlexibleWidth = 0f;
public bool skillCardControlFlexibleHeight = false;
public float skillCardFlexibleHeight = -1f; // Flexible height for skill cards (-1 to ignore)
public bool skillCardControlMinWidth = false;
public int skillCardMinWidth = -1; // Minimum width for skill cards (-1 to ignore)
public bool skillCardControlMinHeight = false;
public int skillCardMinHeight = -1; // Minimum height for skill cards (-1 to ignore)
public float skillCardFlexibleHeight = 0f;
// Documentation text normalized.
public AllyHero_SO currentHeroSO { get; private set; }
[Header("Locked Skill Display")]
public bool revealLockedSkills = true;
private readonly List<GameObject> spawnedObjects = new List<GameObject>();
private readonly List<slots_skillSlots> spawnedSlots = new List<slots_skillSlots>();
// The skill-name Text uses BestFit, whose final (smaller) font size — and therefore
// the card's ContentSizeFitter-driven width — only settles a frame or two AFTER the
// objects are built. FlowLayoutGroup measures card widths during that unsettled window
// and sees the widest possible size, so it wraps every card onto its own row. On rapid
// hero switches the rebuild happens before the previous BestFit pass converged, which is
// why the bug only shows up when switching quickly. This coroutine waits for BestFit to
// converge, then forces one authoritative bottom-up layout rebuild so wrapping is
// recomputed from the final widths. Guarded so a newer switch cancels the pending reflow.
private Coroutine reflowCoroutine;
void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
// nothing
}
// Update is called once per frame
void Update()
{
}
// Clear existing instantiated skill cards
public void Clear()
{
if (contentParent == null) return;
for (int i = contentParent.childCount - 1; i >= 0; i--)
for (int i = spawnedObjects.Count - 1; i >= 0; i--)
{
var c = contentParent.GetChild(i);
GameObject go = spawnedObjects[i];
if (go == null)
{
continue;
}
#if UNITY_EDITOR
if (!Application.isPlaying) DestroyImmediate(c.gameObject);
else Destroy(c.gameObject);
if (!Application.isPlaying)
{
DestroyImmediate(go);
}
else
{
Destroy(go);
}
#else
Destroy(c.gameObject);
Destroy(go);
#endif
}
spawnedObjects.Clear();
spawnedSlots.Clear();
currentHeroSO = null;
}
// Documentation text normalized.
private static AllyHero_SO[] _cachedAllyHeroSOs;
// Load skill groups for a given hero SO id and instantiate skill card prefabs
public void LoadSkillsForHero(int heroId)
{
Debug.Log($"loadSkillsSelect.LoadSkillsForHero called with heroId={heroId}");
LoadSkillsForHero(FindHeroById(heroId));
}
public void LoadSkillsForHero(AllyHero_SO hero)
{
Clear();
if (skillCardPrefab == null)
currentHeroSO = hero;
if (hero == null || skillCardPrefab == null || contentParent == null)
{
Debug.LogWarning("loadSkillsSelect: skillCardPrefab is not assigned in Inspector");
return;
}
if (contentParent == null)
{
Debug.LogWarning("loadSkillsSelect: contentParent is not assigned in Inspector");
return;
}
if (heroId <= 0)
{
Debug.LogWarning("loadSkillsSelect: invalid heroId");
return;
}
AllyHero_SO found = null;
try
List<SkillGroup> groupedSkills = hero.skillGroups == null
? new List<SkillGroup>()
: hero.skillGroups.Where(group => group != null).ToList();
HashSet<int> equippedIds = new HashSet<int>((hero.equippedSkillGroupIDs ?? new int[0]).Where(id => id != 0));
// Group skills by columnID and render one column container (with an optional
// header) per distinct columnID, in ascending column order.
Dictionary<int, List<SkillGroup>> groupsByColumn = new Dictionary<int, List<SkillGroup>>();
foreach (SkillGroup group in groupedSkills)
{
if (_cachedAllyHeroSOs == null || _cachedAllyHeroSOs.Length == 0)
if (!groupsByColumn.TryGetValue(group.columnID, out List<SkillGroup> bucket))
{
_cachedAllyHeroSOs = RuntimeResourcesCache.LoadAllAllyHeroes();
bucket = new List<SkillGroup>();
groupsByColumn[group.columnID] = bucket;
}
foreach (var a in _cachedAllyHeroSOs)
{
if (a != null && a.ally_heroID == heroId) { found = a; break; }
}
}
catch (System.Exception ex)
{
Debug.LogError($"loadSkillsSelect: Resources.LoadAll threw: {ex}");
bucket.Add(group);
}
if (found == null)
{
Debug.LogWarning($"loadSkillsSelect: could not find AllyHero_SO for id={heroId}. Ensure the SO is in a Resources folder.");
return;
}
if (found.skillGroups == null || found.skillGroups.Length == 0)
{
Debug.LogWarning($"loadSkillsSelect: found SO for id={heroId} but no skillGroups defined");
return;
}
found.LoadEquippedSkillsFromLocal();
currentHeroSO = found;
bool revealLockedSkills = PlayerSkillService.IsRevealLockedSkillsEnabled();
var levelInfo = found.GetEffectiveLevelForCurrentEXP();
int currentLevelId = levelInfo != null ? levelInfo.levelID : 0;
var groupsByColumn = new System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<SkillGroup>>();
foreach (var g in found.skillGroups)
{
if (g == null) continue;
if (!groupsByColumn.ContainsKey(g.columnID)) groupsByColumn[g.columnID] = new System.Collections.Generic.List<SkillGroup>();
groupsByColumn[g.columnID].Add(g);
}
var sortedColumns = groupsByColumn.Keys.ToList();
List<int> sortedColumns = groupsByColumn.Keys.ToList();
sortedColumns.Sort();
int created = 0;
foreach (var colId in sortedColumns)
foreach (int columnId in sortedColumns)
{
var columnGroups = groupsByColumn[colId];
var columnName = columnGroups.Count > 0 ? columnGroups[0].columnName : $"Column {colId}";
List<SkillGroup> columnGroups = groupsByColumn[columnId];
string columnName = columnGroups.Count > 0 && !string.IsNullOrEmpty(columnGroups[0].columnName)
? columnGroups[0].columnName
: $"Column {columnId}";
string displayColumnName = columnName;
if (!string.IsNullOrEmpty(columnName))
Transform skillsContainer = BuildColumnContainer(columnId, columnName);
foreach (SkillGroup group in columnGroups)
{
char firstChar = columnName[0];
bool isNormalStart = char.IsLetterOrDigit(firstChar) || (firstChar >= 0x4e00 && firstChar <= 0x9fa5);
if (isNormalStart)
{
displayColumnName = " " + columnName;
}
}
GameObject card = Instantiate(skillCardPrefab, skillsContainer);
if (columnHeaderPrefab != null)
{
var headerGo = Instantiate(columnHeaderPrefab, contentParent.transform);
RectTransform headerRect = headerGo.GetComponent<RectTransform>();
if (headerRect != null)
slots_skillSlots slot = card.GetComponent<slots_skillSlots>();
if (slot == null)
{
headerRect.sizeDelta = new Vector2(columnWidth, headerRect.sizeDelta.y);
headerRect.anchoredPosition = new Vector2(columnPosX, headerRect.anchoredPosition.y);
}
var heroSkillColumnText = headerGo.GetComponent<heroSkill_columnText>() ?? headerGo.GetComponentInChildren<heroSkill_columnText>(true);
if (heroSkillColumnText != null)
{
heroSkillColumnText.SetColumnText(displayColumnName);
}
else
{
var text = headerGo.GetComponent<Text>() ?? headerGo.GetComponentInChildren<Text>(true);
if (text != null)
{
text.text = displayColumnName;
}
else
{
var tmpText = headerGo.GetComponent<TMPro.TextMeshProUGUI>() ?? headerGo.GetComponentInChildren<TMPro.TextMeshProUGUI>(true);
if (tmpText != null)
{
tmpText.text = displayColumnName;
}
}
}
}
var skillGroupContainer = new GameObject("SkillGroupContainer", typeof(RectTransform));
skillGroupContainer.transform.SetParent(contentParent.transform, false);
RectTransform skillGroupRect = skillGroupContainer.GetComponent<RectTransform>();
if (skillGroupRect != null)
{
skillGroupRect.anchorMin = new Vector2(0f, 1f);
skillGroupRect.anchorMax = new Vector2(1f, 1f);
skillGroupRect.pivot = new Vector2(0.5f, 1f);
skillGroupRect.offsetMin = new Vector2(0f, skillGroupRect.offsetMin.y);
skillGroupRect.offsetMax = new Vector2(0f, skillGroupRect.offsetMax.y);
skillGroupRect.anchoredPosition = new Vector2(0f, skillGroupRect.anchoredPosition.y - columnVerticalSpacing);
}
ConfigureSkillGroupContainer(skillGroupContainer);
foreach (var g in columnGroups)
{
if (g == null) continue;
var go = Instantiate(skillCardPrefab, skillGroupContainer.transform);
var s = go.GetComponent<slots_skillSlots>();
if (s == null)
{
Debug.LogWarning("loadSkillsSelect: instantiated prefab missing slots_skillSlots component");
continue;
}
bool unlocked = currentLevelId >= Mathf.Clamp(g.thisSkill_levelLimit, 1, 4);
spawnedSlots.Add(slot);
bool unlocked = IsSkillUnlocked(hero, group);
bool shouldMaskSkill = !revealLockedSkills && !unlocked;
string displayName = shouldMaskSkill ? HiddenSkillName : (g.groupName ?? string.Empty);
string displayDescription = shouldMaskSkill ? HiddenSkillDescription : (g.skillDescriptionsText ?? string.Empty);
if (s.skillCard_skillName != null) s.skillCard_skillName.text = displayName;
s.skillCard_skillID = g.skillGroupID;
if (s.skillCard_skillIconImage != null)
slot.heroId = hero.ally_heroID;
slot.skillCard_skillID = group.skillGroupID;
slot.skillCard_skillDescription = shouldMaskSkill ? HiddenSkillDescription : (group.skillDescriptionsText ?? string.Empty);
slot.isSelected = equippedIds.Contains(group.skillGroupID);
slot.isReadOnly = false;
if (slot.skillCard_skillName != null)
{
s.skillCard_skillIconImage.sprite = g.groupIcon;
s.skillCard_skillIconImage.color = g.groupIcon != null ? new Color(1f,1f,1f,1f) : new Color(1f,1f,1f,0f);
}
s.skillCard_skillDescription = displayDescription;
s.heroId = heroId;
s.isSelected = false;
s.ApplyState();
s.isReadOnly = true;
if (skillCardUseLayoutElement)
{
var le = go.GetComponent<UnityEngine.UI.LayoutElement>() ?? go.AddComponent<UnityEngine.UI.LayoutElement>();
if (skillCardControlPreferredWidth) le.preferredWidth = skillCardPreferredWidth;
if (skillCardControlPreferredHeight) le.preferredHeight = skillCardPreferredHeight;
if (skillCardControlFlexibleWidth) le.flexibleWidth = skillCardFlexibleWidth;
if (skillCardControlFlexibleHeight) le.flexibleHeight = skillCardFlexibleHeight;
if (skillCardControlMinWidth) le.minWidth = skillCardMinWidth;
if (skillCardControlMinHeight) le.minHeight = skillCardMinHeight;
slot.skillCard_skillName.text = shouldMaskSkill ? HiddenSkillName : (group.groupName ?? string.Empty);
}
if (s.skillCard_button != null)
if (slot.skillCard_skillIconImage != null)
{
s.skillCard_button.onClick.RemoveAllListeners();
var localS = s;
s.skillCard_button.onClick.AddListener(() => {
if (localS == null) return;
localS.ToggleSelect();
});
}
else
{
Debug.LogWarning("loadSkillsSelect: skillCard_button is null on instantiated prefab");
slot.skillCard_skillIconImage.sprite = group.groupIcon;
slot.skillCard_skillIconImage.color = group.groupIcon != null ? Color.white : new Color(1f, 1f, 1f, 0f);
}
created++;
ApplySkillCardLayout(card);
slot.ApplyState();
}
}
Debug.Log($"loadSkillsSelect: instantiated {created} skill cards for heroId={heroId}");
UpdateSkillCardSelections();
// Wait for the skill-name BestFit pass to converge, then force one authoritative
// layout rebuild so FlowLayoutGroup wraps against the final (settled) card widths.
ScheduleReflow();
}
private void ConfigureSkillGroupContainer(GameObject skillGroupContainer)
// Cancels any pending reflow (so the newest hero switch wins) and starts a fresh one.
private void ScheduleReflow()
{
if (skillGroupContainer == null)
if (!isActiveAndEnabled)
{
return;
}
var existingGrid = skillGroupContainer.GetComponent<GridLayoutGroup>();
if (existingGrid != null)
if (reflowCoroutine != null)
{
existingGrid.enabled = false;
StopCoroutine(reflowCoroutine);
reflowCoroutine = null;
}
var flow = skillGroupContainer.GetComponent<FlowLayoutGroup>() ?? skillGroupContainer.AddComponent<FlowLayoutGroup>();
flow.childAlignment = skillGroupChildAlignment;
flow.padding = new RectOffset(skillGroupPaddingLeft, skillGroupPaddingRight, skillGroupPaddingTop, skillGroupPaddingBottom);
reflowCoroutine = StartCoroutine(ReflowAfterBestFitSettles());
}
// BestFit resolves during rendering, so its final font size (and the resulting
// ContentSizeFitter-driven card width) is only readable a frame or two after the cards
// are built. Wait for that, then rebuild the layout bottom-up from the settled widths.
private IEnumerator ReflowAfterBestFitSettles()
{
// Two frames: one for BestFit to run, one for the CSF chain to propagate widths.
yield return null;
yield return null;
if (contentParent != null)
{
RebuildLayoutBottomUp(contentParent);
}
reflowCoroutine = null;
}
// Force each LayoutGroup to recompute, working from the deepest children up to the
// contentParent, so FlowLayoutGroup sees the final child widths when it wraps.
private static void RebuildLayoutBottomUp(RectTransform root)
{
if (root == null)
{
return;
}
var groups = root.GetComponentsInChildren<LayoutGroup>(true);
// Children come before parents in reverse of the hierarchy walk; rebuild deepest first.
for (int i = groups.Length - 1; i >= 0; i--)
{
LayoutGroup group = groups[i];
if (group != null)
{
LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)group.transform);
}
}
LayoutRebuilder.ForceRebuildLayoutImmediate(root);
}
// Builds one column container (VerticalLayoutGroup) parented to contentParent,
// instantiates the optional column header into it, then returns the horizontal
// skills container that skill cards should be parented under.
private Transform BuildColumnContainer(int columnId, string columnName)
{
GameObject columnContainer = new GameObject($"Column_{columnId}", typeof(RectTransform));
columnContainer.transform.SetParent(contentParent, false);
spawnedObjects.Add(columnContainer);
VerticalLayoutGroup vlg = columnContainer.AddComponent<VerticalLayoutGroup>();
vlg.spacing = columnVerticalSpacing;
vlg.childAlignment = TextAnchor.UpperLeft;
// Do NOT let the VLG drive/override child widths. The SkillsContainer's width is
// set explicitly below so FlowLayoutGroup has a definite, reliable width to wrap
// against. If the VLG controlled width, that width would depend on contentParent
// having a layout group that honors LayoutElement.preferredWidth — when it does
// not, the container collapses to ~0 and every card wraps onto its own row.
vlg.childControlWidth = false;
vlg.childControlHeight = false;
vlg.childForceExpandWidth = false;
vlg.childForceExpandHeight = false;
// Resolve the width the cards get to flow across. Prefer the real available
// width of contentParent (its own width minus its layout padding) so cards only
// wrap when they genuinely run out of horizontal space. columnWidth is only a
// fallback for when contentParent has no definite width yet — using it directly
// is what caused every card to wrap onto its own row (it is far narrower than a
// single card).
float innerWidth = ResolveFlowWidth();
LayoutElement columnLayout = columnContainer.AddComponent<LayoutElement>();
columnLayout.preferredWidth = innerWidth;
RectTransform columnRect = (RectTransform)columnContainer.transform;
columnRect.anchorMin = new Vector2(0f, 1f);
columnRect.anchorMax = new Vector2(0f, 1f);
columnRect.pivot = new Vector2(0f, 1f);
columnRect.sizeDelta = new Vector2(innerWidth, columnRect.sizeDelta.y);
if (columnHeaderPrefab != null)
{
GameObject header = Instantiate(columnHeaderPrefab, columnContainer.transform);
ApplyColumnHeaderText(header, columnName);
}
GameObject skillsContainer = new GameObject("SkillsContainer", typeof(RectTransform));
skillsContainer.transform.SetParent(columnContainer.transform, false);
// Give the SkillsContainer the same explicit width as the column so
// FlowLayoutGroup reads a definite width directly from its own RectTransform
// (not relying on the parent-width fallback). Anchored top-left so the width is
// fixed rather than stretched by the parent.
RectTransform skillsRect = (RectTransform)skillsContainer.transform;
skillsRect.anchorMin = new Vector2(0f, 1f);
skillsRect.anchorMax = new Vector2(0f, 1f);
skillsRect.pivot = new Vector2(0f, 1f);
skillsRect.sizeDelta = new Vector2(innerWidth, skillsRect.sizeDelta.y);
// FlowLayoutGroup measures each card's width and wraps to a new row when the
// current row exceeds the container's available width. innerWidth (above) gives
// it a definite width to wrap against.
FlowLayoutGroup flow = skillsContainer.AddComponent<FlowLayoutGroup>();
flow.SpacingX = skillGroupSpacing.x;
flow.SpacingY = skillGroupSpacing.y;
flow.childAlignment = skillGroupChildAlignment;
flow.ExpandChildHeight = skillGroupExpandChildHeight;
flow.ForcedChildHeight = skillGroupForcedChildHeight;
flow.padding = new RectOffset(skillGroupPaddingLeft, skillGroupPaddingRight, skillGroupPaddingTop, skillGroupPaddingBottom);
var fitter = skillGroupContainer.GetComponent<ContentSizeFitter>() ?? skillGroupContainer.AddComponent<ContentSizeFitter>();
fitter.horizontalFit = skillGroupHorizontalFit;
// Only fit vertically: FlowLayoutGroup reports the wrapped height as its
// preferred height. Horizontal must stay unconstrained so the width is driven
// by the column (a PreferredSize horizontal fit would defeat wrapping).
ContentSizeFitter fitter = skillsContainer.AddComponent<ContentSizeFitter>();
fitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
fitter.verticalFit = skillGroupVerticalFit;
return skillsContainer.transform;
}
private static void ApplyColumnHeaderText(GameObject header, string columnName)
{
if (header == null)
{
return;
}
heroSkill_columnText columnTextBinder = header.GetComponent<heroSkill_columnText>();
if (columnTextBinder != null)
{
columnTextBinder.SetColumnText(columnName);
return;
}
Text uiText = header.GetComponent<Text>();
if (uiText == null)
{
uiText = header.GetComponentInChildren<Text>(true);
}
if (uiText != null)
{
uiText.text = columnName;
return;
}
TMPro.TMP_Text tmpText = header.GetComponent<TMPro.TMP_Text>();
if (tmpText == null)
{
tmpText = header.GetComponentInChildren<TMPro.TMP_Text>(true);
}
if (tmpText != null)
{
tmpText.text = columnName;
}
}
// Returns the width that skill cards flow across before wrapping. Uses the real
// rendered width of contentParent minus its layout padding, so a card only wraps
// when it genuinely runs out of horizontal room. Falls back to columnWidth (then a
// hard 200) only when contentParent has no definite width resolved yet.
private float ResolveFlowWidth()
{
RectTransform parentRect = contentParent;
if (parentRect != null)
{
float width = parentRect.rect.width;
HorizontalOrVerticalLayoutGroup hvlg = parentRect.GetComponent<HorizontalOrVerticalLayoutGroup>();
if (hvlg != null)
{
width -= hvlg.padding.horizontal;
}
if (width > 1f)
{
return width;
}
}
if (columnWidth > 0f)
{
return columnWidth;
}
return 200f;
}
private void ApplySkillCardLayout(GameObject card)
{
if (!skillCardUseLayoutElement)
{
return;
}
LayoutElement le = card.GetComponent<LayoutElement>() ?? card.AddComponent<LayoutElement>();
if (skillCardControlPreferredWidth)
{
le.preferredWidth = skillCardPreferredWidth;
}
if (skillCardControlPreferredHeight)
{
le.preferredHeight = skillCardPreferredHeight;
}
else if (skillGroupExpandChildHeight && skillGroupForcedChildHeight > 0f)
{
le.preferredHeight = skillGroupForcedChildHeight;
}
if (skillCardControlFlexibleWidth)
{
le.flexibleWidth = skillCardFlexibleWidth;
}
if (skillCardControlFlexibleHeight)
{
le.flexibleHeight = skillCardFlexibleHeight;
}
}
public void UpdateSkillCardSelections()
{
if (currentHeroSO == null || contentParent == null) return;
var equippedIds = currentHeroSO.equippedSkillGroupIDs ?? new int[0];
var equipped = new System.Collections.Generic.HashSet<int>(equippedIds);
UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(contentParent);
var slots = contentParent.GetComponentsInChildren<slots_skillSlots>(true);
if (slots == null || slots.Length == 0) return;
foreach (var slot in slots)
if (currentHeroSO == null)
{
if (slot == null) continue;
slot.isSelected = equipped.Contains(slot.skillCard_skillID);
return;
}
HashSet<int> equippedIds = new HashSet<int>((currentHeroSO.equippedSkillGroupIDs ?? new int[0]).Where(id => id != 0));
for (int i = 0; i < spawnedSlots.Count; i++)
{
slots_skillSlots slot = spawnedSlots[i];
if (slot == null)
{
continue;
}
slot.heroId = currentHeroSO.ally_heroID;
slot.isSelected = equippedIds.Contains(slot.skillCard_skillID);
slot.ApplyState();
}
}
}
private static bool IsSkillUnlocked(AllyHero_SO hero, SkillGroup group)
{
if (hero == null || group == null)
{
return false;
}
int currentLevelId = hero.GetEffectiveLevelForCurrentEXP()?.levelID ?? 0;
int requiredLevelId = Mathf.Clamp(group.thisSkill_levelLimit, 1, 4);
return currentLevelId >= requiredLevelId;
}
private static AllyHero_SO FindHeroById(int heroId)
{
if (heroId <= 0)
{
return null;
}
AllyHero_SO[] allHeroes = RuntimeResourcesCache.LoadAllAllyHeroes();
for (int i = 0; i < allHeroes.Length; i++)
{
AllyHero_SO hero = allHeroes[i];
if (hero != null && hero.ally_heroID == heroId)
{
return hero;
}
}
return null;
}
}
@@ -1,53 +1,80 @@
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using System.Linq;
public class load_skill_detail_prefab_inHeroDetailPrefab : MonoBehaviour
{
private const string EmptySlotNameDefault = "\u7A7A\u6280\u80FD\u69FD\u4F4D";
private const string EmptySlotDescriptionDefault = "\u8BE5\u69FD\u4F4D\u53EF\u7528\uFF0C\u4F46\u5F53\u524D\u672A\u88C5\u914D\u4EFB\u4F55\u6280\u80FD\u3002";
private const string LockedSlotNameDefault = "\u672A\u89E3\u9501\u69FD\u4F4D";
private const string LockedSlotDescriptionDefault = "\u9700\u8981\u63D0\u5347\u89D2\u8272\u7B49\u7EA7\u540E\u89E3\u9501\u8BE5\u69FD\u4F4D\u3002";
public static load_skill_detail_prefab_inHeroDetailPrefab Instance { get; private set; }
[Header("Prefab / UI")]
public GameObject skillDetailPrefab; // prefab must have skill_detail_prefab_inHeroDetail component
public RectTransform contentParent; // container where skill detail prefabs will be instantiated (e.g., Scroll View Content)
public GameObject skillDetailPrefab;
public RectTransform contentParent;
[Header("Fallback Texts")]
public string emptySlotName = EmptySlotNameDefault;
public string emptySlotDescription = EmptySlotDescriptionDefault;
public string lockedSlotName = LockedSlotNameDefault;
public string lockedSlotDescription = LockedSlotDescriptionDefault;
void Awake()
{
if (Instance == null) Instance = this;
else Destroy(gameObject);
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
// Clear existing instantiated skill details
public void Clear()
{
if (contentParent == null) return;
if (contentParent == null)
{
return;
}
for (int i = contentParent.childCount - 1; i >= 0; i--)
{
var c = contentParent.GetChild(i);
Transform child = contentParent.GetChild(i);
#if UNITY_EDITOR
if (!Application.isPlaying) DestroyImmediate(c.gameObject);
else Destroy(c.gameObject);
if (!Application.isPlaying)
{
DestroyImmediate(child.gameObject);
}
else
{
Destroy(child.gameObject);
}
#else
Destroy(c.gameObject);
Destroy(child.gameObject);
#endif
}
}
// Load equipped skill details for a given hero SO id and instantiate skill detail prefabs
public void LoadSkillDetails(int heroId)
{
Debug.Log($"load_skill_detail_prefab_inHeroDetailPrefab.LoadSkillDetails called with heroId={heroId}");
Clear();
if (skillDetailPrefab == null)
{
Debug.LogWarning("load_skill_detail_prefab_inHeroDetailPrefab: skillDetailPrefab is not assigned in Inspector");
return;
}
if (contentParent == null)
{
Debug.LogWarning("load_skill_detail_prefab_inHeroDetailPrefab: contentParent is not assigned in Inspector");
return;
}
if (heroId <= 0)
{
Debug.LogWarning("load_skill_detail_prefab_inHeroDetailPrefab: invalid heroId");
@@ -57,10 +84,14 @@ public class load_skill_detail_prefab_inHeroDetailPrefab : MonoBehaviour
AllyHero_SO found = null;
try
{
var arr = RuntimeResourcesCache.LoadAllAllyHeroes();
foreach (var a in arr)
var allHeroes = RuntimeResourcesCache.LoadAllAllyHeroes();
foreach (var hero in allHeroes)
{
if (a != null && a.ally_heroID == heroId) { found = a; break; }
if (hero != null && hero.ally_heroID == heroId)
{
found = hero;
break;
}
}
}
catch (System.Exception ex)
@@ -74,18 +105,13 @@ public class load_skill_detail_prefab_inHeroDetailPrefab : MonoBehaviour
return;
}
// Get equipped skill group IDs (filter out 0)
var equippedIds = (found.equippedSkillGroupIDs ?? new int[0]).Where(id => id != 0).ToList();
// Get current level info
var levelInfo = found.GetEffectiveLevelForCurrentEXP();
int currentMaxSlots = levelInfo?.skill_slot_limited ?? 0;
// Get max slots from highest level
int maxSlotsOverall = 0;
if (found.levelStats != null && found.levelStats.Count > 0)
{
// Assume levelStats is sorted by requiredEXP ascending, so last is highest
maxSlotsOverall = found.levelStats[found.levelStats.Count - 1].skill_slot_limited;
}
@@ -94,7 +120,6 @@ public class load_skill_detail_prefab_inHeroDetailPrefab : MonoBehaviour
{
if (i < equippedIds.Count)
{
// Instantiate equipped skill
int skillId = equippedIds[i];
SkillGroup group = found.GetSkillGroupByID(skillId);
if (group == null)
@@ -111,74 +136,64 @@ public class load_skill_detail_prefab_inHeroDetailPrefab : MonoBehaviour
continue;
}
// Populate fields for equipped skill
if (detail.skill_info_image != null)
{
detail.skill_info_image.sprite = group.groupIcon;
detail.skill_info_image.color = group.groupIcon != null ? new Color(1f, 1f, 1f, 1f) : new Color(1f, 1f, 1f, 0f);
detail.skill_info_image.color = group.groupIcon != null ? Color.white : new Color(1f, 1f, 1f, 0f);
}
if (detail.skill_info_name != null)
{
detail.skill_info_name.text = group.groupName ?? string.Empty;
}
if (detail.skill_info_description != null)
{
detail.skill_info_description.text = group.skillDescriptionsText ?? string.Empty;
}
created++;
continue;
}
else
var emptyGo = Instantiate(skillDetailPrefab, contentParent);
var detailEmpty = emptyGo.GetComponent<skill_detail_prefab_inHeroDetail>();
if (detailEmpty == null)
{
// Instantiate empty slot
var go = Instantiate(skillDetailPrefab, contentParent);
var detail = go.GetComponent<skill_detail_prefab_inHeroDetail>();
if (detail == null)
{
Debug.LogWarning("load_skill_detail_prefab_inHeroDetailPrefab: instantiated prefab missing skill_detail_prefab_inHeroDetail component");
continue;
}
// Populate fields for empty slot
if (i < currentMaxSlots)
{
// Available slot
if (detail.skill_info_image != null)
{
detail.skill_info_image.sprite = detail.skill_slot_available_image;
detail.skill_info_image.color = detail.skill_slot_available_image != null ? Color.white : new Color(1f, 1f, 1f, 0f);
}
if (detail.skill_info_name != null)
{
detail.skill_info_name.text = "ܲλ";
}
if (detail.skill_info_description != null)
{
detail.skill_info_description.text = "ܲλУƼװһ";
}
}
else
{
// Locked slot
if (detail.skill_info_image != null)
{
detail.skill_info_image.sprite = detail.skill_slot_locked_image;
detail.skill_info_image.color = detail.skill_slot_locked_image != null ? Color.white : new Color(1f, 1f, 1f, 0f);
}
if (detail.skill_info_name != null)
{
detail.skill_info_name.text = "ܲλ";
}
if (detail.skill_info_description != null)
{
detail.skill_info_description.text = "Խ²λ";
}
}
created++;
Debug.LogWarning("load_skill_detail_prefab_inHeroDetailPrefab: instantiated prefab missing skill_detail_prefab_inHeroDetail component");
continue;
}
bool slotUnlocked = i < currentMaxSlots;
if (detailEmpty.skill_info_image != null)
{
Sprite spriteToUse = slotUnlocked ? detailEmpty.skill_slot_available_image : detailEmpty.skill_slot_locked_image;
detailEmpty.skill_info_image.sprite = spriteToUse;
detailEmpty.skill_info_image.color = spriteToUse != null ? Color.white : new Color(1f, 1f, 1f, 0f);
}
if (detailEmpty.skill_info_name != null)
{
detailEmpty.skill_info_name.text = slotUnlocked
? ResolveText(emptySlotName, EmptySlotNameDefault)
: ResolveText(lockedSlotName, LockedSlotNameDefault);
}
if (detailEmpty.skill_info_description != null)
{
detailEmpty.skill_info_description.text = slotUnlocked
? ResolveText(emptySlotDescription, EmptySlotDescriptionDefault)
: ResolveText(lockedSlotDescription, LockedSlotDescriptionDefault);
}
created++;
}
Debug.Log($"load_skill_detail_prefab_inHeroDetailPrefab: instantiated {created} skill detail prefabs for heroId={heroId}");
}
private static string ResolveText(string value, string fallback)
{
return string.IsNullOrWhiteSpace(value) ? fallback : value;
}
}
@@ -395,7 +395,7 @@ MonoBehaviour:
skillColor_available: {r: 1, g: 1, b: 1, a: 1}
skillColor_selected: {r: 0.5943396, g: 1, b: 0.5943396, a: 1}
skillColor_unavailable: {r: 0.5999999, g: 0.5999999, b: 0.5999999, a: 1}
skillColor_locked: {r: 0.81886786, g: 0.5299465, b: 0.5299465, a: 1}
skillColor_locked: {r: 1, g: 0.6179246, b: 0.6179246, a: 1}
isSelected: 0
heroId: 0
isReadOnly: 0
@@ -34,7 +34,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: 100.84909, y: -25.2972}
m_AnchoredPosition: {x: 100.84909, y: -29.1}
m_SizeDelta: {x: 1030.2424, y: 90}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4124427729845495542
@@ -58,7 +58,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.9254902, g: 0.9254902, b: 0.9254902, a: 1}
m_Color: {r: 0.14117648, g: 0.34117648, b: 0.7764706, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
@@ -66,7 +66,7 @@ MonoBehaviour:
m_PersistentCalls:
m_Calls: []
m_FontData:
m_Font: {fileID: 12800000, guid: 775c674e81662c644b64550d2e8f74e0, type: 3}
m_Font: {fileID: 12800000, guid: d254d20d93651ae448ff13d10dff30ab, type: 3}
m_FontSize: 30
m_FontStyle: 0
m_BestFit: 0
@@ -114,7 +114,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 175, y: 175}
m_SizeDelta: {x: 180, y: 180}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6787124985996953894
CanvasRenderer:
@@ -144,8 +144,8 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: c7c2fe3c72ded9244bf6b5506cc84f41, type: 3}
m_Type: 0
m_Sprite: {fileID: 21300000, guid: 9d8f69bebb9e2e9489798d3336f5d2b4, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
@@ -212,7 +212,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.9245283, g: 0.9245283, b: 0.9245283, a: 1}
m_Color: {r: 0.011764706, g: 0.5176471, b: 0.99215686, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
@@ -233,6 +233,81 @@ MonoBehaviour:
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: "\u6280\u80FD\u540D / \u7A7A\u95F2\u69FD\u4F4D / \u9501\u5B9A\u69FD\u4F4D"
--- !u!1 &5252193634107135165
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 8595639840685474323}
- component: {fileID: 7900913233867467852}
- component: {fileID: 607155179397191471}
m_Layer: 0
m_Name: btm
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &8595639840685474323
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252193634107135165}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7749999690041009414}
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: -526.7, y: 0}
m_SizeDelta: {x: 150, y: 150}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7900913233867467852
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252193634107135165}
m_CullTransparentMesh: 1
--- !u!114 &607155179397191471
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5252193634107135165}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.36078432, g: 0.3647059, b: 0.36078432, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &5512514823739788927
GameObject:
m_ObjectHideFlags: 0
@@ -298,8 +373,8 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
m_Type: 1
m_Sprite: {fileID: 21300000, guid: 44bccbd64c92341409553503e1fa45b4, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 0
m_FillMethod: 4
@@ -337,10 +412,11 @@ RectTransform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 1227634720023912104}
- {fileID: 8595639840685474323}
- {fileID: 1097740655041088334}
- {fileID: 3402633476703254850}
- {fileID: 8725241624818700542}
- {fileID: 1227634720023912104}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -49,11 +49,49 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
// Documentation text normalized.
public bool isReadOnly = false;
private bool buttonListenerBound = false;
void Start()
{
BindButtonClickIfNeeded();
ApplyState();
}
void OnEnable()
{
BindButtonClickIfNeeded();
}
private void BindButtonClickIfNeeded()
{
if (skillCard_button == null || buttonListenerBound)
{
return;
}
skillCard_button.onClick.AddListener(HandleSkillButtonClick);
buttonListenerBound = true;
}
private void OnDestroy()
{
if (skillCard_button != null && buttonListenerBound)
{
skillCard_button.onClick.RemoveListener(HandleSkillButtonClick);
buttonListenerBound = false;
}
}
private void HandleSkillButtonClick()
{
if (isReadOnly)
{
return;
}
ToggleSelect();
}
public void ApplyState()
{
SkillVisualState visualState = GetVisualState();