feat: 角色展示v1.2

This commit is contained in:
m0_75251201
2025-11-18 18:45:14 +08:00
parent 8fcbdc5649
commit 6cb89ba439
51 changed files with 1558 additions and 250 deletions

View File

@@ -23,8 +23,9 @@ namespace SceneView
public float buttonHeight = 50;
private string originalText = "";
private GameObject? _targetGameObject; // 保存当前节点代表的GameObject
private bool _childrenGenerated = false; // 标记子节点和组件是否已经生成
private int _nodeDepth = 0; // 当前节点的深度
private void Awake()
{
@@ -50,12 +51,14 @@ namespace SceneView
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)
@@ -70,6 +73,7 @@ namespace SceneView
}
text.alignment = TextAlignmentOptions.MidlineLeft;
text.color = Color.white; // 确保文本颜色可见
}
if (objectEnable == null)
@@ -77,14 +81,15 @@ namespace SceneView
var button = ControlUtilities.CreateButton(rectTransform, buttonConfig, null);
objectEnable = button.button;
objectText = button.text;
button.text.text = "-";
button.button.image.color = Color.yellow;
objectText.text = ""; // 默认文本为空,使用颜色作指示
objectText.color = Color.black; // 确保文本颜色可见
button.button.image.color = Color.yellow; // 默认颜色
var rect = objectEnable.GetComponent<RectTransform>();
rect.anchorMin = Vector2.one;
rect.anchorMax = Vector2.one;
rect.pivot = Vector2.one / 2;
rect.pivot = new Vector2(1, 1); // 右上角锚点
rect.sizeDelta = new Vector2(30, 30);
rect.anchoredPosition = new Vector2(-25, -25);
rect.anchoredPosition = new Vector2(-10, -10); // 距离右上角10像素
}
// 检查子对象是否存在
@@ -115,12 +120,15 @@ namespace SceneView
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);
childRectTransform.offsetMax = new Vector2(0, -buttonHeight); // 占据主按钮以下的空间
childRectTransform.offsetMin = new Vector2(0, 0); // 确保底部没有额外偏移
verticalLayout = child.GetComponent<VerticalLayoutGroup>();
var sizeFitter = child.GetComponent<ContentSizeFitter>();
@@ -144,121 +152,119 @@ namespace SceneView
return;
}
}
// 调整布局组参数,确保正确计算
verticalLayout.padding.left = (int)(buttonHeight * 0.5f); // 增加左边距以缩进
verticalLayout.padding.top = 2;
verticalLayout.padding.bottom = 4;
verticalLayout.spacing = 2;
verticalLayout.childControlHeight = false;
verticalLayout.childControlWidth = true;
verticalLayout.childControlHeight = false; // 由ContentSizeFitter控制子节点高度
verticalLayout.childControlWidth = true; // 子节点宽度按父级宽度自适应
verticalLayout.childForceExpandHeight = false; // 不要强制展开高度
verticalLayout.childAlignment = TextAnchor.UpperLeft; // 确保子元素从上开始排列
sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
}
public void InsertChild(RectTransform childRectTransform)
// 修改为只初始化当前节点,不立即生成子内容
public IEnumerator InitializeNode(GameObject? targetObject, int depth = 0)
{
if (child != null && childRectTransform != null)
// 将子节点添加到 content 容器中
childRectTransform.SetParent(child.transform, false);
_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()); // 初始化时异步更新高度
}
private void OnExpand(GameObject go)
{
if (!go) return;
CanvasControl.FocusGameObject = go;
Expand();
}
private void Expand()
// 修改 OnExpand 方法以实现懒加载
private void OnExpand()
{
if (child == null)
{
Debug.LogError("Child content not found.");
return;
}
CanvasControl.FocusGameObject=_targetGameObject;
child.SetActive(!child.activeSelf);
UpdateHeight();
// 更新名称以模拟动画效果
if (child.activeSelf)
// 如果已经生成,直接切换显示状态
if (_childrenGenerated)
{
if (text != null) text.text = originalText + " ↓"; // 下三角符号
ToggleExpansionVisuals();
StartCoroutine(DelayedUpdateHeight()); // 每次展开/收拢后异步更新高度
}
else
{
if (text != null) text.text = originalText + " →"; // 右三角符号
else // 如果子内容未生成,则异步生成
{
ToggleExpansionVisuals(); // 生成完成后再切换显示状态 (展开或根据后续逻辑)
// 在生成子节点之前,确保子内容容器是激活的
child.SetActive(true); // <--- 新增这行,在生成子节点之前激活容器
StartCoroutine(GenerateChildrenContent(_targetGameObject, _nodeDepth + 1, () =>
{
_childrenGenerated = true;
// 注意ToggleExpansionVisuals 会再次调用 child.SetActive()
// 如果是展开状态,它会保持 true。如果是收拢则设置为 false。
StartCoroutine(DelayedUpdateHeight()); // 生成并展开后异步更新高度
}));
}
}
public void UpdateHeight()
// 新增方法生成子内容组件和子GameObject
private IEnumerator GenerateChildrenContent(GameObject? targetObject, int depth, Action? onComplete = null)
{
if (child == null || childRectTransform == null || rectTransform == null)
{
Debug.LogError("Failed to update height. Child, ChildRectTransform, or RectTransform is null.");
return;
}
ClearChildNodes(); // 在生成之前清空所有旧的子节点
if (child.activeSelf)
rectTransform.sizeDelta =
new Vector2(rectTransform.sizeDelta.x, buttonHeight + childRectTransform.sizeDelta.y);
else
rectTransform.sizeDelta = new Vector2(rectTransform.sizeDelta.x, buttonHeight);
if (parent != null)
if (targetObject == null) // 场景根节点需要遍历根GameObject
{
LayoutRebuilder.ForceRebuildLayoutImmediate(parent.childRectTransform);
parent.UpdateHeight();
}
}
private IEnumerator DisplayGameObjectStructureCoroutineImpl(GameObject targetObject, int depth,
Action onComplete = null)
{
if (targetObject == null)
{
// 如果目标对象为空,则遍历场景中的所有对象
var rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
var totalRootObjects = rootObjects.Length;
var completedRootObjects = 0;
originalText = $"场景{SceneManager.GetActiveScene().name}";
if (text) text.text = originalText;
foreach (var rootObject in rootObjects)
{
if (!rootObject) continue;
var childNode = new GameObject($"{rootObject.name}").AddComponent<TreeViewNode>();
if (!rootObject || rootObject.name == CanvasControl.ViewCanvasName) continue;
var childNodeGO = new GameObject(rootObject.name);
var childNode = childNodeGO.AddComponent<TreeViewNode>();
childNode.transform.SetParent(childRectTransform, false);
childNode.parent = this;
childNode.CreateUI();
StartCoroutine(childNode.DisplayGameObjectStructureCoroutineImpl(rootObject, depth + 1,
() =>
{
completedRootObjects++;
if (completedRootObjects == totalRootObjects)
{
// 所有根对象及其子对象都已处理完成
onComplete?.Invoke();
Expand();
}
}));
yield return null;
childNode.CreateUI(); // 确保UI元素已创建
// yield return StartCoroutine(childNode.InitializeNode(rootObject, depth)); // 浅初始化子节点
yield return null; // 每处理一个子节点就yield一次分散帧压力
}
onComplete?.Invoke();
yield break;
}
if (targetObject.name == CanvasControl.ViewCanvasName)
yield break;
label?.onClick.RemoveAllListeners();
label?.onClick.AddListener(() =>OnExpand(targetObject));
ClearChildNodes();
originalText = GetIndentedName(targetObject.name, depth);
if (text != null) text.text = originalText;
objectEnable.onClick.AddListener(() => OnObjectEnable(targetObject, objectEnable, objectText));
UpdateObjButton(targetObject, objectEnable, objectText);
// 生成当前GameObject的组件按钮
var components = targetObject.GetComponents<Behaviour>();
foreach (var component in components)
{
@@ -270,8 +276,9 @@ namespace SceneView
var config = ButtonConfig.Default;
config.RectConfig.SizeDelta = new Vector2(0, 40);
config.BackgroundColor = Color.green;
config.Text = new string(' ', (depth + 1) * 2) + component.GetType().Name;
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)
{
@@ -281,16 +288,11 @@ namespace SceneView
var button = buttonBack.button;
var comp = component;
buttonBack.button.onClick.AddListener(() => OnButtonClick(button, comp));
yield return null;
button.onClick.AddListener(() => OnButtonClick(button, comp));
yield return null; // 每创建一个组件按钮就yield一次
}
// 计数器,用于跟踪子协程的数量
var childCount = targetObject.transform.childCount;
var completedChildren = 0;
var allChildrenExpanded = false;
// 遍历目标对象的所有子对象
// 遍历目标对象的所有子对象并创建新的TreeViewNode浅初始化
foreach (Transform childTransform in targetObject.transform)
{
if (childTransform == null)
@@ -299,73 +301,119 @@ namespace SceneView
continue;
}
var childNode = new GameObject($"{childTransform.gameObject.name}").AddComponent<TreeViewNode>();
var childNodeGO = new GameObject(childTransform.gameObject.name);
var childNode = childNodeGO.AddComponent<TreeViewNode>();
childNode.transform.SetParent(childRectTransform, false);
childNode.parent = this;
childNode.CreateUI();
StartCoroutine(childNode.DisplayGameObjectStructureCoroutineImpl(childTransform.gameObject, depth + 1,
() =>
{
completedChildren++;
if (completedChildren == childCount) allChildrenExpanded = true;
}));
yield return null;
childNode.CreateUI(); // 确保UI元素已创建
// yield return StartCoroutine(childNode.InitializeNode(childTransform.gameObject, depth)); // 浅初始化子节点
yield return null; // 每处理一个子节点就yield一次
}
// 等待所有子协程完成
while (!allChildrenExpanded && childCount > 0) yield return null;
// 调用完成回调
onComplete?.Invoke();
// 在所有子节点初始化完毕后调用Expand
Expand();
}
public IEnumerator DisplayGameObjectStructureCoroutine(GameObject targetObject, int depth = 0)
private void ToggleExpansionVisuals()
{
yield return DisplayGameObjectStructureCoroutineImpl(targetObject, depth);
if (child == null) return;
// 切换 child 的激活状态
child.SetActive(!child.activeSelf);
if (text != null)
{
if (child.activeSelf)
{
text.text = originalText + " ↓"; // 下三角符号表示展开
}
else
{
text.text = originalText + " →"; // 右三角符号表示收拢
}
}
}
private string GetIndentedName(string name, int depth)
// 异步更新高度,给布局系统一帧时间来计算
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 = "";
for (var i = 0; i < depth; i++) indent += $"{i}_";
int baseIndent = depth; // GameObject的深度
if (isComponent) baseIndent++; // 组件比GameObject多一级缩进
return $"{indent}_{name}";
// 增加额外的左边距,以产生层级感
for (var i = 0; i < baseIndent; i++)
{
indent += " ";
}
return $"{indent}{name}";
}
public void ClearChildNodes()
{
if (childRectTransform != null)
foreach (Transform childRectTransform in this.childRectTransform)
Destroy(childRectTransform.gameObject);
{
// 遍历并销毁所有子对象
for (int i = childRectTransform.childCount - 1; i >= 0; i--)
{
Destroy(childRectTransform.GetChild(i).gameObject);
}
}
}
private void OnButtonClick(Button button, Behaviour component)
{
if (button == null)
{
Debug.LogError("button is null");
return;
}
if (component == null)
{
Debug.LogError("component is null");
return;
}
if (button == null || component == null) return;
// 切换组件的启用状态
var isEnabled = !component.enabled;
component.enabled = isEnabled;
// 根据组件的启用状态修改按钮背景颜色
button.image.color = isEnabled ? Color.green : Color.red;
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)
private void OnObjectEnable(GameObject? gameObject, Button? button, TMP_Text? text)
{
Debug.Log($"设置{gameObject.name}={!gameObject.activeSelf}");
if (gameObject == null || button == null || text == null) return;
Debug.Log($"设置{gameObject.name} active = {!gameObject.activeSelf}");
gameObject.SetActive(!gameObject.activeSelf);
UpdateObjButton(gameObject, button, text);
}
@@ -375,13 +423,14 @@ namespace SceneView
if (gameObject.activeSelf)
{
button.image.color = Color.green;
text.text = "√";
text.text = "√";
}
else
{
button.image.color = Color.red;
text.text = "×";
text.text = "×";
}
}
}
}
}