mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 01:17:12 +08:00
(client) feat:支持定义实体的碰撞体大小和偏移;建筑支持定义实体建筑和瓦片建筑,建筑支持指定按钮回调;添加存档管理器;Dev支持设置是否暂停;实体允许定义事件组;添加基地界面 (#57)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/57
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Base;
|
||||
using Data; // 确保 Data 命名空间包含 DefBase, CharacterDef, MonsterDef, BuildingDef, ItemDef, EventDef
|
||||
using UnityEngine;
|
||||
@@ -14,11 +15,18 @@ namespace UI
|
||||
public Prefab.TextPrefab textTemplate;
|
||||
public Prefab.ButtonPrefab buttonTemplate;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
if (Setting.Instance.CurrentSettings.developerMode)
|
||||
base.Show();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
InitReloadGameButton();
|
||||
@@ -170,7 +178,7 @@ namespace UI
|
||||
{
|
||||
entityPlacementUI.currentAction = () =>
|
||||
{
|
||||
Managers.EntityManage.Instance.GenerateEntity(Program.Instance.FocusedDimensionId, entityDef, Utils.MousePosition.GetWorldPosition());
|
||||
Managers.EntityManager.Instance.GenerateEntity(Program.Instance.FocusedDimensionId, entityDef, Utils.MousePosition.GetWorldPosition());
|
||||
};
|
||||
entityPlacementUI.Prompt = $"当前生成器:\n名称:{entityDef.label}\n描述:{entityDef.description}";
|
||||
entityPlacementUI.snapEnabled = false;
|
||||
@@ -180,7 +188,7 @@ namespace UI
|
||||
{
|
||||
entityPlacementUI.currentAction = () =>
|
||||
{
|
||||
Managers.EntityManage.Instance.GenerateMonsterEntity(Program.Instance.FocusedDimensionId, monsterDef, Utils.MousePosition.GetWorldPosition());
|
||||
Managers.EntityManager.Instance.GenerateMonsterEntity(Program.Instance.FocusedDimensionId, monsterDef, Utils.MousePosition.GetWorldPosition());
|
||||
};
|
||||
entityPlacementUI.Prompt = $"当前生成器:\n名称:{monsterDef.label}\n描述:{monsterDef.description}";
|
||||
entityPlacementUI.snapEnabled = false;
|
||||
@@ -190,7 +198,7 @@ namespace UI
|
||||
{
|
||||
entityPlacementUI.currentAction = () =>
|
||||
{
|
||||
Managers.EntityManage.Instance.GenerateBuildingEntity(Program.Instance.FocusedDimensionId, def, Utils.MousePosition.GetSnappedWorldPosition());
|
||||
Managers.EntityManager.Instance.GenerateBuildingEntity(Program.Instance.FocusedDimensionId, def, Utils.MousePosition.GetSnappedWorldPosition());
|
||||
};
|
||||
entityPlacementUI.Prompt = $"当前生成器:\n名称:{def.label}\n描述:{def.description}";
|
||||
entityPlacementUI.snapEnabled = true;
|
||||
@@ -200,7 +208,7 @@ namespace UI
|
||||
{
|
||||
entityPlacementUI.currentAction = () =>
|
||||
{
|
||||
Managers.EntityManage.Instance.GeneratePickupEntity(Program.Instance.FocusedDimensionId, itemDef, Utils.MousePosition.GetWorldPosition());
|
||||
Managers.EntityManager.Instance.GeneratePickupEntity(Program.Instance.FocusedDimensionId, itemDef, Utils.MousePosition.GetWorldPosition());
|
||||
};
|
||||
entityPlacementUI.Prompt = $"当前生成器:\n名称:{itemDef.label}\n描述:{itemDef.description}";
|
||||
entityPlacementUI.snapEnabled = false;
|
||||
@@ -209,7 +217,7 @@ namespace UI
|
||||
public static void HotReload()
|
||||
{
|
||||
UIInputControl.Instance.HideAll();
|
||||
Program.Instance.needLoad = true;
|
||||
Program.Instance.NeedLoad = true;
|
||||
SceneManager.LoadScene(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using Base;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -10,6 +11,18 @@ namespace UI
|
||||
isInputOccupied = true;
|
||||
exclusive=true;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Clock.AddTickUI(this);
|
||||
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Clock.RemoveTickUI(this);
|
||||
}
|
||||
|
||||
public virtual void TickUI()
|
||||
{
|
||||
if(!IsVisible)
|
||||
|
||||
@@ -4,6 +4,7 @@ using Prefab; // 假设 TextPrefab 在 Prefab 命名空间下
|
||||
using TMPro; // 假设 TextPrefab 内部使用了 TextMeshPro
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
@@ -11,6 +12,8 @@ namespace UI
|
||||
{
|
||||
public Transform contentPanel; // 日志内容容器
|
||||
public TextPrefab textPrefab; // 文本预制体引用
|
||||
|
||||
public Toggle pauseToggle;
|
||||
|
||||
// 日志类型颜色映射
|
||||
private static readonly Dictionary<LogType, Color> logColors = new Dictionary<LogType, Color>
|
||||
@@ -41,6 +44,7 @@ namespace UI
|
||||
public override void Show()
|
||||
{
|
||||
base.Show();
|
||||
pauseToggle.isOn = needPause;
|
||||
RefreshLogDisplay(); // 首次显示时刷新
|
||||
}
|
||||
|
||||
@@ -133,5 +137,11 @@ namespace UI
|
||||
{
|
||||
UIInputControl.Instance.Hide(this);
|
||||
}
|
||||
|
||||
public void SetPauseCallback()
|
||||
{
|
||||
needPause = pauseToggle.isOn;
|
||||
Clock.Instance.Pause=needPause;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
244
Client/Assets/Scripts/UI/SelectCharacterUI.cs
Normal file
244
Client/Assets/Scripts/UI/SelectCharacterUI.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Data;
|
||||
using Managers;
|
||||
using Map;
|
||||
using Prefab;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
public class SelectCharacterUI : MonoBehaviour
|
||||
{
|
||||
// 角色显示位置
|
||||
public Transform characterDisplay;
|
||||
|
||||
// 角色信息文本
|
||||
public TMP_Text characterInformation;
|
||||
|
||||
public Button enterButton;
|
||||
public TMP_Text buttonText;
|
||||
|
||||
[SerializeField]
|
||||
// 目标角色动画显示尺寸
|
||||
private float targetCharacterAnimationDisplaySize = 600f;
|
||||
|
||||
// 存储角色动画和角色定义的列表
|
||||
public List<(TemporaryAnimatorImageUI, CharacterDef)> characterAnimation = new();
|
||||
|
||||
// 当前选中的角色索引
|
||||
public int currentCharacter = 0;
|
||||
|
||||
// 获取当前选中的角色定义
|
||||
public CharacterDef SelectedCharacter => characterAnimation[currentCharacter].Item2;
|
||||
|
||||
/// <summary>
|
||||
/// 当脚本实例被启用时调用。
|
||||
/// 初始化角色UI,加载角色数据,并生成动画。
|
||||
/// </summary>
|
||||
private void Start()
|
||||
{
|
||||
Program.Instance.FocusedDimension.OnDimensionLoaded += MapLoaded;
|
||||
enterButton.interactable = false;
|
||||
buttonText.text = "地图加载中";
|
||||
|
||||
var characterDefs = DefineManager.Instance.QueryDefinesByType<CharacterDef>();
|
||||
|
||||
if (characterDefs == null || characterDefs.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("未找到任何角色定义,禁用SelectCharacterUI。");
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var def in characterDefs)
|
||||
{
|
||||
if (def.drawingOrder == null)
|
||||
{
|
||||
Debug.LogWarning($"角色 '{def.label}' 的 drawingOrder 为 null,跳过动画生成。");
|
||||
continue;
|
||||
}
|
||||
|
||||
var drawingOrder = def.drawingOrder.GetDrawNodeDef(EntityState.Idle, Orientation.Down, out _);
|
||||
if (drawingOrder == null)
|
||||
{
|
||||
Debug.LogWarning($"角色 '{def.label}' 在 Idle/Down 状态下没有有效的绘制节点,跳过动画生成。");
|
||||
continue;
|
||||
}
|
||||
|
||||
// GenerateTemporaryAnimationUI 返回一个组件(例如 RectTransform 或 CanvasGroup),
|
||||
// 其gameObject是实际的UI对象。
|
||||
var animationComponent = TemporaryAnimationManager.Instance.GenerateTemporaryAnimationUI(
|
||||
drawingOrder, characterDisplay.position, characterDisplay, -1);
|
||||
|
||||
// 确保animationComponent及其gameObject存在
|
||||
if (animationComponent != null && animationComponent.gameObject != null)
|
||||
{
|
||||
var rectTransform = animationComponent.gameObject.GetComponent<RectTransform>();
|
||||
if (rectTransform != null)
|
||||
{
|
||||
var initialSize = rectTransform.sizeDelta;
|
||||
// 检查原始尺寸是否有效,避免除以零和处理极小值
|
||||
if (initialSize.x > 0.001f && initialSize.y > 0.001f)
|
||||
{
|
||||
var scaleX = targetCharacterAnimationDisplaySize / initialSize.x;
|
||||
var scaleY = targetCharacterAnimationDisplaySize / initialSize.y;
|
||||
var scaleFactor = Mathf.Min(scaleX, scaleY);
|
||||
rectTransform.localScale = new Vector3(scaleFactor, scaleFactor, 1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"动画 '{animationComponent.name}' 原始尺寸无效 ({initialSize.x}x{initialSize.y}), 无法进行缩放。");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"生成动画 '{animationComponent.name}' 的GameObject没有RectTransform组件,无法进行缩放。");
|
||||
}
|
||||
|
||||
characterAnimation.Add((animationComponent, def));
|
||||
animationComponent.gameObject.SetActive(false); // 初始时全部禁用
|
||||
animationComponent.gameObject.name = def.label;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(
|
||||
$"GenerateTemporaryAnimationUI 返回了null或者GameObject为null for character '{def.label}',跳过添加和缩放。");
|
||||
}
|
||||
}
|
||||
|
||||
// 在调用 UpdateUI 之前验证 characterAnimation 列表是否为空。
|
||||
// 如果列表为空,说明没有可显示的角色,UI应该被禁用。
|
||||
if (characterAnimation.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("尽管找到了角色定义,但未能成功生成任何角色动画UI。禁用SelectCharacterUI。");
|
||||
gameObject.SetActive(false);
|
||||
return; // Early exit if no animatable characters
|
||||
}
|
||||
|
||||
UpdateUI(currentCharacter); // 初始显示第一个角色
|
||||
|
||||
}
|
||||
|
||||
private void MapLoaded(Dimension dimension)
|
||||
{
|
||||
dimension.OnDimensionLoaded -= MapLoaded;
|
||||
buttonText.text = "选择此角色";
|
||||
enterButton.interactable = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新UI显示当前选中角色的动画和信息。
|
||||
/// </summary>
|
||||
/// <param name="newCharacterIndex">新角色的索引。</param>
|
||||
public void UpdateUI(int newCharacterIndex)
|
||||
{
|
||||
// 在更新之前,检查 characterAnimation 列表是否非空。
|
||||
if (characterAnimation.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("UpdateUI 被调用,但 characterAnimation 列表为空。无法更新UI。");
|
||||
// 此时,如果UI仍激活,可能需要禁用它,取决于具体需求
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 在更新之前,确保新索引是有效的,防止越界
|
||||
if (newCharacterIndex < 0 || newCharacterIndex >= characterAnimation.Count)
|
||||
{
|
||||
// 如果 newCharacterIndex 计算有误,应进行修正或报错
|
||||
// 这里假设传入的 newCharacterIndex 是经过 OnLeft/OnRight 处理的环绕索引
|
||||
// 但为了健壮性,仍进行检查
|
||||
Debug.LogError($"UpdateUI 接收到无效的角色索引: {newCharacterIndex} (角色总数: {characterAnimation.Count})。");
|
||||
// 可选择将索引强制规范化,例如 newCharacterIndex = 0; 或 return;
|
||||
newCharacterIndex = 0; // 强制设置为第一个,以防万一
|
||||
if (characterAnimation.Count == 0) return; // 再次检查以防强制为0后依然越界
|
||||
}
|
||||
|
||||
// 优化前一个角色的禁用逻辑
|
||||
// 只有当前角色索引有效且其对应的GameObject存在时才禁用
|
||||
if (currentCharacter >= 0 && currentCharacter < characterAnimation.Count)
|
||||
{
|
||||
var previousAnimator = characterAnimation[currentCharacter].Item1;
|
||||
if (previousAnimator != null && previousAnimator.gameObject.activeSelf)
|
||||
{
|
||||
previousAnimator.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
currentCharacter = newCharacterIndex; // 更新当前角色索引
|
||||
|
||||
// 激活新的角色动画
|
||||
var currentAnimator = characterAnimation[currentCharacter].Item1;
|
||||
if (currentAnimator != null)
|
||||
{
|
||||
currentAnimator.gameObject.SetActive(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果当前选中的角色动画组件丢失,记录警告并尝试继续显示信息
|
||||
Debug.LogWarning($"当前选中角色的动画组件为null (索引: {currentCharacter})。");
|
||||
// 考虑更强大的错误处理:跳到下一个有效角色,或者禁用此UI。
|
||||
}
|
||||
|
||||
// 更新角色信息
|
||||
var def = characterAnimation[currentCharacter].Item2;
|
||||
characterInformation.text = $"<color=#FFBF00>{def.label}</color>\n{def.description}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理用户点击左箭头按钮的逻辑,切换到上一个角色。
|
||||
/// </summary>
|
||||
public void OnLeft()
|
||||
{
|
||||
// 增加对 characterAnimation.Count 的检查
|
||||
if (characterAnimation.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("无法左移,因为没有可用的角色动画。");
|
||||
return;
|
||||
}
|
||||
|
||||
var newPos = currentCharacter - 1;
|
||||
if (newPos < 0)
|
||||
newPos += characterAnimation.Count; // 环绕到列表末尾
|
||||
UpdateUI(newPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理用户点击右箭头按钮的逻辑,切换到下一个角色。
|
||||
/// </summary>
|
||||
public void OnRight()
|
||||
{
|
||||
// 增加对 characterAnimation.Count 的检查
|
||||
if (characterAnimation.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("无法右移,因为没有可用的角色动画。");
|
||||
return;
|
||||
}
|
||||
|
||||
var newPos = currentCharacter + 1;
|
||||
if (newPos >= characterAnimation.Count)
|
||||
newPos %= characterAnimation.Count; // 环绕到列表开头
|
||||
UpdateUI(newPos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理用户点击“确认”按钮的逻辑,选择当前角色并关闭UI。
|
||||
/// </summary>
|
||||
public void OnEnter()
|
||||
{
|
||||
// 确保在尝试选择角色之前,characterAnimation 列表非空,并且 currentCharacter 有效
|
||||
if (characterAnimation.Count == 0 || currentCharacter < 0 || currentCharacter >= characterAnimation.Count)
|
||||
{
|
||||
Debug.LogWarning("无法选择角色,因为没有可用的角色或当前选择无效。");
|
||||
gameObject.SetActive(false); // 强制关闭UI,避免空选
|
||||
return;
|
||||
}
|
||||
|
||||
gameObject.SetActive(false);
|
||||
Program.Instance.PlayGame(SelectedCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Client/Assets/Scripts/UI/SelectCharacterUI.cs.meta
Normal file
3
Client/Assets/Scripts/UI/SelectCharacterUI.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42f6e44a28ba47acb94eb772df972cfa
|
||||
timeCreated: 1758377349
|
||||
@@ -1,52 +1,81 @@
|
||||
using System.Collections.Generic;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using TMPro;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 技能树节点的用户界面组件。
|
||||
/// 技能树节点组件。
|
||||
/// </summary>
|
||||
public class SkillTreeNodeUI : MonoBehaviour
|
||||
public class SkillTreeNodeUI : MonoBehaviour,IPointerClickHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// 节点的RectTransform组件。
|
||||
/// </summary>
|
||||
public RectTransform rectTransform;
|
||||
|
||||
/// <summary>
|
||||
/// 节点的最小高度。
|
||||
/// </summary>
|
||||
[SerializeField] private float NODE_MIN_HEIGHT = 39.0122f * 2;
|
||||
|
||||
/// <summary>
|
||||
/// 两条连接线结束点之间的最小垂直间距。
|
||||
/// </summary>
|
||||
[SerializeField]private float MIN_VERTICAL_SPACING = 12.9447f;
|
||||
[SerializeField] private float MIN_VERTICAL_SPACING = 12.9447f;
|
||||
|
||||
/// <summary>
|
||||
/// 技能节点入口线UI组件。
|
||||
/// </summary>
|
||||
public SkillNodeEnterLineUI skillNodeEnterLineUI;
|
||||
|
||||
/// <summary>
|
||||
/// 输出线连接点的参考或容器。
|
||||
/// </summary>
|
||||
public RectTransform outputHead;
|
||||
|
||||
/// <summary>
|
||||
/// 文本显示组件。
|
||||
/// </summary>
|
||||
public TMP_Text text;
|
||||
|
||||
public SkillTreeDef skillTreeDef;
|
||||
public SkillTreeDef SkillTreeDefine { get; private set; }
|
||||
|
||||
public Image lockedNodeImage;
|
||||
[SerializeField] private Sprite unlockedSprite;
|
||||
[SerializeField] private Sprite lockedSprite;
|
||||
[SerializeField] private Color unlockedColor;
|
||||
[SerializeField] private Color lockedColor;
|
||||
[SerializeField] private GameObject shader;
|
||||
|
||||
|
||||
public void Init(SkillTreeDef skillTreeDef)
|
||||
{
|
||||
SkillTreeDefine = skillTreeDef;
|
||||
text.text = skillTreeDef.label;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
var isSkillTreeUnlocked = SkillTreeManager.Instance.IsSkillTreeUnlocked(SkillTreeDefine.defName);
|
||||
shader.SetActive(!isSkillTreeUnlocked);
|
||||
|
||||
lockedNodeImage.sprite = isSkillTreeUnlocked ? unlockedSprite : lockedSprite;
|
||||
lockedNodeImage.color = isSkillTreeUnlocked ? unlockedColor : lockedColor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化技能树节点UI。
|
||||
/// </summary>
|
||||
/// <param name="linkLinePoints">连接线的点数组。</param>
|
||||
/// <param name="label">节点显示的文本标签。</param>
|
||||
public void Init(Vector2[] linkLinePoints, string label)
|
||||
public void LinkLine(Vector2[] linkLinePoints)
|
||||
{
|
||||
text.text = label;
|
||||
if (linkLinePoints is { Length: > 0 })
|
||||
{
|
||||
var height = skillNodeEnterLineUI.GetRequiredHeight(linkLinePoints.Length);
|
||||
@@ -54,7 +83,9 @@ namespace UI
|
||||
rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, height);
|
||||
skillNodeEnterLineUI.Init(linkLinePoints);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预先计算技能树节点在初始化后所需的最小高度。
|
||||
/// 此方法可以在调用 Init 之前用于布局计算。
|
||||
@@ -68,14 +99,16 @@ namespace UI
|
||||
{
|
||||
return NODE_MIN_HEIGHT;
|
||||
}
|
||||
|
||||
// 获取 SkillNodeEnterLineUI 根据连接线数量计算出的所需高度
|
||||
var calculatedLineHeight = skillNodeEnterLineUI.GetRequiredHeight(linkLineCount);
|
||||
// 结合最小节点高度和额外的填充,确保节点有足够的空间
|
||||
// 逻辑与 Init() 方法中的高度计算保持一致
|
||||
var finalHeight = Mathf.Max(calculatedLineHeight + NODE_MIN_HEIGHT / 2 + 10, NODE_MIN_HEIGHT);
|
||||
|
||||
|
||||
return finalHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询出口位置。
|
||||
/// </summary>
|
||||
@@ -127,5 +160,19 @@ namespace UI
|
||||
|
||||
return new Vector2(worldX, worldY);
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button == PointerEventData.InputButton.Left)
|
||||
{
|
||||
SkillTreeManager.Instance.UnlockSkillTree(SkillTreeDefine.defName);
|
||||
Refresh();
|
||||
}
|
||||
else if (eventData.button == PointerEventData.InputButton.Right)
|
||||
{
|
||||
SkillTreeManager.Instance.LockSkillTree(SkillTreeDefine.defName);
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +355,7 @@ namespace UI
|
||||
|
||||
var nodeUI = Instantiate(skillTreeNodeUIPrefab, rectTransform);
|
||||
nodeUI.name = $"SkillNode_{skillDef.defName}";
|
||||
nodeUI.skillTreeDef = skillDef;
|
||||
// 暂时不设置位置,稍后统一布局
|
||||
nodeUI.Init(skillDef);
|
||||
layerNodesUI.Add(nodeUI);
|
||||
}
|
||||
|
||||
@@ -383,7 +382,7 @@ namespace UI
|
||||
|
||||
foreach (var nodeUI in layerNodes)
|
||||
{
|
||||
if (nodeUI.skillTreeDef == null)
|
||||
if (nodeUI.SkillTreeDefine == null)
|
||||
{
|
||||
Debug.LogWarning($"[技能树] 技能树节点UI '{nodeUI.name}' 没有关联的技能定义。跳过此节点的尺寸计算。");
|
||||
continue; // 无法计算,跳过此节点
|
||||
@@ -391,7 +390,7 @@ namespace UI
|
||||
|
||||
// 精确计算连接线数量:获取所有父节点,并筛选出属于当前tag的父节点
|
||||
var actualLinkLineCount = 0;
|
||||
var directParents = SkillTreeManager.Instance.GetAllDirectParents(nodeUI.skillTreeDef);
|
||||
var directParents = SkillTreeManager.Instance.GetAllDirectParents(nodeUI.SkillTreeDefine);
|
||||
if (directParents != null)
|
||||
{
|
||||
// 仅计算那些来自当前技能图谱内部的、具有指定tag的父节点作为连接线
|
||||
@@ -528,7 +527,7 @@ namespace UI
|
||||
Debug.Log($"{layerX},{nodeY}");
|
||||
nodeY += NODE_VERTICAL_SPACING +
|
||||
node.GetRequiredNodeHeight(Managers.SkillTreeManager.Instance
|
||||
.GetAllDirectParents(node.skillTreeDef).Count);
|
||||
.GetAllDirectParents(node.SkillTreeDefine).Count);
|
||||
}
|
||||
|
||||
layerX += NODE_HORIZONTAL_SPACING + layerSize.totalWidth;
|
||||
@@ -543,9 +542,9 @@ namespace UI
|
||||
{
|
||||
foreach (var nodeUI in j)
|
||||
{
|
||||
if(string.IsNullOrEmpty(nodeUI.skillTreeDef.position))
|
||||
if(string.IsNullOrEmpty(nodeUI.SkillTreeDefine.position))
|
||||
continue;
|
||||
var pos=Utils.StringUtils.StringToVector2(nodeUI.skillTreeDef.position);
|
||||
var pos=Utils.StringUtils.StringToVector2(nodeUI.SkillTreeDefine.position);
|
||||
nodeUI.rectTransform.anchoredPosition = pos;
|
||||
}
|
||||
}
|
||||
@@ -559,24 +558,24 @@ namespace UI
|
||||
var nodeDefToNodeUI = nodes
|
||||
.SelectMany(middle => middle)
|
||||
.SelectMany(inner => inner)
|
||||
.Where(node => node != null && node.skillTreeDef != null)
|
||||
.GroupBy(node => node.skillTreeDef)
|
||||
.Where(node => node != null && node.SkillTreeDefine != null)
|
||||
.GroupBy(node => node.SkillTreeDefine)
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
foreach (var node in sortedNodes)
|
||||
{
|
||||
var parentOutputPoint = new List<Vector2>();
|
||||
var parentNodes=SkillTreeManager.Instance.GetAllDirectParents(node.skillTreeDef);
|
||||
var parentNodes=SkillTreeManager.Instance.GetAllDirectParents(node.SkillTreeDefine);
|
||||
ReorderParentNodesBySortedNodesAlternative(sortedNodes,parentNodes);
|
||||
foreach (var parentNodeDef in parentNodes)
|
||||
{
|
||||
var index = GetChildOrderUnderParent(sortedNodes, node.skillTreeDef, parentNodeDef, nodeDefToNodeUI);
|
||||
var index = GetChildOrderUnderParent(sortedNodes, node.SkillTreeDefine, parentNodeDef, nodeDefToNodeUI);
|
||||
var parentNode=nodeDefToNodeUI[parentNodeDef];
|
||||
var outputCount = SkillTreeManager.Instance.GetAllDirectChildren(parentNodeDef).Count;
|
||||
var point= parentNode.GetOutputPosition(outputCount,index);
|
||||
parentOutputPoint.Add(point);
|
||||
}
|
||||
|
||||
node.Init(parentOutputPoint.ToArray(), node.skillTreeDef.label);
|
||||
node.LinkLine(parentOutputPoint.ToArray());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -624,16 +623,16 @@ namespace UI
|
||||
var orderedSiblingsUI = sortedNodes
|
||||
.Where(nodeUI =>
|
||||
nodeUI != null &&
|
||||
nodeUI.skillTreeDef != null &&
|
||||
allDirectChildrenDefs.Contains(nodeUI.skillTreeDef) &&
|
||||
nodeDefToNodeUI.ContainsKey(nodeUI.skillTreeDef) // 确保有UI对象映射
|
||||
nodeUI.SkillTreeDefine != null &&
|
||||
allDirectChildrenDefs.Contains(nodeUI.SkillTreeDefine) &&
|
||||
nodeDefToNodeUI.ContainsKey(nodeUI.SkillTreeDefine) // 确保有UI对象映射
|
||||
)
|
||||
.ToList();
|
||||
// 4. 查找 childDef 在 orderedSiblingsUI 中的索引
|
||||
// 遍历找到 childDef 对应的 UI 节点
|
||||
for (var i = 0; i < orderedSiblingsUI.Count; i++)
|
||||
{
|
||||
if (orderedSiblingsUI[i].skillTreeDef == childDef)
|
||||
if (orderedSiblingsUI[i].SkillTreeDefine == childDef)
|
||||
{
|
||||
return i; // 返回 0-based 索引
|
||||
}
|
||||
@@ -653,7 +652,7 @@ namespace UI
|
||||
|
||||
foreach (var node in sortedNodes)
|
||||
{
|
||||
var def = node.skillTreeDef;
|
||||
var def = node.SkillTreeDefine;
|
||||
if (def != null && set.Remove(def)) // 存在则添加并从集合中移除,避免重复
|
||||
{
|
||||
result.Add(def);
|
||||
|
||||
@@ -22,6 +22,6 @@ namespace UI
|
||||
}
|
||||
|
||||
// 判断是否可见
|
||||
public bool IsVisible => gameObject.activeInHierarchy;
|
||||
public bool IsVisible => gameObject && gameObject.activeInHierarchy;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user