技能主要更新,修复卡顿并加入动画,以及各种其他更新。
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler,
|
||||
IPointerEnterHandler, IPointerExitHandler, IPointerMoveHandler
|
||||
{
|
||||
public int characterID;
|
||||
public string currentBoundaryLevel;
|
||||
@@ -19,12 +21,38 @@ public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler,
|
||||
private GameObject dragGhost;
|
||||
private Canvas rootCanvas;
|
||||
private CanvasGroup canvasGroup;
|
||||
private LayoutElement layoutElement;
|
||||
|
||||
[Header("Hover Anim")]
|
||||
[SerializeField] bool enableHoverAnim = true;
|
||||
[SerializeField] float hoverScale = 1.06f;
|
||||
[SerializeField] float hoverTweenTime = 0.12f;
|
||||
[SerializeField] float hoverExpand = 0.18f;
|
||||
[SerializeField] float hoverCompress = 0.08f;
|
||||
[SerializeField] float gyroMaxAngle = 6f;
|
||||
[SerializeField] float gyroFollow = 8f;
|
||||
|
||||
private bool isHovering;
|
||||
private bool isDragging;
|
||||
private Camera lastEventCamera;
|
||||
|
||||
class LayoutInfo
|
||||
{
|
||||
public CharacterCardView card;
|
||||
public LayoutElement layout;
|
||||
public float baseSize;
|
||||
}
|
||||
|
||||
static readonly Dictionary<Transform, List<LayoutInfo>> layoutCache = new();
|
||||
static readonly Dictionary<Transform, bool> layoutAxisIsHorizontal = new();
|
||||
|
||||
void Start()
|
||||
{
|
||||
charactCard = gameObject;
|
||||
rootCanvas = GetComponentInParent<Canvas>();
|
||||
canvasGroup = GetComponent<CanvasGroup>() ?? gameObject.AddComponent<CanvasGroup>();
|
||||
layoutElement = GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
|
||||
if (characterImage != null) characterImage.raycastTarget = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// 未在队中的卡点击后进入被操纵状态。若在队伍中默认进入已经入队状态
|
||||
@@ -59,6 +87,13 @@ public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler,
|
||||
// prevent the original from blocking raycasts so drop targets can receive events
|
||||
if (canvasGroup == null) canvasGroup = GetComponent<CanvasGroup>() ?? gameObject.AddComponent<CanvasGroup>();
|
||||
canvasGroup.blocksRaycasts = false;
|
||||
canvasGroup.alpha = 0f;
|
||||
isDragging = true;
|
||||
ApplyHoverLayout(false);
|
||||
StopHoverEffects();
|
||||
if (layoutElement == null) layoutElement = GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
|
||||
layoutElement.ignoreLayout = true;
|
||||
ForceRebuildParentLayout();
|
||||
|
||||
// ensure pointerDrag references this object for drop handlers
|
||||
eventData.pointerDrag = gameObject;
|
||||
@@ -93,6 +128,12 @@ public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler,
|
||||
// restore raycast blocking on the original
|
||||
if (canvasGroup != null)
|
||||
canvasGroup.blocksRaycasts = true;
|
||||
if (canvasGroup != null)
|
||||
canvasGroup.alpha = 1f;
|
||||
if (layoutElement != null)
|
||||
layoutElement.ignoreLayout = false;
|
||||
isDragging = false;
|
||||
ForceRebuildParentLayout();
|
||||
}
|
||||
|
||||
private void UpdateGhostPosition(PointerEventData eventData)
|
||||
@@ -106,4 +147,125 @@ public class CharacterCardView : MonoBehaviour, IBeginDragHandler, IDragHandler,
|
||||
if (dragGhost != null)
|
||||
dragGhost.transform.localPosition = localPoint;
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (!enableHoverAnim || isDragging) return;
|
||||
isHovering = true;
|
||||
lastEventCamera = eventData.enterEventCamera;
|
||||
ApplyHoverLayout(true);
|
||||
transform.DOKill();
|
||||
transform.DOScale(hoverScale, hoverTweenTime).SetEase(Ease.OutCubic);
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
if (!enableHoverAnim) return;
|
||||
isHovering = false;
|
||||
ApplyHoverLayout(false);
|
||||
StopHoverEffects();
|
||||
}
|
||||
|
||||
public void OnPointerMove(PointerEventData eventData)
|
||||
{
|
||||
if (!enableHoverAnim) return;
|
||||
lastEventCamera = eventData.enterEventCamera;
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!enableHoverAnim || !isHovering || isDragging) return;
|
||||
RectTransform rt = transform as RectTransform;
|
||||
if (rt == null) return;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, lastEventCamera, out Vector2 localPoint))
|
||||
{
|
||||
return;
|
||||
}
|
||||
Vector2 size = rt.rect.size;
|
||||
if (size.x <= 0f || size.y <= 0f) return;
|
||||
Vector2 norm = new Vector2(localPoint.x / size.x, localPoint.y / size.y);
|
||||
norm = Vector2.ClampMagnitude(norm * 2f, 1f);
|
||||
float xRot = -norm.y * gyroMaxAngle;
|
||||
float yRot = norm.x * gyroMaxAngle;
|
||||
Quaternion target = Quaternion.Euler(xRot, yRot, 0f);
|
||||
transform.localRotation = Quaternion.Slerp(transform.localRotation, target, Time.unscaledDeltaTime * gyroFollow);
|
||||
}
|
||||
|
||||
void StopHoverEffects()
|
||||
{
|
||||
transform.DOKill();
|
||||
transform.DOScale(1f, hoverTweenTime).SetEase(Ease.OutCubic);
|
||||
transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
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.card == 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>();
|
||||
CharacterCardView[] cards = parent.GetComponentsInChildren<CharacterCardView>(true);
|
||||
for (int i = 0; i < cards.Length; i++)
|
||||
{
|
||||
var card = cards[i];
|
||||
if (card == null) continue;
|
||||
var le = card.layoutElement ?? card.GetComponent<LayoutElement>() ?? card.gameObject.AddComponent<LayoutElement>();
|
||||
RectTransform rt = card.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 { card = card, 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,563 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
public class TeamSelectorAnimController : MonoBehaviour
|
||||
{
|
||||
public static TeamSelectorAnimController Instance { get; private set; }
|
||||
|
||||
[Header("Roots")]
|
||||
[SerializeField] RectTransform teamCanvas;
|
||||
[SerializeField] RectTransform leftSlotsContainer;
|
||||
[SerializeField] RectTransform rightTopPanel;
|
||||
[SerializeField] RectTransform rightBottomPanel;
|
||||
[SerializeField] RectTransform confirmButton;
|
||||
[SerializeField] RectTransform titleText;
|
||||
[SerializeField] RectTransform smallCardsContainer;
|
||||
|
||||
[Header("Timing")]
|
||||
[SerializeField] bool useUnscaled = true;
|
||||
[SerializeField] float panelEnterOffset = 200f;
|
||||
[SerializeField] float panelEnterTime = 0.35f;
|
||||
[SerializeField] float panelStagger = 0.08f;
|
||||
[SerializeField] float slotEnterOffset = 140f;
|
||||
[SerializeField] float slotEnterTime = 0.3f;
|
||||
[SerializeField] float slotStagger = 0.06f;
|
||||
[SerializeField] float confirmEnterOffset = 120f;
|
||||
[SerializeField] float confirmEnterTime = 0.3f;
|
||||
[SerializeField] float titleBlinkTime = 0.06f;
|
||||
[SerializeField] int titleBlinkCount = 2;
|
||||
[SerializeField] float cardBlinkTime = 0.05f;
|
||||
[SerializeField] int cardBlinkCount = 2;
|
||||
[SerializeField] float skillsBlinkTime = 0.05f;
|
||||
[SerializeField] int skillsBlinkCount = 2;
|
||||
|
||||
bool teamChanged;
|
||||
int[] cachedTeam = new int[5];
|
||||
bool playRequested;
|
||||
|
||||
static bool pendingTeamChanged;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance == null) Instance = this;
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (Instance == null) Instance = this;
|
||||
CacheTeamSnapshot();
|
||||
if (pendingTeamChanged) teamChanged = true;
|
||||
pendingTeamChanged = false;
|
||||
BindConfirmButton();
|
||||
if (!playRequested)
|
||||
{
|
||||
playRequested = true;
|
||||
StartCoroutine(PlayOpenAnim());
|
||||
}
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
playRequested = false;
|
||||
if (HasTeamChanged())
|
||||
{
|
||||
FlashTeammatesProfiles();
|
||||
}
|
||||
teamChanged = false;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
public static void NotifyTeamChanged()
|
||||
{
|
||||
pendingTeamChanged = true;
|
||||
if (Instance != null) Instance.teamChanged = true;
|
||||
}
|
||||
|
||||
public static void PlaySkillsFlash(RectTransform container = null)
|
||||
{
|
||||
if (Instance == null) return;
|
||||
Instance.PlaySkillsFlashInternal(container);
|
||||
}
|
||||
|
||||
IEnumerator PlayOpenAnim()
|
||||
{
|
||||
yield return null; // wait one frame for layout
|
||||
EnsureRefs();
|
||||
if (teamCanvas != null && teamCanvas.localScale == Vector3.zero)
|
||||
{
|
||||
teamCanvas.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
AnimatePanel(rightTopPanel, panelEnterOffset, 0f);
|
||||
AnimatePanel(rightBottomPanel, panelEnterOffset, panelStagger);
|
||||
AnimateConfirm(confirmButton);
|
||||
FlickerTitle();
|
||||
|
||||
StartCoroutine(AnimateSlots());
|
||||
StartCoroutine(AnimateSmallCards());
|
||||
|
||||
// skills flash after bottom panel enters
|
||||
float delay = panelEnterTime + panelStagger + 0.05f;
|
||||
DOVirtual.DelayedCall(delay, () => PlaySkillsFlashInternal(null)).SetUpdate(useUnscaled);
|
||||
}
|
||||
|
||||
void EnsureRefs()
|
||||
{
|
||||
if (teamCanvas == null)
|
||||
{
|
||||
var child = transform.Find("teamCanvas");
|
||||
teamCanvas = child as RectTransform;
|
||||
}
|
||||
if (teamCanvas != null && teamCanvas.localScale == Vector3.zero)
|
||||
{
|
||||
teamCanvas.localScale = Vector3.one;
|
||||
}
|
||||
|
||||
if (leftSlotsContainer == null)
|
||||
{
|
||||
if (newTeamSelector.Instance != null && newTeamSelector.Instance.container != null)
|
||||
leftSlotsContainer = newTeamSelector.Instance.container;
|
||||
else if (teamCanvas != null)
|
||||
leftSlotsContainer = FindChildByName(teamCanvas, "horizontal");
|
||||
}
|
||||
|
||||
if (rightTopPanel == null && teamCanvas != null)
|
||||
rightTopPanel = FindChildByName(teamCanvas, "selectBase_hero");
|
||||
if (rightBottomPanel == null && teamCanvas != null)
|
||||
rightBottomPanel = FindChildByName(teamCanvas, "selectBase_skills");
|
||||
if (confirmButton == null && teamCanvas != null)
|
||||
confirmButton = FindChildByName(teamCanvas, "confirmTeamSelecting");
|
||||
if (titleText == null && teamCanvas != null)
|
||||
titleText = FindChildByName(teamCanvas, "titleTXT");
|
||||
|
||||
if (smallCardsContainer == null && rightTopPanel != null)
|
||||
{
|
||||
smallCardsContainer = FindSmallCardsContainer(rightTopPanel);
|
||||
}
|
||||
}
|
||||
|
||||
void CacheTeamSnapshot()
|
||||
{
|
||||
cachedTeam[0] = PlayerPrefs.GetInt("selected_heroSlot01_heroID", 0);
|
||||
cachedTeam[1] = PlayerPrefs.GetInt("selected_heroSlot02_heroID", 0);
|
||||
cachedTeam[2] = PlayerPrefs.GetInt("selected_heroSlot03_heroID", 0);
|
||||
cachedTeam[3] = PlayerPrefs.GetInt("selected_heroSlot04_heroID", 0);
|
||||
cachedTeam[4] = PlayerPrefs.GetInt("selected_heroSlot05_heroID", 0);
|
||||
}
|
||||
|
||||
bool HasTeamChanged()
|
||||
{
|
||||
if (teamChanged) return true;
|
||||
if (cachedTeam[0] != PlayerPrefs.GetInt("selected_heroSlot01_heroID", 0)) return true;
|
||||
if (cachedTeam[1] != PlayerPrefs.GetInt("selected_heroSlot02_heroID", 0)) return true;
|
||||
if (cachedTeam[2] != PlayerPrefs.GetInt("selected_heroSlot03_heroID", 0)) return true;
|
||||
if (cachedTeam[3] != PlayerPrefs.GetInt("selected_heroSlot04_heroID", 0)) return true;
|
||||
if (cachedTeam[4] != PlayerPrefs.GetInt("selected_heroSlot05_heroID", 0)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerator AnimateSlots()
|
||||
{
|
||||
List<RectTransform> slotRects = GetSlotRects();
|
||||
int tries = 0;
|
||||
while ((slotRects == null || slotRects.Count == 0) && tries < 10)
|
||||
{
|
||||
tries++;
|
||||
yield return null;
|
||||
slotRects = GetSlotRects();
|
||||
}
|
||||
if (slotRects == null || slotRects.Count == 0) yield break;
|
||||
|
||||
LayoutGroup layout = leftSlotsContainer != null ? leftSlotsContainer.GetComponent<LayoutGroup>() : null;
|
||||
bool layoutEnabled = layout != null && layout.enabled;
|
||||
if (leftSlotsContainer != null)
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(leftSlotsContainer);
|
||||
|
||||
slotRects.Sort((a, b) => a.position.y.CompareTo(b.position.y));
|
||||
|
||||
if (layoutEnabled) layout.enabled = false;
|
||||
|
||||
for (int i = 0; i < slotRects.Count; i++)
|
||||
{
|
||||
RectTransform rt = slotRects[i];
|
||||
if (rt == null) continue;
|
||||
Vector2 basePos = rt.anchoredPosition;
|
||||
rt.DOKill();
|
||||
rt.anchoredPosition = basePos + new Vector2(0f, -slotEnterOffset);
|
||||
rt.DOAnchorPos(basePos, slotEnterTime)
|
||||
.SetEase(Ease.OutCubic)
|
||||
.SetDelay(i * slotStagger)
|
||||
.SetUpdate(useUnscaled)
|
||||
.SetLink(rt.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
|
||||
float total = slotEnterTime + slotStagger * Mathf.Max(0, slotRects.Count - 1);
|
||||
float t = 0f;
|
||||
while (t < total)
|
||||
{
|
||||
t += useUnscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
if (layoutEnabled) layout.enabled = true;
|
||||
if (leftSlotsContainer != null)
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(leftSlotsContainer);
|
||||
}
|
||||
|
||||
IEnumerator AnimateSmallCards()
|
||||
{
|
||||
if (smallCardsContainer == null)
|
||||
EnsureRefs();
|
||||
// wait for top panel to slide in
|
||||
float t = panelEnterTime + 0.02f;
|
||||
while (t > 0f)
|
||||
{
|
||||
t -= useUnscaled ? Time.unscaledDeltaTime : Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
List<RectTransform> cards = FindSmallCards();
|
||||
if (cards.Count == 0) yield break;
|
||||
cards.Sort((a, b) => a.anchoredPosition.x.CompareTo(b.anchoredPosition.x));
|
||||
|
||||
for (int i = 0; i < cards.Count; i++)
|
||||
{
|
||||
RectTransform rt = cards[i];
|
||||
if (rt == null) continue;
|
||||
if (rt.GetComponent<TeamSelectorMiniCardHover>() == null)
|
||||
rt.gameObject.AddComponent<TeamSelectorMiniCardHover>();
|
||||
EnsureRaycastTarget(rt);
|
||||
CanvasGroup cg = EnsureCanvasGroup(rt);
|
||||
if (cg == null) continue;
|
||||
cg.DOKill();
|
||||
cg.alpha = 0f;
|
||||
Sequence seq = DOTween.Sequence().SetUpdate(useUnscaled);
|
||||
seq.SetDelay(i * 0.04f);
|
||||
seq.Append(cg.DOFade(1f, cardBlinkTime));
|
||||
for (int b = 0; b < cardBlinkCount; b++)
|
||||
{
|
||||
seq.Append(cg.DOFade(0f, cardBlinkTime));
|
||||
seq.Append(cg.DOFade(1f, cardBlinkTime));
|
||||
}
|
||||
seq.SetLink(cg.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimatePanel(RectTransform panel, float offsetX, float delay)
|
||||
{
|
||||
if (panel == null) return;
|
||||
Vector2 basePos = panel.anchoredPosition;
|
||||
panel.DOKill();
|
||||
panel.anchoredPosition = basePos + new Vector2(offsetX, 0f);
|
||||
panel.DOAnchorPos(basePos, panelEnterTime)
|
||||
.SetEase(Ease.OutCubic)
|
||||
.SetDelay(delay)
|
||||
.SetUpdate(useUnscaled)
|
||||
.SetLink(panel.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
|
||||
void AnimateConfirm(RectTransform button)
|
||||
{
|
||||
if (button == null) return;
|
||||
Vector2 basePos = button.anchoredPosition;
|
||||
button.DOKill();
|
||||
button.anchoredPosition = basePos + new Vector2(0f, -confirmEnterOffset);
|
||||
button.DOAnchorPos(basePos, confirmEnterTime)
|
||||
.SetEase(Ease.OutCubic)
|
||||
.SetUpdate(useUnscaled)
|
||||
.SetLink(button.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
|
||||
void FlickerTitle()
|
||||
{
|
||||
if (titleText == null) return;
|
||||
CanvasGroup cg = EnsureCanvasGroup(titleText);
|
||||
if (cg == null) return;
|
||||
cg.DOKill();
|
||||
cg.alpha = 0f;
|
||||
Sequence seq = DOTween.Sequence().SetUpdate(useUnscaled);
|
||||
seq.Append(cg.DOFade(1f, titleBlinkTime));
|
||||
for (int i = 0; i < titleBlinkCount; i++)
|
||||
{
|
||||
seq.Append(cg.DOFade(0f, titleBlinkTime));
|
||||
seq.Append(cg.DOFade(1f, titleBlinkTime));
|
||||
}
|
||||
seq.SetLink(cg.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
|
||||
void PlaySkillsFlashInternal(RectTransform container)
|
||||
{
|
||||
Transform target = container != null ? container.transform : FindSkillContainer();
|
||||
if (target == null) target = rightBottomPanel != null ? rightBottomPanel.transform : null;
|
||||
if (target == null) return;
|
||||
|
||||
var skillSlots = target.GetComponentsInChildren<slots_skillSlots>(true);
|
||||
if (skillSlots != null && skillSlots.Length > 0)
|
||||
{
|
||||
for (int i = 0; i < skillSlots.Length; i++)
|
||||
{
|
||||
var slot = skillSlots[i];
|
||||
if (slot == null) continue;
|
||||
CanvasGroup cg = EnsureCanvasGroup(slot.transform as RectTransform);
|
||||
if (cg == null) continue;
|
||||
cg.DOKill();
|
||||
cg.alpha = 0f;
|
||||
Sequence seq = DOTween.Sequence().SetUpdate(useUnscaled);
|
||||
seq.SetDelay(i * 0.03f);
|
||||
seq.Append(cg.DOFade(1f, skillsBlinkTime));
|
||||
for (int b = 0; b < skillsBlinkCount; b++)
|
||||
{
|
||||
seq.Append(cg.DOFade(0f, skillsBlinkTime));
|
||||
seq.Append(cg.DOFade(1f, skillsBlinkTime));
|
||||
}
|
||||
seq.SetLink(cg.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// fallback: blink all graphics under the panel if no skill slots yet
|
||||
var graphics = target.GetComponentsInChildren<Graphic>(true);
|
||||
for (int i = 0; i < graphics.Length; i++)
|
||||
{
|
||||
Graphic g = graphics[i];
|
||||
if (g == null) continue;
|
||||
CanvasGroup cg = EnsureCanvasGroup(g.transform as RectTransform);
|
||||
if (cg == null) continue;
|
||||
cg.DOKill();
|
||||
cg.alpha = 0f;
|
||||
Sequence seq = DOTween.Sequence().SetUpdate(useUnscaled);
|
||||
seq.SetDelay(i * 0.02f);
|
||||
seq.Append(cg.DOFade(1f, skillsBlinkTime));
|
||||
for (int b = 0; b < skillsBlinkCount; b++)
|
||||
{
|
||||
seq.Append(cg.DOFade(0f, skillsBlinkTime));
|
||||
seq.Append(cg.DOFade(1f, skillsBlinkTime));
|
||||
}
|
||||
seq.SetLink(cg.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
}
|
||||
|
||||
void FlashTeammatesProfiles()
|
||||
{
|
||||
GameObject go = GameObject.Find("teammates_profilesHorizontalLayout");
|
||||
if (go == null) return;
|
||||
CanvasGroup cg = EnsureCanvasGroup(go.transform as RectTransform);
|
||||
if (cg == null) return;
|
||||
cg.DOKill();
|
||||
cg.alpha = 0f;
|
||||
Sequence seq = DOTween.Sequence().SetUpdate(useUnscaled);
|
||||
seq.Append(cg.DOFade(1f, 0.06f));
|
||||
seq.Append(cg.DOFade(0f, 0.06f));
|
||||
seq.Append(cg.DOFade(1f, 0.06f));
|
||||
seq.SetLink(cg.gameObject, LinkBehaviour.KillOnDisable);
|
||||
}
|
||||
|
||||
RectTransform FindChildByName(Transform root, string name)
|
||||
{
|
||||
if (root == null || string.IsNullOrEmpty(name)) return null;
|
||||
Transform found = root.Find(name);
|
||||
if (found != null) return found as RectTransform;
|
||||
foreach (Transform child in root.GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (child.name == name) return child as RectTransform;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
RectTransform FindSmallCardsContainer(Transform root)
|
||||
{
|
||||
if (root == null) return null;
|
||||
var all = root.GetComponentsInChildren<RectTransform>(true);
|
||||
Transform parent = null;
|
||||
int count = 0;
|
||||
for (int i = 0; i < all.Length; i++)
|
||||
{
|
||||
RectTransform rt = all[i];
|
||||
if (rt == null) continue;
|
||||
if (!rt.name.StartsWith("teamSettings_teammateProfile")) continue;
|
||||
parent = rt.parent;
|
||||
count++;
|
||||
}
|
||||
if (count > 0 && parent != null)
|
||||
{
|
||||
return parent as RectTransform;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<RectTransform> FindSmallCards()
|
||||
{
|
||||
List<RectTransform> list = new List<RectTransform>();
|
||||
if (rightTopPanel == null) return list;
|
||||
var all = rightTopPanel.GetComponentsInChildren<RectTransform>(true);
|
||||
for (int i = 0; i < all.Length; i++)
|
||||
{
|
||||
RectTransform rt = all[i];
|
||||
if (rt == null) continue;
|
||||
if (!rt.name.StartsWith("teamSettings_teammateProfile")) continue;
|
||||
list.Add(rt);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
Transform FindSkillContainer()
|
||||
{
|
||||
if (rightBottomPanel == null) return null;
|
||||
var slot = rightBottomPanel.GetComponentInChildren<slots_skillSlots>(true);
|
||||
return slot != null ? slot.transform.parent : null;
|
||||
}
|
||||
|
||||
List<RectTransform> GetSlotRects()
|
||||
{
|
||||
List<RectTransform> list = new List<RectTransform>();
|
||||
if (newTeamSelector.Instance != null)
|
||||
{
|
||||
var created = newTeamSelector.Instance.GetCreatedSlots();
|
||||
if (created != null && created.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < created.Count; i++)
|
||||
{
|
||||
if (created[i] == null) continue;
|
||||
RectTransform rt = created[i].transform as RectTransform;
|
||||
if (rt != null) list.Add(rt);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
if (leftSlotsContainer != null)
|
||||
{
|
||||
var slots = leftSlotsContainer.GetComponentsInChildren<slots_heroSlots>(true);
|
||||
for (int i = 0; i < slots.Length; i++)
|
||||
{
|
||||
if (slots[i] == null) continue;
|
||||
RectTransform rt = slots[i].transform as RectTransform;
|
||||
if (rt != null) list.Add(rt);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void BindConfirmButton()
|
||||
{
|
||||
if (confirmButton == null) return;
|
||||
Button btn = confirmButton.GetComponent<Button>();
|
||||
if (btn == null) return;
|
||||
btn.onClick.RemoveListener(OnConfirmClicked);
|
||||
btn.onClick.AddListener(OnConfirmClicked);
|
||||
}
|
||||
|
||||
void OnConfirmClicked()
|
||||
{
|
||||
if (HasTeamChanged())
|
||||
{
|
||||
FlashTeammatesProfiles();
|
||||
teamChanged = false;
|
||||
}
|
||||
}
|
||||
|
||||
CanvasGroup EnsureCanvasGroup(RectTransform rt)
|
||||
{
|
||||
if (rt == null) return null;
|
||||
CanvasGroup cg = rt.GetComponent<CanvasGroup>();
|
||||
if (cg == null)
|
||||
{
|
||||
cg = rt.gameObject.AddComponent<CanvasGroup>();
|
||||
}
|
||||
return cg;
|
||||
}
|
||||
|
||||
void EnsureRaycastTarget(RectTransform rt)
|
||||
{
|
||||
if (rt == null) return;
|
||||
Graphic g = rt.GetComponent<Graphic>();
|
||||
if (g != null) g.raycastTarget = true;
|
||||
}
|
||||
}
|
||||
|
||||
static class TeamSelectorAnimBootstrap
|
||||
{
|
||||
static bool installed;
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
|
||||
static void Reset()
|
||||
{
|
||||
installed = false;
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
static void Install()
|
||||
{
|
||||
if (installed) return;
|
||||
installed = true;
|
||||
GameObject runner = GameObject.Find("__TeamSelectorAnimRunner");
|
||||
if (runner == null)
|
||||
{
|
||||
runner = new GameObject("__TeamSelectorAnimRunner");
|
||||
Object.DontDestroyOnLoad(runner);
|
||||
}
|
||||
if (runner.GetComponent<TeamSelectorAnimRunner>() == null)
|
||||
{
|
||||
runner.AddComponent<TeamSelectorAnimRunner>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TeamSelectorAnimRunner : MonoBehaviour
|
||||
{
|
||||
void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
SceneManager.activeSceneChanged += OnActiveSceneChanged;
|
||||
TryAttach(SceneManager.GetActiveScene());
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
SceneManager.activeSceneChanged -= OnActiveSceneChanged;
|
||||
}
|
||||
|
||||
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
||||
{
|
||||
TryAttach(scene);
|
||||
}
|
||||
|
||||
void OnActiveSceneChanged(Scene from, Scene to)
|
||||
{
|
||||
TryAttach(to);
|
||||
}
|
||||
|
||||
void TryAttach(Scene scene)
|
||||
{
|
||||
if (!scene.IsValid() || !scene.isLoaded) return;
|
||||
GameObject target = FindTeamSelectorInScene(scene);
|
||||
if (target == null) return;
|
||||
if (target.GetComponent<TeamSelectorAnimController>() == null)
|
||||
{
|
||||
target.AddComponent<TeamSelectorAnimController>();
|
||||
}
|
||||
}
|
||||
|
||||
GameObject FindTeamSelectorInScene(Scene scene)
|
||||
{
|
||||
var all = Resources.FindObjectsOfTypeAll<GameObject>();
|
||||
for (int i = 0; i < all.Length; i++)
|
||||
{
|
||||
var go = all[i];
|
||||
if (go == null) continue;
|
||||
if (go.name != "teamSelector") continue;
|
||||
if (!go.scene.IsValid() || go.scene != scene) continue;
|
||||
return go;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1800f83344a9d0546ba50d11c2907895
|
||||
@@ -0,0 +1,146 @@
|
||||
using System.Collections.Generic;
|
||||
using DG.Tweening;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class TeamSelectorMiniCardHover : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerMoveHandler
|
||||
{
|
||||
[SerializeField] float hoverScale = 1.05f;
|
||||
[SerializeField] float hoverTweenTime = 0.12f;
|
||||
[SerializeField] float hoverExpand = 0.18f;
|
||||
[SerializeField] float hoverCompress = 0.08f;
|
||||
[SerializeField] float gyroMaxAngle = 6f;
|
||||
[SerializeField] float gyroFollow = 8f;
|
||||
|
||||
bool isHovering;
|
||||
Camera lastEventCamera;
|
||||
LayoutElement layoutElement;
|
||||
|
||||
class LayoutInfo
|
||||
{
|
||||
public TeamSelectorMiniCardHover card;
|
||||
public LayoutElement layout;
|
||||
public float baseSize;
|
||||
}
|
||||
|
||||
static readonly Dictionary<Transform, List<LayoutInfo>> layoutCache = new();
|
||||
static readonly Dictionary<Transform, bool> layoutAxisIsHorizontal = new();
|
||||
|
||||
void Awake()
|
||||
{
|
||||
layoutElement = GetComponent<LayoutElement>() ?? gameObject.AddComponent<LayoutElement>();
|
||||
var g = GetComponent<Graphic>();
|
||||
if (g != null) g.raycastTarget = true;
|
||||
}
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
isHovering = true;
|
||||
lastEventCamera = eventData.enterEventCamera;
|
||||
ApplyHoverLayout(true);
|
||||
transform.DOKill();
|
||||
transform.DOScale(hoverScale, hoverTweenTime).SetEase(Ease.OutCubic);
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
isHovering = false;
|
||||
ApplyHoverLayout(false);
|
||||
transform.DOKill();
|
||||
transform.DOScale(1f, hoverTweenTime).SetEase(Ease.OutCubic);
|
||||
transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
public void OnPointerMove(PointerEventData eventData)
|
||||
{
|
||||
lastEventCamera = eventData.enterEventCamera;
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (!isHovering) return;
|
||||
RectTransform rt = transform as RectTransform;
|
||||
if (rt == null) return;
|
||||
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rt, Input.mousePosition, lastEventCamera, out Vector2 localPoint))
|
||||
return;
|
||||
Vector2 size = rt.rect.size;
|
||||
if (size.x <= 0f || size.y <= 0f) return;
|
||||
Vector2 norm = new Vector2(localPoint.x / size.x, localPoint.y / size.y);
|
||||
norm = Vector2.ClampMagnitude(norm * 2f, 1f);
|
||||
float xRot = -norm.y * gyroMaxAngle;
|
||||
float yRot = norm.x * gyroMaxAngle;
|
||||
Quaternion target = Quaternion.Euler(xRot, yRot, 0f);
|
||||
transform.localRotation = Quaternion.Slerp(transform.localRotation, target, Time.unscaledDeltaTime * gyroFollow);
|
||||
}
|
||||
|
||||
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.card == 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>();
|
||||
TeamSelectorMiniCardHover[] cards = parent.GetComponentsInChildren<TeamSelectorMiniCardHover>(true);
|
||||
for (int i = 0; i < cards.Length; i++)
|
||||
{
|
||||
var card = cards[i];
|
||||
if (card == null) continue;
|
||||
var le = card.layoutElement ?? card.GetComponent<LayoutElement>() ?? card.gameObject.AddComponent<LayoutElement>();
|
||||
RectTransform rt = card.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 { card = card, 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3ea3ad435a1db6d45855e83c796e5d16
|
||||
@@ -7,13 +7,13 @@ public class newTeamSelector : MonoBehaviour
|
||||
{
|
||||
public static newTeamSelector Instance { get; private set; }
|
||||
|
||||
[Header("等级图片CBAS")]
|
||||
[Header("�ȼ�ͼƬCBAS")]
|
||||
public Sprite levelIcon_sprite_C;
|
||||
public Sprite levelIcon_sprite_B;
|
||||
public Sprite levelIcon_sprite_A;
|
||||
public Sprite levelIcon_sprite_S;
|
||||
|
||||
[Header("已选择的队伍")]
|
||||
[Header("��ѡ��Ķ���")]
|
||||
public int selected_heroSlot01_heroID;
|
||||
public int selected_heroSlot02_heroID;
|
||||
public int selected_heroSlot03_heroID;
|
||||
@@ -175,7 +175,7 @@ public class newTeamSelector : MonoBehaviour
|
||||
PlayerPrefs.SetInt("selected_heroSlot05_heroID", selected_heroSlot05_heroID);
|
||||
PlayerPrefs.Save();
|
||||
|
||||
Debug.Log("已保存当前队伍到PlayerPrefs");
|
||||
Debug.Log("�ѱ��浱ǰ���鵽PlayerPrefs");
|
||||
}
|
||||
|
||||
public void LoadSelectedHeroes()
|
||||
@@ -212,7 +212,7 @@ public class newTeamSelector : MonoBehaviour
|
||||
{
|
||||
// Clear slot logic
|
||||
slot.heroSlot_heroID = 0;
|
||||
if (slot.heroSlot_heroName != null) slot.heroSlot_heroName.text = "空置";
|
||||
if (slot.heroSlot_heroName != null) slot.heroSlot_heroName.text = "����";
|
||||
if (slot.heroImage != null)
|
||||
{
|
||||
// revert to default sprite if provided, otherwise keep transparent
|
||||
|
||||
Reference in New Issue
Block a user