using System; using System.Collections; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace SceneView { public class TreeViewNode : MonoBehaviour { public TreeViewNode? parent; public RectTransform? rectTransform; public TextMeshProUGUI? text; public Button? label; public GameObject? child; public RectTransform? childRectTransform; public VerticalLayoutGroup? verticalLayout; public Button? objectEnable; public TextMeshProUGUI? objectText; public float buttonHeight = 50; private string originalText = ""; private GameObject? _targetGameObject; // 保存当前节点代表的GameObject private bool _childrenGenerated = false; // 标记子节点和组件是否已经生成 private int _nodeDepth = 0; // 当前节点的深度 private void Awake() { CreateUI(); } [ContextMenu("CreateUI")] public void CreateUI() { // 获取 RectTransform 组件,如果不存在则添加 if (rectTransform == null) { rectTransform = GetComponent(); if (rectTransform == null) rectTransform = gameObject.AddComponent(); if (rectTransform == null) { Debug.LogError("Failed to add RectTransform to the GameObject."); return; } } rectTransform.sizeDelta = new Vector2(0, buttonHeight); rectTransform.anchorMin = Vector2.up; rectTransform.anchorMax = Vector2.one; rectTransform.pivot = new Vector2(0.5f, 1); // 确保轴心点在顶部中央,便于布局 var buttonConfig = ButtonConfig.Default; buttonConfig.RectConfig.Pivot = new Vector2(0.5f, 1); buttonConfig.RectConfig.AnchorMin = new Vector2(0, 1); buttonConfig.RectConfig.AnchorMax = new Vector2(1, 1); buttonConfig.RectConfig.SizeDelta = new Vector2(0, buttonHeight); buttonConfig.BackgroundColor = new Color(0.2f, 0.2f, 0.2f, 1f); // 默认背景色 // 创建按钮并检查是否已存在 if (label == null) { var button = ControlUtilities.CreateButton(rectTransform, buttonConfig, null); label = button.button; text = button.text; if (text == null) { Debug.LogError("Failed to get button text."); return; } text.alignment = TextAlignmentOptions.MidlineLeft; text.color = Color.white; // 确保文本颜色可见 } if (objectEnable == null) { var button = ControlUtilities.CreateButton(rectTransform, buttonConfig, null); objectEnable = button.button; objectText = button.text; objectText.text = ""; // 默认文本为空,使用颜色作指示 objectText.color = Color.black; // 确保文本颜色可见 button.button.image.color = Color.yellow; // 默认颜色 var rect = objectEnable.GetComponent(); rect.anchorMin = Vector2.one; rect.anchorMax = Vector2.one; rect.pivot = new Vector2(1, 1); // 右上角锚点 rect.sizeDelta = new Vector2(30, 30); rect.anchoredPosition = new Vector2(-10, -10); // 距离右上角10像素 } // 检查子对象是否存在 child = transform.Find("content")?.gameObject; if (child == null) { child = new GameObject("content"); child.transform.SetParent(rectTransform, false); if (child == null) { Debug.LogError("Failed to create or set parent for child content."); return; } childRectTransform = child.AddComponent(); if (childRectTransform == null) { Debug.LogError("Failed to add RectTransform to the child content."); return; } } else { childRectTransform = child.GetComponent(); if (childRectTransform == null) { Debug.LogError("Failed to get RectTransform for the child content."); return; } } // 确保子内容一开始是隐藏的 (懒加载的初始状态) child.SetActive(false); childRectTransform.anchorMin = new Vector2(0, 1); childRectTransform.anchorMax = new Vector2(1, 1); childRectTransform.pivot = new Vector2(0.5f, 1); childRectTransform.offsetMax = new Vector2(0, -buttonHeight); // 占据主按钮以下的空间 childRectTransform.offsetMin = new Vector2(0, 0); // 确保底部没有额外偏移 verticalLayout = child.GetComponent(); var sizeFitter = child.GetComponent(); if (verticalLayout == null) { verticalLayout = child.AddComponent(); if (verticalLayout == null) { Debug.LogError("Failed to add VerticalLayoutGroup to the child content."); return; } } if (sizeFitter == null) { sizeFitter = child.AddComponent(); if (sizeFitter == null) { Debug.LogError("Failed to add ContentSizeFitter to the child content."); return; } } // 调整布局组参数,确保正确计算 verticalLayout.padding.left = (int)(buttonHeight * 0.5f); // 增加左边距以缩进 verticalLayout.padding.top = 2; verticalLayout.padding.bottom = 4; verticalLayout.spacing = 2; verticalLayout.childControlHeight = false; // 由ContentSizeFitter控制子节点高度 verticalLayout.childControlWidth = true; // 子节点宽度按父级宽度自适应 verticalLayout.childForceExpandHeight = false; // 不要强制展开高度 verticalLayout.childAlignment = TextAnchor.UpperLeft; // 确保子元素从上开始排列 sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; } // 修改为只初始化当前节点,不立即生成子内容 public IEnumerator InitializeNode(GameObject? targetObject, int depth = 0) { _targetGameObject = targetObject; _nodeDepth = depth; // 清除之前的监听器,避免重复 label?.onClick.RemoveAllListeners(); objectEnable?.onClick.RemoveAllListeners(); if (targetObject == null) // 代表这是场景根节点 { originalText = $"场景 {SceneManager.GetActiveScene().name}"; if (text) text.text = originalText + " →"; // 默认收拢,显示右箭头 label?.onClick.AddListener(OnExpand); // 场景节点点击展开自身 objectEnable?.gameObject.SetActive(false); // 场景节点没有Enable/Disable功能 yield break; // 场景节点不立即生成根对象,等待点击展开 } // 过滤掉特定Canvas if (targetObject == null || targetObject.name == CanvasControl.ViewCanvasName) { yield break; } originalText = GetIndentedName(targetObject.name, depth); if (text != null) text.text = originalText + " →"; // 默认收拢,显示右箭头 // 注册展开事件 label?.onClick.AddListener(OnExpand); // 注册对象启用/禁用事件 objectEnable?.gameObject.SetActive(true); objectEnable?.onClick.AddListener(() => OnObjectEnable(_targetGameObject, objectEnable, objectText)); UpdateObjButton(_targetGameObject, objectEnable, objectText); // 确保子内容一开始是隐藏的 if (child) child.SetActive(false); yield return StartCoroutine(DelayedUpdateHeight()); // 初始化时异步更新高度 } // 修改 OnExpand 方法以实现懒加载 private void OnExpand() { if (child == null) { Debug.LogError("Child content not found."); return; } CanvasControl.FocusGameObject=_targetGameObject; // 如果已经生成,直接切换显示状态 if (_childrenGenerated) { ToggleExpansionVisuals(); StartCoroutine(DelayedUpdateHeight()); // 每次展开/收拢后异步更新高度 } else // 如果子内容未生成,则异步生成 { ToggleExpansionVisuals(); // 生成完成后再切换显示状态 (展开或根据后续逻辑) // 在生成子节点之前,确保子内容容器是激活的 child.SetActive(true); // <--- 新增这行,在生成子节点之前激活容器 StartCoroutine(GenerateChildrenContent(_targetGameObject, _nodeDepth + 1, () => { _childrenGenerated = true; // 注意:ToggleExpansionVisuals 会再次调用 child.SetActive() // 如果是展开状态,它会保持 true。如果是收拢,则设置为 false。 StartCoroutine(DelayedUpdateHeight()); // 生成并展开后异步更新高度 })); } } // 新增方法:生成子内容(组件和子GameObject) private IEnumerator GenerateChildrenContent(GameObject? targetObject, int depth, Action? onComplete = null) { ClearChildNodes(); // 在生成之前清空所有旧的子节点 if (targetObject == null) // 场景根节点,需要遍历根GameObject { var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects(); foreach (var rootObject in rootObjects) { if (!rootObject || rootObject.name == CanvasControl.ViewCanvasName) continue; var childNodeGO = new GameObject(rootObject.name); var childNode = childNodeGO.AddComponent(); childNode.transform.SetParent(childRectTransform, false); childNode.parent = this; childNode.CreateUI(); // 确保UI元素已创建 // yield return StartCoroutine(childNode.InitializeNode(rootObject, depth)); // 浅初始化子节点 yield return null; // 每处理一个子节点就yield一次,分散帧压力 } onComplete?.Invoke(); yield break; } // 生成当前GameObject的组件按钮 var components = targetObject.GetComponents(); foreach (var component in components) { if (childRectTransform == null) { Debug.LogError("childRectTransform is null"); continue; } var config = ButtonConfig.Default; config.RectConfig.SizeDelta = new Vector2(0, 40); config.BackgroundColor = component.enabled ? new Color(0.2f, 0.4f, 0.2f, 1f) : new Color(0.4f, 0.2f, 0.2f, 1f); // 根据组件启用状态设置颜色 config.Text = GetIndentedName(component.GetType().Name, depth, true); // 组件也需要缩进 config.TextColor = Color.white; var buttonBack = ControlUtilities.CreateButton(childRectTransform, config, null); if (buttonBack.button == null) { Debug.LogError("buttonBack.button is null"); continue; } var button = buttonBack.button; var comp = component; button.onClick.AddListener(() => OnButtonClick(button, comp)); yield return null; // 每创建一个组件按钮就yield一次 } // 遍历目标对象的所有子对象并创建新的TreeViewNode(浅初始化) foreach (Transform childTransform in targetObject.transform) { if (childTransform == null) { Debug.LogError("Child transform is null."); continue; } var childNodeGO = new GameObject(childTransform.gameObject.name); var childNode = childNodeGO.AddComponent(); childNode.transform.SetParent(childRectTransform, false); childNode.parent = this; childNode.CreateUI(); // 确保UI元素已创建 // yield return StartCoroutine(childNode.InitializeNode(childTransform.gameObject, depth)); // 浅初始化子节点 yield return null; // 每处理一个子节点就yield一次 } onComplete?.Invoke(); } private void ToggleExpansionVisuals() { if (child == null) return; // 切换 child 的激活状态 child.SetActive(!child.activeSelf); if (text != null) { if (child.activeSelf) { text.text = originalText + " ↓"; // 下三角符号表示展开 } else { text.text = originalText + " →"; // 右三角符号表示收拢 } } } // 异步更新高度,给布局系统一帧时间来计算 private IEnumerator DelayedUpdateHeight() { // 等待一帧,让 LayoutGroup 和 ContentSizeFitter 完成计算 yield return null; InternalUpdateHeight(); } private void InternalUpdateHeight() { if (rectTransform == null || child == null || childRectTransform == null) { return; // Editor退出时可能为null } float currentChildHeight = 0f; if (child.activeSelf && _childrenGenerated) { // 强制重建 childRectTransform 的布局,以获取准确的 PreferredHeight LayoutRebuilder.ForceRebuildLayoutImmediate(childRectTransform); currentChildHeight = LayoutUtility.GetPreferredHeight(childRectTransform); } // 设置主RectTransform的高度 = 按钮高度 + 子内容高度 rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, buttonHeight + currentChildHeight); // Debug.Log($"Node: {gameObject.name}, childActive: {child.activeSelf}, childrenGenerated: {_childrenGenerated}, currentChildHeight: {currentChildHeight}, finalNodeHeight: {rectTransform.sizeDelta.y}"); // 如果有父节点,通知父节点重新计算布局 if (parent != null) { // 强制重建父节点的 childRectTransform 的布局 LayoutRebuilder.ForceRebuildLayoutImmediate(parent.childRectTransform); // 递归父节点异步更新高度 StartCoroutine(parent.DelayedUpdateHeight()); } } // 调整 GetIndentedName 以支持组件类型的缩进 private string GetIndentedName(string name, int depth, bool isComponent = false) { var indent = ""; int baseIndent = depth; // GameObject的深度 if (isComponent) baseIndent++; // 组件比GameObject多一级缩进 // 增加额外的左边距,以产生层级感 for (var i = 0; i < baseIndent; i++) { indent += " "; } return $"{indent}{name}"; } public void ClearChildNodes() { if (childRectTransform != null) { // 遍历并销毁所有子对象 for (int i = childRectTransform.childCount - 1; i >= 0; i--) { Destroy(childRectTransform.GetChild(i).gameObject); } } } private void OnButtonClick(Button button, Behaviour component) { if (button == null || component == null) return; // 切换组件的启用状态 var isEnabled = !component.enabled; component.enabled = isEnabled; // 根据组件的启用状态修改按钮背景颜色 button.image.color = isEnabled ? new Color(0.2f, 0.4f, 0.2f, 1f) : new Color(0.4f, 0.2f, 0.2f, 1f); } private void OnObjectEnable(GameObject? gameObject, Button? button, TMP_Text? text) { if (gameObject == null || button == null || text == null) return; Debug.Log($"设置{gameObject.name} active = {!gameObject.activeSelf}"); gameObject.SetActive(!gameObject.activeSelf); UpdateObjButton(gameObject, button, text); } private void UpdateObjButton(GameObject gameObject, Button button, TMP_Text text) { if (gameObject.activeSelf) { button.image.color = Color.green; text.text = "√"; } else { button.image.color = Color.red; text.text = "×"; } } } }