Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/UI/SkillTreeUI.cs

175 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using UnityEngine;
namespace UI
{
public class SkillTreeUI : FullScreenUI
{
public SkillTreePageUI skillTreePageUIPrefab;
public Transform skillTreePageUIParent; // 用于承载 SkillTreePageUI 实例的 Transform
private List<SkillTreePageUI> _skillTreePageUIs = new List<SkillTreePageUI>();
private int _currentPageIndex = 0; // 当前显示的页面索引
// 动画相关
private bool _isAnimating = false;
private float _animationProgress = 0f; // 0到1的进度
[SerializeField] private float _animationDuration = 0.3f; // 动画持续时间
private int _animationDirection = 0; // -1:左翻页, 1:右翻页 (或0表示无动画)
private SkillTreePageUI _currentMovingPage; // 当前正在移动的页面
private SkillTreePageUI _targetMovingPage; // 正在移入的页面
private float _pageWidth; // 页面的标准宽度,用于计算滑动位置
private void Start()
{
// 获取所有技能树标签
var allTags = Managers.SkillTreeManager.Instance.GetAllTag();
if (allTags == null || allTags.Length == 0)
{
Debug.LogWarning("No skill tree tags found. SkillTreeUI will be empty.");
// 可以在这里禁用翻页按钮或显示提示
return;
}
// [改进] 动态获取页面的宽度
// 为了准确获取 prefab 的尺寸,最好在编辑器中设置好 prefab 的 RectTransform
// 或者在这里实例化一个临时对象来获取其 RectTransform 信息。
var prefabRect = skillTreePageUIPrefab.GetComponent<RectTransform>();
if (prefabRect != null)
{
_pageWidth = prefabRect.rect.width;
// 如果父级有LayoutGroup或者Canvas Scaler可能影响实际尺寸需注意
// 如果 prefab 是撑满 FullScreenUI 的,那 _pageWidth 应该等于 FullScreenUI 的宽度
// 这里假设 SkillTreePageUI 会充满其父级,或者具有固定宽度。
// 简单起见,我们也可以假设它充满父级,那么 _pageWidth = parent.GetComponent<RectTransform>().rect.width;
if (skillTreePageUIParent != null)
{
_pageWidth = skillTreePageUIParent.GetComponent<RectTransform>().rect.width;
}
}
else
{
Debug.LogError("SkillTreePageUI Prefab does not have a RectTransform!");
_pageWidth = 1920; // fallback default
}
// 为每个标签实例化并初始化 SkillTreePageUI
foreach (var tag in allTags)
{
var newPage = Instantiate(skillTreePageUIPrefab, skillTreePageUIParent);
newPage.name = $"SkillTreePage_{tag}"; // 方便在 Hierarchy 中识别
// 设置 RectTransform 属性,确保页面正确布局
var pageRectTransform = newPage.GetComponent<RectTransform>();
pageRectTransform.anchorMin = Vector2.zero;
pageRectTransform.anchorMax = Vector2.one;
pageRectTransform.pivot = new Vector2(0.5f, 0.5f); // 中心
pageRectTransform.anchoredPosition = Vector2.zero;
pageRectTransform.sizeDelta = Vector2.zero; // 撑满父级
newPage.GenerateAndLayoutAllSkillTrees(tag);
_skillTreePageUIs.Add(newPage);
// 初始状态下所有页面都先设为激活,通过位置控制显示
newPage.gameObject.SetActive(true);
}
// 根据 _currentPageIndex 设置所有页面的初始位置
// 只有当前页面在中央 (0,0),其他页面在屏幕外
for (var i = 0; i < _skillTreePageUIs.Count; i++)
{
_skillTreePageUIs[i].GetComponent<RectTransform>().anchoredPosition =
new Vector2(_pageWidth * (i - _currentPageIndex), 0);
}
// 确保当前页面在层级的最上方,防止被其他未隐藏的页面遮挡
if (_skillTreePageUIs.Count > 0)
{
_skillTreePageUIs[_currentPageIndex].transform.SetAsLastSibling();
}
}
// 实现翻页动画
public override void TickUI()
{
base.TickUI();
if (_isAnimating)
{
_animationProgress += Time.deltaTime / _animationDuration;
_animationProgress = Mathf.Clamp01(_animationProgress);
// 计算当前页面的目标位置:从 0 移动到 -_pageWidth * _animationDirection
var currentX = Mathf.Lerp(0, -_pageWidth * _animationDirection, _animationProgress);
_currentMovingPage.GetComponent<RectTransform>().anchoredPosition = new Vector2(currentX, 0);
// 计算目标页面的目标位置:从 _pageWidth * _animationDirection 移动到 0
var targetX = Mathf.Lerp(_pageWidth * _animationDirection, 0, _animationProgress);
_targetMovingPage.GetComponent<RectTransform>().anchoredPosition = new Vector2(targetX, 0);
if (_animationProgress >= 1f)
{
// 动画结束
_isAnimating = false;
_animationDirection = 0; // 重置动画方向
// 更新当前页面索引 (使用 _targetMovingPage 的索引)
_currentPageIndex = _skillTreePageUIs.IndexOf(_targetMovingPage);
// 确保新的当前页面在正确位置 (0,0)
_targetMovingPage.GetComponent<RectTransform>().anchoredPosition = Vector2.zero;
// 隐藏所有非当前页面,将其放置到屏幕外,减少渲染负担
for (var i = 0; i < _skillTreePageUIs.Count; i++)
{
if (i != _currentPageIndex)
{
// 将非当前页面放置到正确的位置,以便下次翻页时能从正确位置移入
_skillTreePageUIs[i].GetComponent<RectTransform>().anchoredPosition =
new Vector2(_pageWidth * (i - _currentPageIndex), 0);
}
}
// 确保新的当前页面在层级最上方
_skillTreePageUIs[_currentPageIndex].transform.SetAsLastSibling();
}
}
}
/// <summary>
/// 控制切换到上一页。
/// </summary>
public void TurnPageLeft()
{
if (_isAnimating) return; // 动画进行中,忽略新的翻页请求
// 检查是否已经到达第一页
if (_currentPageIndex <= 0)
{
Debug.Log("SkillTreeUI: Already at the first page.");
return;
}
_animationDirection = -1; // 左翻页方向
StartPageTurnAnimation(_currentPageIndex - 1);
}
/// <summary>
/// 控制切换到下一页。
/// </summary>
public void TurnPageRight()
{
if (_isAnimating) return; // 动画进行中,忽略新的翻页请求
// 检查是否已经到达最后一页
if (_currentPageIndex >= _skillTreePageUIs.Count - 1)
{
Debug.Log("SkillTreeUI: Already at the last page.");
return;
}
_animationDirection = 1; // 右翻页方向
StartPageTurnAnimation(_currentPageIndex + 1);
}
/// <summary>
/// 启动翻页动画的内部方法。
/// </summary>
/// <param name="targetPageIndex">目标页面的索引。</param>
private void StartPageTurnAnimation(int targetPageIndex)
{
_isAnimating = true;
_animationProgress = 0f; // 重置动画进度
_currentMovingPage = _skillTreePageUIs[_currentPageIndex];
_targetMovingPage = _skillTreePageUIs[targetPageIndex];
// 确保参与动画的两个页面都是激活状态
_currentMovingPage.gameObject.SetActive(true);
_targetMovingPage.gameObject.SetActive(true);
// 将目标页面初始位置设置在当前页的左侧或右侧
// 例如,如果向右翻页 (_animationDirection = 1),目标页从右边 (_pageWidth) 滑入
_targetMovingPage.GetComponent<RectTransform>().anchoredPosition =
new Vector2(_pageWidth * _animationDirection, 0);
// 确保目标页面在当前页面之上,以便在滑动时覆盖旧页面
_targetMovingPage.transform.SetAsLastSibling();
}
// 可以添加一个 GoToPage(int pageIndex) 方法来直接跳转到某一页
// 这里为了简化,只实现了左右翻页
}
}