技能主要更新,修复卡顿并加入动画,以及各种其他更新。

This commit is contained in:
FloatGaming
2026-02-07 21:05:17 +08:00
parent abeca51be5
commit 1ac3cd0104
1349 changed files with 1526749 additions and 24850 deletions
@@ -147,7 +147,8 @@ TextureImporter:
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
nameFileIdTable:
"20251222\u65B0\u9009\u66F2_0": -1224498633502166962
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
@@ -35,8 +35,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_AnchoredPosition: {x: 150, y: 10}
m_SizeDelta: {x: 300, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8690290934256204690
CanvasRenderer:
@@ -70,7 +70,7 @@ MonoBehaviour:
m_Font: {fileID: 12800000, guid: cc180dff846d13a4d88ddaed6f77e5cd, type: 3}
m_FontSize: 20
m_FontStyle: 0
m_BestFit: 1
m_BestFit: 0
m_MinSize: 2
m_MaxSize: 40
m_Alignment: 3
@@ -92,5 +92,5 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 2
m_HorizontalFit: 0
m_VerticalFit: 2
@@ -147,7 +147,8 @@ TextureImporter:
secondaryTextures: []
spriteCustomMetadata:
entries: []
nameFileIdTable: {}
nameFileIdTable:
e2f5f12320cf0a6ace97565f8ffc9872 (3)_0: 339797066516483561
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
@@ -12,16 +12,16 @@ public class loadDetailsPrefab : MonoBehaviour
[Header("Settings")]
public float hoverDelay = 0.5f;
[Tooltip("偏移量相对于屏幕宽高的百分比 (0.01 = 1%)")]
[Tooltip("ƫĻߵİٷֱ (0.01 = 1%)")]
public Vector2 offsetPercent = new Vector2(0f, -0.03f);
[Tooltip("最小偏移像素值")]
[Tooltip("Сƫֵ")]
public Vector2 minOffsetPixels = new Vector2(0f, -50f);
private GameObject currentDetailsInstance;
private detailsPrefabController currentController;
private Coroutine showCoroutine;
private bool isAbove; // 是否在上方显示
private bool isAbove; // ǷϷʾ
void Awake()
{
@@ -37,7 +37,7 @@ public class loadDetailsPrefab : MonoBehaviour
if (detailsPrefab == null) return;
isAbove = (type == "技能"); // 技能在上方,英雄在下方
isAbove = (type == ""); // ϷӢ·
showCoroutine = StartCoroutine(ShowDetailsDelayed(type, name, status, description, position));
}
@@ -65,7 +65,7 @@ public class loadDetailsPrefab : MonoBehaviour
if (currentController.item_statusText != null)
{
Color statusColor;
if (status == "技能可用" || status == "技能已选择" || status == "可用")
if (status == "ܿ" || status == "ѡ" || status == "")
{
statusColor = currentController.availableColor;
}
@@ -96,35 +96,35 @@ public class loadDetailsPrefab : MonoBehaviour
RectTransform canvasRect = canvas.GetComponent<RectTransform>();
// 计算动态偏移量(垂直方向保持一致大小)
// 㶯̬ƫֱ򱣳һ´С
Vector2 dynamicOffset = CalculateDynamicOffset();
Vector2 baseScreenPos = new Vector2(position.x, position.y) + dynamicOffset;
// 计算 prefab 宽度(屏幕像素)并设置最大水平位移为 prefab 宽度(之前为一半)
// prefab ȣĻأˮƽλΪ prefab ȣ֮ǰΪһ
float prefWidthPixels = rt.rect.width * (canvas != null ? canvas.scaleFactor : 1f);
float maxNudge = prefWidthPixels*4; // now allow up to full prefab width
// 以屏幕中心为基准计算鼠标水平偏离因子 (-1..1)
// ĻΪ׼ˮƽƫ (-1..1)
float screenCenterX = Screen.width * 0.5f;
float normalized = 0f;
if (screenCenterX > 0f)
normalized = (position.x - screenCenterX) / screenCenterX; // -1(left) .. 0(center) .. +1(right)
normalized = Mathf.Clamp(normalized, -1f, 1f);
// nudge 方向为向屏幕中心移动:鼠标在左侧(normalized<0)时,向右移动 (+),右侧相反
// nudge ΪĻƶ(normalized<0)ʱƶ (+)Ҳ
float nudge = -normalized * maxNudge;
Vector2 screenPos = baseScreenPos + new Vector2(nudge, 0f);
Vector3 initialPos = CalculateWorldPos(screenPos, canvas, canvasRect, rt);
// 如果初始位置超出画布,尝试翻转上下方并重新计算(保持相同垂直间距)
// ʼλóԷת·¼ֱͬ
if (IsOutOfCanvas(initialPos, rt, canvas))
{
isAbove = !isAbove;
dynamicOffset = CalculateDynamicOffset();
baseScreenPos = new Vector2(position.x, position.y) + dynamicOffset;
// 重新计算 nudge(基于原始鼠标位置仍然有效)
// ¼ nudgeԭʼλȻЧ
normalized = 0f;
if (screenCenterX > 0f)
normalized = (position.x - screenCenterX) / screenCenterX;
@@ -137,7 +137,7 @@ public class loadDetailsPrefab : MonoBehaviour
rt.position = initialPos;
// 最终调整以确保不超出(会修正剩余的左右/上下边界)
// յȷʣ/±߽磩
AdjustPositionWithinCanvas(rt, canvas);
}
@@ -181,43 +181,43 @@ public class loadDetailsPrefab : MonoBehaviour
float canvasBottom = canvasCorners[0].y;
float canvasTop = canvasCorners[2].y;
// 临时设置位置以获取角落
// ʱλԻȡ
Vector3 originalPos = rt.position;
rt.position = pos;
Vector3[] rtCorners = new Vector3[4];
rt.GetWorldCorners(rtCorners);
rt.position = originalPos;
// 检查是否完全在 Canvas
// Ƿȫ Canvas
return rtCorners[0].x < canvasLeft || rtCorners[2].x > canvasRight ||
rtCorners[0].y < canvasBottom || rtCorners[2].y > canvasTop;
}
private Vector2 CalculateDynamicOffset()
{
// 获取当前屏幕分辨率
// ȡǰĻֱ
float screenWidth = Screen.width;
float screenHeight = Screen.height;
// 计算基于百分比的偏移
// ڰٷֱȵƫ
Vector2 percentOffset = new Vector2(
screenWidth * offsetPercent.x,
screenHeight * offsetPercent.y
);
// 如果在上方,翻转Y偏移
// ϷתYƫ
if (isAbove)
{
percentOffset.y = -percentOffset.y;
}
// 应用最小像素偏移限制
// ӦСƫ
Vector2 finalOffset = new Vector2(
Mathf.Abs(percentOffset.x) > Mathf.Abs(minOffsetPixels.x) ? percentOffset.x : minOffsetPixels.x,
Mathf.Abs(percentOffset.y) > Mathf.Abs(minOffsetPixels.y) ? percentOffset.y : minOffsetPixels.y
);
// 如果在上方,确保Y偏移为正
// ϷȷΪ
if (isAbove)
{
finalOffset.y = Mathf.Abs(finalOffset.y);
@@ -241,7 +241,7 @@ public class loadDetailsPrefab : MonoBehaviour
Canvas selfCanvas = GetComponentInParent<Canvas>();
if (selfCanvas != null) return selfCanvas;
return FindObjectOfType<Canvas>();
return Object.FindAnyObjectByType<Canvas>();
}
private void AdjustPositionWithinCanvas(RectTransform rt, Canvas canvas)
@@ -260,13 +260,13 @@ public class loadDetailsPrefab : MonoBehaviour
Vector3 adjustedPos = rt.position;
// 调整水平位置
// ˮƽλ
if (rtCorners[2].x > canvasRight)
adjustedPos.x -= (rtCorners[2].x - canvasRight);
if (rtCorners[0].x < canvasLeft)
adjustedPos.x += (canvasLeft - rtCorners[0].x);
// 调整垂直位置
// ֱλ
if (rtCorners[2].y > canvasTop)
adjustedPos.y -= (rtCorners[2].y - canvasTop);
if (rtCorners[0].y < canvasBottom)
@@ -290,4 +290,4 @@ public class loadDetailsPrefab : MonoBehaviour
currentController = null;
}
}
}
}
@@ -14,9 +14,15 @@ public class loadSkillsSelect : MonoBehaviour
[Header("Column Layout Settings")]
public float columnSpacing = 10f; // Spacing between items in a column
public float columnWidth = 200f; // 宽度控制 (RectTransform width)
public float columnPosX = 0f; // 位置控制 (RectTransform pos x)
public TextAnchor columnChildAlignment = TextAnchor.MiddleLeft; // Alignment of children in the column
public bool columnChildControlWidth = false; // Whether to control child width
public bool columnChildControlHeight = false; // Whether to control child height
public bool columnChildScaleWidth = false; // Whether to use child scale width
public bool columnChildScaleHeight = false; // Whether to use child scale height
public bool columnChildForceExpandWidth = false; // Whether to force expand width
public bool columnChildForceExpandHeight = false; // Whether to force expand height
public int columnPaddingLeft = 0; // Left padding for the column
public int columnPaddingRight = 0; // Right padding for the column
public int columnPaddingTop = 0; // Top padding for the column
@@ -25,14 +31,20 @@ public class loadSkillsSelect : MonoBehaviour
[Header("Skill Card Layout Settings")]
public bool skillCardUseLayoutElement = true; // Whether to add LayoutElement to skill cards
public bool skillCardControlPreferredWidth = false;
public float skillCardPreferredWidth = -1f; // Preferred width for skill cards (-1 to ignore)
public bool skillCardControlPreferredHeight = false;
public float skillCardPreferredHeight = -1f; // Preferred height for skill cards (-1 to ignore)
public bool skillCardControlFlexibleWidth = false;
public float skillCardFlexibleWidth = -1f; // Flexible width for skill cards (-1 to ignore)
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)
// 当前加载的英雄SO,用于技能卡片更新equippedSkillGroupIDs
// ǰصӢSOڼܿƬequippedSkillGroupIDs
public AllyHero_SO currentHeroSO { get; private set; }
void Awake()
@@ -69,6 +81,9 @@ public class loadSkillsSelect : MonoBehaviour
}
}
// 缓存 AllyHero_SO 数组以避免在 UI 刷新时重复调用昂贵的 Resources.LoadAll
private static AllyHero_SO[] _cachedAllyHeroSOs;
// Load skill groups for a given hero SO id and instantiate skill card prefabs
public void LoadSkillsForHero(int heroId)
{
@@ -93,8 +108,11 @@ public class loadSkillsSelect : MonoBehaviour
AllyHero_SO found = null;
try
{
var arr = Resources.LoadAll<AllyHero_SO>("");
foreach (var a in arr)
if (_cachedAllyHeroSOs == null || _cachedAllyHeroSOs.Length == 0)
{
_cachedAllyHeroSOs = Resources.LoadAll<AllyHero_SO>("");
}
foreach (var a in _cachedAllyHeroSOs)
{
if (a != null && a.ally_heroID == heroId) { found = a; break; }
}
@@ -115,9 +133,9 @@ public class loadSkillsSelect : MonoBehaviour
return;
}
currentHeroSO = found; // 设置当前英雄SO
currentHeroSO = found; // õǰӢSO
// columnID分组技能组
// columnID
var groupsByColumn = new System.Collections.Generic.Dictionary<int, System.Collections.Generic.List<SkillGroup>>();
foreach (var g in found.skillGroups)
{
@@ -126,7 +144,7 @@ public class loadSkillsSelect : MonoBehaviour
groupsByColumn[g.columnID].Add(g);
}
// columnID排序
// columnID
var sortedColumns = groupsByColumn.Keys.ToList();
sortedColumns.Sort();
@@ -136,47 +154,69 @@ public class loadSkillsSelect : MonoBehaviour
var columnGroups = groupsByColumn[colId];
var columnName = columnGroups.Count > 0 ? columnGroups[0].columnName : $"Column {colId}";
// 创建column容器
var columnContainer = new GameObject($"Column_{colId}");
columnContainer.transform.SetParent(contentParent, false);
var vlg = columnContainer.AddComponent<UnityEngine.UI.VerticalLayoutGroup>();
vlg.spacing = columnVerticalSpacing; // 垂直间距,可调整
vlg.childAlignment = TextAnchor.UpperLeft; // 对齐方式
vlg.childControlWidth = true; // 控制子元素宽度
vlg.childControlHeight = false; // 不控制子元素高度
vlg.childForceExpandWidth = false; // 不强制扩展宽度
vlg.childForceExpandHeight = false; // 不强制扩展高度
// 如果不是以特殊字符开头(即以汉字、英文或数字开头),则加入两个空格
string displayColumnName = columnName;
if (!string.IsNullOrEmpty(columnName))
{
char firstChar = columnName[0];
bool isNormalStart = char.IsLetterOrDigit(firstChar) || (firstChar >= 0x4e00 && firstChar <= 0x9fa5);
if (isNormalStart)
{
displayColumnName = " " + columnName;
}
}
// 实例化栏目标题
// 实例化column容器
var columnContainer = new GameObject($"Column_{colId}", typeof(RectTransform));
columnContainer.transform.SetParent(contentParent, false);
// 应用RectTransform参数
RectTransform colRt = columnContainer.GetComponent<RectTransform>();
colRt.sizeDelta = new Vector2(columnWidth, colRt.sizeDelta.y);
colRt.anchoredPosition = new Vector2(columnPosX, colRt.anchoredPosition.y);
var vlg = columnContainer.AddComponent<UnityEngine.UI.VerticalLayoutGroup>();
vlg.spacing = columnVerticalSpacing; // ֱɵ
vlg.childAlignment = TextAnchor.UpperLeft; // 뷽ʽ
vlg.childControlWidth = true; // Ԫؿ
vlg.childControlHeight = false; // Ԫظ߶
vlg.childForceExpandWidth = false; // ǿչ
vlg.childForceExpandHeight = false; // ǿչ߶
// ʵĿ
if (columnHeaderPrefab != null)
{
var headerGo = Instantiate(columnHeaderPrefab, columnContainer.transform);
var text = headerGo.GetComponent<Text>();
if (text != null)
{
text.text = columnName;
text.text = displayColumnName;
}
else
{
var tmpText = headerGo.GetComponent<TMPro.TextMeshProUGUI>();
if (tmpText != null)
{
tmpText.text = columnName;
tmpText.text = displayColumnName;
}
}
}
// 创建技能容器
//
var skillsContainer = new GameObject("SkillsContainer");
skillsContainer.transform.SetParent(columnContainer.transform, false);
var hlg = skillsContainer.AddComponent<UnityEngine.UI.HorizontalLayoutGroup>();
hlg.spacing = columnSpacing; // 设置间距
hlg.childAlignment = columnChildAlignment; // 对齐方式
hlg.childControlWidth = columnChildControlWidth; // 控制子元素宽度
hlg.childControlHeight = columnChildControlHeight; // 控制子元素高度
hlg.padding = new UnityEngine.RectOffset(columnPaddingLeft, columnPaddingRight, columnPaddingTop, columnPaddingBottom); // 设置padding
hlg.spacing = columnSpacing; // ü
hlg.childAlignment = columnChildAlignment; // 뷽ʽ
hlg.childControlWidth = columnChildControlWidth; // Ԫؿ
hlg.childControlHeight = columnChildControlHeight; // Ԫظ߶
hlg.childScaleWidth = columnChildScaleWidth;
hlg.childScaleHeight = columnChildScaleHeight;
hlg.childForceExpandWidth = columnChildForceExpandWidth;
hlg.childForceExpandHeight = columnChildForceExpandHeight;
hlg.padding = new UnityEngine.RectOffset(columnPaddingLeft, columnPaddingRight, columnPaddingTop, columnPaddingBottom); // padding
// 实例化该栏目的技能卡片
// ʵĿļܿƬ
foreach (var g in columnGroups)
{
if (g == null) continue;
@@ -213,12 +253,12 @@ public class loadSkillsSelect : MonoBehaviour
if (skillCardUseLayoutElement)
{
var le = go.GetComponent<UnityEngine.UI.LayoutElement>() ?? go.AddComponent<UnityEngine.UI.LayoutElement>();
if (skillCardPreferredWidth >= 0) le.preferredWidth = skillCardPreferredWidth;
if (skillCardPreferredHeight >= 0) le.preferredHeight = skillCardPreferredHeight;
if (skillCardFlexibleWidth >= 0) le.flexibleWidth = skillCardFlexibleWidth;
if (skillCardFlexibleHeight >= 0) le.flexibleHeight = skillCardFlexibleHeight;
if (skillCardMinWidth >= 0) le.minWidth = skillCardMinWidth;
if (skillCardMinHeight >= 0) le.minHeight = skillCardMinHeight;
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;
}
// use the existing Button component on the skill card
@@ -18,16 +18,20 @@ public class loadTeam_select : MonoBehaviour
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
LoadAllRoleCards();
{ StartCoroutine(LoadAllRoleCardsRoutine());
}
public void LoadAllRoleCards()
{
StartCoroutine(LoadAllRoleCardsRoutine());
}
private System.Collections.IEnumerator LoadAllRoleCardsRoutine()
{
if (roleCardPrefab == null || contentParent == null)
{
Debug.LogError("loadTeam_select: roleCardPrefab or contentParent not assigned");
return;
yield break;
}
// clear existing
@@ -96,8 +100,9 @@ public class loadTeam_select : MonoBehaviour
Debug.Log($"loadTeam_select: discovered {found.Count} AllyHero_SO entries");
// instantiate in left-to-right order in the contentParent
foreach (var so in found)
for (int i = 0; i < found.Count; i++)
{
var so = found[i];
if (so == null) continue;
var go = Instantiate(roleCardPrefab, contentParent);
go.transform.SetParent(contentParent, false);
@@ -122,6 +127,12 @@ public class loadTeam_select : MonoBehaviour
{
ctrl.roleCard_bottomImage.color = ctrl.bottomImage_colors[colorIdx];
}
// 每实例化 2 个卡片等待一帧,避免瞬时大量实例化导致的掉帧
if (i % 2 == 0)
{
yield return null;
}
}
}
@@ -2,8 +2,10 @@ using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using DG.Tweening;
public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler,
IPointerEnterHandler, IPointerExitHandler
{
[Header("Hero Slots' Prefabs Necessary Info")]
public int heroSlot_slotID;
@@ -33,7 +35,7 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
public GameObject skillCardPrefab; // Prefab for skill cards, should have slots_skillSlots component
[Header("等级图片CBAS")]
[Header("等级图片CBAS")]
public Sprite levelIcon_sprite_C;
public Sprite levelIcon_sprite_B;
public Sprite levelIcon_sprite_A;
@@ -41,10 +43,35 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
private Canvas rootCanvas;
private CanvasGroup canvasGroup;
private LayoutElement layoutElement;
[Header("Hover Anim")]
[SerializeField] bool enableHoverAnim = true;
[SerializeField] float hoverScale = 1.04f;
[SerializeField] float hoverTweenTime = 0.12f;
[SerializeField] float hoverExpand = 0.18f;
[SerializeField] float hoverCompress = 0.08f;
[Header("Change Anim")]
[SerializeField] float changeMoveOffset = 40f;
[SerializeField] float changeMoveTime = 0.18f;
[SerializeField] float changeBlinkTime = 0.05f;
class LayoutInfo
{
public slots_heroSlots slot;
public LayoutElement layout;
public float baseSize;
}
static readonly Dictionary<Transform, List<LayoutInfo>> layoutCache = new();
static readonly Dictionary<Transform, bool> layoutAxisIsHorizontal = new();
void Start()
{
rootCanvas = GetComponentInParent<Canvas>();
layoutElement = GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
if (heroImage != null) heroImage.raycastTarget = true;
// Ensure header colors have visible alpha on start
if (headerImage_colors != null)
{
@@ -123,11 +150,15 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
}
}
// 缓存 AllyHero_SO 数组,避免在拖放操作时多次调用昂贵的 Resources.LoadAll
private static AllyHero_SO[] _cachedAllyHeroSOs;
// Accept drops from CharacterCardView (role items) or roleCard_prefabController
public void OnDrop(PointerEventData eventData)
{
Debug.Log($"slots_heroSlots OnDrop on {gameObject.name}, pointerDrag={eventData.pointerDrag?.name}");
if (eventData.pointerDrag == null) return;
int prevId = heroSlot_heroID;
// accept CharacterCardView or roleCard_prefabController as sources (check parents as well)
var sourceCard = eventData.pointerDrag.GetComponent<CharacterCardView>() ?? eventData.pointerDrag.GetComponentInParent<CharacterCardView>();
@@ -144,6 +175,11 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
if (sourceSlot != null && sourceSlot != this)
{
SwapWith(sourceSlot);
if (prevId != heroSlot_heroID)
{
PlaySlotChangeAnim();
TeamSelectorAnimController.NotifyTeamChanged();
}
return;
}
@@ -167,8 +203,11 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
AllyHero_SO found = null;
try
{
var arr = Resources.LoadAll<AllyHero_SO>("");
foreach (var a in arr)
if (_cachedAllyHeroSOs == null || _cachedAllyHeroSOs.Length == 0)
{
_cachedAllyHeroSOs = Resources.LoadAll<AllyHero_SO>("");
}
foreach (var a in _cachedAllyHeroSOs)
{
if (a != null && a.ally_heroID == heroSlot_heroID)
{
@@ -201,7 +240,7 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
heroImage.color = new Color(heroImage.color.r, heroImage.color.g, heroImage.color.b, 1f);
}
// 设置等级图标
// 设置等级图标
var levelInfo = found.GetEffectiveLevelForCurrentEXP();
if (levelInfo != null && heroLevelImage != null)
{
@@ -250,6 +289,11 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
// Update the skill list after setting the hero
UpdateSkillList();
if (prevId != heroSlot_heroID)
{
PlaySlotChangeAnim();
TeamSelectorAnimController.NotifyTeamChanged();
}
// Update and save selected heroes
if (newTeamSelector.Instance != null)
@@ -271,7 +315,7 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
{
int prevId = heroSlot_heroID; // remember previous id
heroSlot_heroID = 0;
if (heroSlot_heroName != null) heroSlot_heroName.text = "空置";
if (heroSlot_heroName != null) heroSlot_heroName.text = "空置";
if (heroImage != null)
{
// revert to default sprite if provided, otherwise keep transparent
@@ -306,6 +350,11 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
{
newTeamSelector.Instance.UpdateAndSaveSelectedHeroes();
}
if (prevId != heroSlot_heroID)
{
PlaySlotChangeAnim();
TeamSelectorAnimController.NotifyTeamChanged();
}
}
// Drag between slots only
@@ -371,7 +420,7 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
if (heroImage != null) heroImage.sprite = sprite;
if (heroImage != null) heroImage.color = sprite != null ? new Color(heroImage.color.r, heroImage.color.g, heroImage.color.b, 1f) : new Color(heroImage.color.r, heroImage.color.g, heroImage.color.b, 0f);
// 设置等级图标
// 设置等级图标
if (id != 0)
{
var hero = FindHeroById(id);
@@ -459,9 +508,114 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
}
}
}
TeamSelectorAnimController.PlaySkillsFlash(skillContainer != null ? skillContainer.transform as RectTransform : null);
}
// 辅助方法:根据ID查找英雄SO
public void OnPointerEnter(PointerEventData eventData)
{
if (!enableHoverAnim) return;
ApplyHoverLayout(true);
transform.DOKill();
transform.DOScale(hoverScale, hoverTweenTime).SetEase(Ease.OutCubic);
}
public void OnPointerExit(PointerEventData eventData)
{
if (!enableHoverAnim) return;
ApplyHoverLayout(false);
transform.DOKill();
transform.DOScale(1f, hoverTweenTime).SetEase(Ease.OutCubic);
}
void ApplyHoverLayout(bool active)
{
Transform parent = transform.parent;
if (parent == null) return;
List<LayoutInfo> group = GetOrCacheGroup(parent);
if (group == null || group.Count == 0) return;
bool horizontal = layoutAxisIsHorizontal.TryGetValue(parent, out bool h) ? h : true;
for (int i = 0; i < group.Count; i++)
{
LayoutInfo info = group[i];
if (info == null || info.layout == null) continue;
float baseSize = info.baseSize;
float target = baseSize;
if (active)
{
target = info.slot == this
? baseSize * (1f + hoverExpand)
: baseSize * Mathf.Max(0.1f, 1f - hoverCompress);
}
if (horizontal)
info.layout.preferredWidth = target;
else
info.layout.preferredHeight = target;
}
ForceRebuildParentLayout();
}
List<LayoutInfo> GetOrCacheGroup(Transform parent)
{
if (layoutCache.TryGetValue(parent, out var cached) && cached != null && cached.Count > 0)
{
return cached;
}
bool horizontal = parent.GetComponent<HorizontalLayoutGroup>() != null;
bool vertical = parent.GetComponent<VerticalLayoutGroup>() != null;
layoutAxisIsHorizontal[parent] = !vertical || horizontal;
var list = new List<LayoutInfo>();
slots_heroSlots[] slots = parent.GetComponentsInChildren<slots_heroSlots>(true);
for (int i = 0; i < slots.Length; i++)
{
var slot = slots[i];
if (slot == null) continue;
var le = slot.layoutElement ?? slot.GetComponent<LayoutElement>() ?? slot.gameObject.AddComponent<LayoutElement>();
RectTransform rt = slot.transform as RectTransform;
float baseSize = 100f;
if (rt != null)
{
baseSize = horizontal ? rt.rect.width : rt.rect.height;
}
if (horizontal)
le.preferredWidth = baseSize;
else
le.preferredHeight = baseSize;
list.Add(new LayoutInfo { slot = slot, layout = le, baseSize = baseSize });
}
layoutCache[parent] = list;
return list;
}
void ForceRebuildParentLayout()
{
if (transform.parent == null) return;
RectTransform prt = transform.parent as RectTransform;
if (prt != null)
LayoutRebuilder.ForceRebuildLayoutImmediate(prt);
}
void PlaySlotChangeAnim()
{
RectTransform rt = transform as RectTransform;
if (rt == null) return;
CanvasGroup cg = GetComponent<CanvasGroup>() ?? gameObject.AddComponent<CanvasGroup>();
rt.DOKill();
cg.DOKill();
Vector2 basePos = rt.anchoredPosition;
rt.anchoredPosition = basePos + new Vector2(0f, -changeMoveOffset);
cg.alpha = 0f;
Sequence seq = DOTween.Sequence();
seq.Join(rt.DOAnchorPos(basePos, changeMoveTime).SetEase(Ease.OutCubic));
seq.Join(cg.DOFade(1f, changeMoveTime));
seq.Append(cg.DOFade(0f, changeBlinkTime));
seq.Append(cg.DOFade(1f, changeBlinkTime));
}
// 辅助方法:根据ID查找英雄SO
private AllyHero_SO FindHeroById(int id)
{
var arr = Resources.LoadAll<AllyHero_SO>("");
@@ -472,7 +626,7 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
return null;
}
// 获取等级对应的sprite
// 获取等级对应的sprite
private Sprite GetLevelSprite(string level)
{
switch (level)
@@ -486,12 +640,12 @@ public class slots_heroSlots : MonoBehaviour, IDropHandler, IBeginDragHandler, I
case "S": // S
return levelIcon_sprite_S;
default:
Debug.LogWarning($"未找到对应等级贴图,当前等级为:{level}");
Debug.LogWarning($"未找到对应等级贴图,当前等级为:{level}");
return null;
}
}
// 获取角色等级字符串(与loadTeam_select.cs一致)
// 获取角色等级字符串(与loadTeam_select.cs一致)
private string GetRatingFromSO(AllyHero_SO so)
{
if (so == null || so.levelStats == null || so.levelStats.Count == 0) return "C";
@@ -8,7 +8,7 @@ using System.Linq;
public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnterHandler, IPointerExitHandler
{
[Header("技能卡片内容")]
[Header("ܿƬ")]
public Image skillCard_bottomImage;
public Text skillCard_skillName;
public int skillCard_skillID;
@@ -16,7 +16,7 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
public string skillCard_skillDescription;
public Button skillCard_button;
[Header("技能状态改变背景颜色")]
[Header("״̬ı䱳ɫ")]
[Tooltip("Color when the skill is available")]
public Color skillColor_available = Color.white;
@@ -35,7 +35,7 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
// hero ID to modify the corresponding SO
public int heroId;
// 是否只读模式(用于英雄槽位内的技能显示)
// ǷֻģʽӢ۲λڵļʾ
public bool isReadOnly = false;
void Start()
@@ -51,12 +51,12 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
private AllyHero_SO GetHeroSO()
{
// 优先使用loadSkillsSelectcurrentHeroSO
// ʹloadSkillsSelectcurrentHeroSO
if (loadSkillsSelect.Instance != null && loadSkillsSelect.Instance.currentHeroSO != null)
{
return loadSkillsSelect.Instance.currentHeroSO;
}
// 备用:通过heroId查找
// ãͨheroId
var arr = Resources.LoadAll<AllyHero_SO>("");
foreach (var a in arr)
{
@@ -65,42 +65,42 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
return null;
}
// 新增:统一切换选中状态的方法,供按钮或点击事件调用
// ͳһлѡ״̬ķť¼
public void ToggleSelect()
{
var so = GetHeroSO();
if (so == null)
{
Debug.LogWarning($"slots_skillSlots.ToggleSelect: 未找到 hero SO, heroId={heroId}");
Debug.LogWarning($"slots_skillSlots.ToggleSelect: δҵ hero SO, heroId={heroId}");
}
// 检查是否可以选择(如果要选择且未选中)
// ǷѡҪѡδѡУ
if (!isSelected)
{
// 获取当前等级的技能槽位限制
// ȡǰȼļܲλ
var levelInfo = so?.GetEffectiveLevelForCurrentEXP();
int maxSlots = levelInfo?.skill_slot_limited ?? int.MaxValue; // 如果未设置,默认无限制
int maxSlots = levelInfo?.skill_slot_limited ?? int.MaxValue; // δãĬ
int currentEquipped = so?.equippedSkillGroupIDs.Where(id => id != 0).Count() ?? 0;
if (currentEquipped >= maxSlots)
{
Debug.LogWarning($"slots_skillSlots.ToggleSelect: 超过技能槽位限制 ({currentEquipped}/{maxSlots}), 无法选择");
return; // 不切换状态
Debug.LogWarning($"slots_skillSlots.ToggleSelect: ܲλ ({currentEquipped}/{maxSlots}), ޷ѡ");
return; // л״̬
}
// 获取技能组的等级限制
// ȡĵȼ
var skillGroup = so?.GetSkillGroupByID(skillCard_skillID);
int requiredLevel = skillGroup?.thisSkill_levelLimit ?? 1;
int currentLevel = levelInfo?.levelID ?? 0;
if (currentLevel < requiredLevel)
{
Debug.LogWarning($"slots_skillSlots.ToggleSelect: 等级不足 ({currentLevel}/{requiredLevel}), 无法选择技能组 {skillCard_skillID}");
return; // 不切换状态
Debug.LogWarning($"slots_skillSlots.ToggleSelect: ȼ ({currentLevel}/{requiredLevel}), ޷ѡ {skillCard_skillID}");
return; // л״̬
}
}
// 切换状态
// л״̬
isSelected = !isSelected;
ApplyState();
@@ -130,7 +130,7 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
}
// Notify all slots_heroSlots with matching heroId to update their skill lists
var allSlots = FindObjectsOfType<slots_heroSlots>();
var allSlots = Object.FindObjectsByType<slots_heroSlots>(FindObjectsInactive.Exclude, FindObjectsSortMode.InstanceID);
foreach (var slot in allSlots)
{
if (slot.heroSlot_heroID == heroId)
@@ -152,10 +152,10 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
#endif
}
// 鼠标左/右键:左键选中(或切换),右键仅在选中时取消
// /ҼѡУлҼѡʱȡ
public void OnPointerClick(PointerEventData eventData)
{
if (isReadOnly) return; // 只读模式下不允许点击切换
if (isReadOnly) return; // ֻģʽ²л
if (eventData.button == PointerEventData.InputButton.Left)
{
@@ -165,28 +165,28 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
{
if (isSelected)
{
ToggleSelect(); // 右键在选中时取消选中
ToggleSelect(); // Ҽѡʱȡѡ
}
}
}
// 鼠标悬停进入:显示详细信息
// ͣʾϸϢ
public void OnPointerEnter(PointerEventData eventData)
{
if (loadDetailsPrefab.Instance == null) return;
// 获取英雄SO
// ȡӢSO
AllyHero_SO hero = FindHeroById(heroId);
if (hero == null) return;
// 获取技能组
// ȡ
SkillGroup group = hero.GetSkillGroupByID(skillCard_skillID);
if (group == null) return;
string type = "技能";
string name = group.groupName ?? "未知技能";
string type = "";
string name = group.groupName ?? "δ֪";
// 检查技能可用性
// ܿ
string status;
var levelInfo = hero.GetEffectiveLevelForCurrentEXP();
int maxSlots = levelInfo?.skill_slot_limited ?? int.MaxValue;
@@ -197,27 +197,27 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
if (currentLevel < requiredLevel)
{
string levelName = GetLevelName(requiredLevel);
status = $"需要等级{levelName}";
status = $"Ҫȼ{levelName}";
}
else if (currentEquipped >= maxSlots)
{
status = "技能已达上限";
status = "Ѵ";
}
else if (isSelected)
{
status = "技能已选择";
status = "ѡ";
}
else
{
status = "技能可用";
status = "ܿ";
}
string description = group.skillDescriptionsText ?? "无描述";
string description = group.skillDescriptionsText ?? "";
loadDetailsPrefab.Instance.ShowDetails(type, name, status, description, Input.mousePosition);
}
// 鼠标悬停离开:隐藏详细信息
// ͣϸϢ
public void OnPointerExit(PointerEventData eventData)
{
if (loadDetailsPrefab.Instance != null)
@@ -226,7 +226,7 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
}
}
// 辅助方法:根据ID查找英雄SO
// IDӢSO
private AllyHero_SO FindHeroById(int id)
{
var arr = Resources.LoadAll<AllyHero_SO>("");
@@ -237,7 +237,7 @@ public class slots_skillSlots : MonoBehaviour, IPointerClickHandler, IPointerEnt
return null;
}
// 辅助方法:根据levelID获取等级名称
// levelIDȡȼ
private string GetLevelName(int levelID)
{
switch (levelID)