988 lines
37 KiB
C#
988 lines
37 KiB
C#
|
|
#if UNITY_EDITOR
|
|||
|
|
using UnityEditor;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using UnityEditor.SceneManagement;
|
|||
|
|
using UnityEngine.SceneManagement;
|
|||
|
|
using UnityEngine.UIElements;
|
|||
|
|
using System.Linq;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
|
|||
|
|
namespace Aliciza.UXTool
|
|||
|
|
{
|
|||
|
|
public static class SceneViewContextMenu
|
|||
|
|
{
|
|||
|
|
// 右键状态和拖动状态
|
|||
|
|
private static bool rightMouseDown = false;
|
|||
|
|
private static bool hasDrag = false;
|
|||
|
|
|
|||
|
|
// 右键菜单的委托,允许外部插件扩展菜单
|
|||
|
|
public delegate void AddContextMenuFunc();
|
|||
|
|
|
|||
|
|
public static AddContextMenuFunc addContextMenuFunc;
|
|||
|
|
|
|||
|
|
// 用于复制/粘贴RectTransform数据的缓存
|
|||
|
|
private static RectTransformCache clipboard = null;
|
|||
|
|
|
|||
|
|
[InitializeOnLoadMethod]
|
|||
|
|
public static void Init()
|
|||
|
|
{
|
|||
|
|
// 在SceneView中注册事件回调
|
|||
|
|
if (UXDesinUtil.InDesign)
|
|||
|
|
{
|
|||
|
|
SceneView.duringSceneGui += OnSceneGUI;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SceneView.duringSceneGui -= OnSceneGUI;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void OnSceneGUI(SceneView sceneView)
|
|||
|
|
{
|
|||
|
|
if (PrefabStageUtils.InEmptyStage) return;
|
|||
|
|
|
|||
|
|
// 如果不是右键点击,直接返回
|
|||
|
|
if (Event.current == null || Event.current.button != 1)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 右键按下,初始化状态
|
|||
|
|
if (Event.current.type == EventType.MouseDown)
|
|||
|
|
{
|
|||
|
|
hasDrag = false;
|
|||
|
|
rightMouseDown = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 拖动过程中标记状态
|
|||
|
|
if (Event.current.type == EventType.MouseDrag && rightMouseDown)
|
|||
|
|
{
|
|||
|
|
hasDrag = true;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 右键松开时弹出菜单
|
|||
|
|
if (Event.current.type == EventType.MouseUp && rightMouseDown && !hasDrag)
|
|||
|
|
{
|
|||
|
|
HandleRightClick();
|
|||
|
|
Event.current.Use(); // 防止事件进一步传播
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 右键菜单生成和显示的核心方法
|
|||
|
|
private static void HandleRightClick()
|
|||
|
|
{
|
|||
|
|
// 生成右键菜单项
|
|||
|
|
GenerateRectTransformMenu();
|
|||
|
|
GenerateSelectObjectMenu();
|
|||
|
|
|
|||
|
|
// 调用外部扩展菜单
|
|||
|
|
addContextMenuFunc?.Invoke();
|
|||
|
|
|
|||
|
|
// 显示菜单
|
|||
|
|
ContextMenuUtils.ShowContextMenu(); // 调用修改后的 ShowContextMenu
|
|||
|
|
|
|||
|
|
// 重置状态
|
|||
|
|
rightMouseDown = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region RectTransform 操作菜单
|
|||
|
|
|
|||
|
|
private static void GenerateRectTransformMenu()
|
|||
|
|
{
|
|||
|
|
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
|||
|
|
if (!(UXSelectionUtil.gameObjects[0].transform is RectTransform))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
GameObject[] selected = UXSelectionUtil.gameObjects;
|
|||
|
|
|
|||
|
|
// 添加分隔符
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
|
|||
|
|
// 添加通用菜单项
|
|||
|
|
AddCommonItem();
|
|||
|
|
|
|||
|
|
// 布局与对齐
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对齐/Left", false, () => AlignSelected(AlignType.Left));
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对齐/Center (水平)", false, () => AlignSelected(AlignType.CenterHorizontal));
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对齐/Right", false, () => AlignSelected(AlignType.Right));
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对齐/Top", false, () => AlignSelected(AlignType.Top));
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对齐/Middle (垂直)", false, () => AlignSelected(AlignType.CenterVertical));
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对齐/Bottom", false, () => AlignSelected(AlignType.Bottom));
|
|||
|
|
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("分发/水平分发", false, () => DistributeSelected(DistributeAxis.Horizontal));
|
|||
|
|
ContextMenuUtils.BuildContextMenu("分发/垂直分发", false, () => DistributeSelected(DistributeAxis.Vertical));
|
|||
|
|
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("大小/匹配第一个尺寸", false, () => MatchSizeToFirst());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("像素对齐/位置取整", false, () => PixelSnapSelected());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("重置/位置与旋转与缩放", false, () => ResetTransformSelected());
|
|||
|
|
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("锚点/设为四角(保持位置)", false, () => SetAnchorsToCornersKeepPos());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("锚点/居中锚点(保持位置)", false, () => SetAnchorsToCenterKeepPos());
|
|||
|
|
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("临时操作/复制 RectTransform", false, () => CopyRectTransform());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("临时操作/粘贴 RectTransform", clipboard == null, () => PasteRectTransform());
|
|||
|
|
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/向前移动(置为最后Sibling)", false, () => BringToFront());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/发送到最底层(置为第0)", false, () => SendToBack());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/创建容器并包裹", false, () => CreateContainerAroundSelection());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/复制(场景内)", false, () => DuplicateSelection());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/重命名选中", false, () => RenameSelection());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/选择父对象", false, () => SelectParent());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("对象/聚焦(场景视图)", false, () => FrameSelection());
|
|||
|
|
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
ContextMenuUtils.BuildContextMenu("Graphic/切换 RaycastTarget", false, () => ToggleRaycastTarget());
|
|||
|
|
ContextMenuUtils.BuildContextMenu("排序/按名称排序同级子项", false, () => SortSiblingsByName());
|
|||
|
|
|
|||
|
|
// 如果可以组合RectTransform,则添加组合项
|
|||
|
|
ContextMenuUtils.BuildSeparator();
|
|||
|
|
if (CombineWidgetLogic.CanCombine(UXSelectionUtil.gameObjects))
|
|||
|
|
{
|
|||
|
|
ContextMenuUtils.BuildContextMenu("组合", false, () => CombineWidgetLogic.GenCombineRootRect(UXSelectionUtil.gameObjects));
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
ContextMenuUtils.BuildContextMenu("组合", true, null); // Disabled item
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SetAnchors(GameObject[] selectedObjects)
|
|||
|
|
{
|
|||
|
|
// 这里是设置锚点的逻辑,示例函数
|
|||
|
|
Debug.Log("设置锚点的功能...");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
private static void AddCommonItem()
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#region UI 选择菜单
|
|||
|
|
|
|||
|
|
private static void GenerateSelectObjectMenu()
|
|||
|
|
{
|
|||
|
|
var prefabStage = PrefabStageUtils.GetCurrentPrefabStage();
|
|||
|
|
List<RectTransform> inSceneObjs = GetAllUIObjects(prefabStage);
|
|||
|
|
|
|||
|
|
// 获取鼠标位置并转换为屏幕坐标(GUI坐标调整)
|
|||
|
|
Camera camera = SceneView.currentDrawingSceneView.camera;
|
|||
|
|
Vector2 mousePosGui = Event.current.mousePosition;
|
|||
|
|
Vector2 mousePos = new Vector2(mousePosGui.x, camera.pixelHeight - mousePosGui.y);
|
|||
|
|
|
|||
|
|
// 排序UI对象
|
|||
|
|
SortedList<string, RectTransform> sortedUIObjects = SortUIObjects(inSceneObjs);
|
|||
|
|
|
|||
|
|
// 将UI对象添加到菜单中
|
|||
|
|
foreach (string key in sortedUIObjects.Keys)
|
|||
|
|
{
|
|||
|
|
RectTransform obj = sortedUIObjects[key];
|
|||
|
|
if (RectTransformUtility.RectangleContainsScreenPoint(obj, mousePos, camera))
|
|||
|
|
{
|
|||
|
|
ContextMenuUtils.BuildContextMenu(obj.name, false, () => UXSelectionUtil.activeGameObject = obj.gameObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取场景中的所有UI对象
|
|||
|
|
private static List<RectTransform> GetAllUIObjects(PrefabStage prefabStage)
|
|||
|
|
{
|
|||
|
|
List<RectTransform> uiObjects = new List<RectTransform>();
|
|||
|
|
|
|||
|
|
if (prefabStage != null)
|
|||
|
|
{
|
|||
|
|
RectTransform[] allObjects = prefabStage.prefabContentsRoot.GetComponentsInChildren<RectTransform>(true);
|
|||
|
|
foreach (RectTransform obj in allObjects)
|
|||
|
|
{
|
|||
|
|
if (FindContainerLogic.ObjectFit(obj.gameObject))
|
|||
|
|
{
|
|||
|
|
uiObjects.Add(obj);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Scene scene = SceneManager.GetActiveScene();
|
|||
|
|
GameObject[] rootObjects = scene.GetRootGameObjects();
|
|||
|
|
foreach (GameObject rootObj in rootObjects)
|
|||
|
|
{
|
|||
|
|
RectTransform[] childRects = rootObj.GetComponentsInChildren<RectTransform>(true);
|
|||
|
|
foreach (RectTransform rect in childRects)
|
|||
|
|
{
|
|||
|
|
if (FindContainerLogic.ObjectFit(rect.gameObject))
|
|||
|
|
{
|
|||
|
|
uiObjects.Add(rect);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return uiObjects;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 排序UI对象
|
|||
|
|
private static SortedList<string, RectTransform> SortUIObjects(List<RectTransform> uiObjects)
|
|||
|
|
{
|
|||
|
|
List<string> uiNames = new List<string>();
|
|||
|
|
foreach (var rect in uiObjects)
|
|||
|
|
{
|
|||
|
|
uiNames.Add(GetTransformHierarchyString("", rect));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 按照名称排序
|
|||
|
|
SortedList<string, RectTransform> sortedList = new SortedList<string, RectTransform>(new DuplicateKeyComparer());
|
|||
|
|
for (int i = 0; i < uiObjects.Count; i++)
|
|||
|
|
{
|
|||
|
|
// 确保 key 唯一:包含索引
|
|||
|
|
string key = uiNames[i] + "_" + i;
|
|||
|
|
sortedList.Add(key, uiObjects[i]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return sortedList;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取UI对象的层级字符串
|
|||
|
|
private static string GetTransformHierarchyString(string prefix, Transform trans)
|
|||
|
|
{
|
|||
|
|
string str = string.IsNullOrEmpty(prefix) ? trans.GetSiblingIndex().ToString() : $"{trans.GetSiblingIndex()}.{prefix}";
|
|||
|
|
if (trans.parent != null)
|
|||
|
|
{
|
|||
|
|
return GetTransformHierarchyString(str, trans.parent);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return str;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 辅助类与功能实现
|
|||
|
|
|
|||
|
|
// 自定义比较器,用于排序UI元素
|
|||
|
|
private class DuplicateKeyComparer : IComparer<string>
|
|||
|
|
{
|
|||
|
|
public int Compare(string x, string y)
|
|||
|
|
{
|
|||
|
|
return -string.Compare(x, y); // 逆序排序
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private enum AlignType
|
|||
|
|
{
|
|||
|
|
Left,
|
|||
|
|
CenterHorizontal,
|
|||
|
|
Right,
|
|||
|
|
Top,
|
|||
|
|
CenterVertical,
|
|||
|
|
Bottom
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private enum DistributeAxis
|
|||
|
|
{
|
|||
|
|
Horizontal,
|
|||
|
|
Vertical
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private class RectTransformCache
|
|||
|
|
{
|
|||
|
|
public Vector2 anchoredPosition;
|
|||
|
|
public Vector2 sizeDelta;
|
|||
|
|
public Vector2 anchorMin;
|
|||
|
|
public Vector2 anchorMax;
|
|||
|
|
public Vector2 pivot;
|
|||
|
|
public Quaternion rotation;
|
|||
|
|
public Vector3 localScale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void CopyRectTransform()
|
|||
|
|
{
|
|||
|
|
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
|||
|
|
var rt = UXSelectionUtil.gameObjects[0].GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) return;
|
|||
|
|
clipboard = new RectTransformCache()
|
|||
|
|
{
|
|||
|
|
anchoredPosition = rt.anchoredPosition,
|
|||
|
|
sizeDelta = rt.sizeDelta,
|
|||
|
|
anchorMin = rt.anchorMin,
|
|||
|
|
anchorMax = rt.anchorMax,
|
|||
|
|
pivot = rt.pivot,
|
|||
|
|
rotation = rt.localRotation,
|
|||
|
|
localScale = rt.localScale
|
|||
|
|
};
|
|||
|
|
Debug.Log("已复制 RectTransform 数据到剪贴板");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void PasteRectTransform()
|
|||
|
|
{
|
|||
|
|
if (clipboard == null || UXSelectionUtil.gameObjects == null) return;
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects)
|
|||
|
|
{
|
|||
|
|
var rt = go.GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) continue;
|
|||
|
|
Undo.RecordObject(rt, "Paste RectTransform");
|
|||
|
|
rt.anchorMin = clipboard.anchorMin;
|
|||
|
|
rt.anchorMax = clipboard.anchorMax;
|
|||
|
|
rt.pivot = clipboard.pivot;
|
|||
|
|
rt.sizeDelta = clipboard.sizeDelta;
|
|||
|
|
rt.localRotation = clipboard.rotation;
|
|||
|
|
rt.localScale = clipboard.localScale;
|
|||
|
|
|
|||
|
|
// 保持世界位置:计算当前 world pos,然后应用 anchoredPosition 使其看起来相同
|
|||
|
|
Vector3 worldPos = rt.position;
|
|||
|
|
rt.anchoredPosition = clipboard.anchoredPosition;
|
|||
|
|
rt.position = worldPos;
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void AlignSelected(AlignType align)
|
|||
|
|
{
|
|||
|
|
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
|||
|
|
if (gos == null || gos.Length <= 1) return;
|
|||
|
|
// 统一以第一个为目标
|
|||
|
|
RectTransform[] rts = gos.Select(g => g.GetComponent<RectTransform>()).Where(r => r != null).ToArray();
|
|||
|
|
if (rts.Length <= 1) return;
|
|||
|
|
|
|||
|
|
// 计算所有对象在世界空间的边界
|
|||
|
|
switch (align)
|
|||
|
|
{
|
|||
|
|
case AlignType.Left:
|
|||
|
|
{
|
|||
|
|
float target = rts.Min(rt => GetWorldLeft(rt));
|
|||
|
|
foreach (var rt in rts) MoveWorldLeftTo(rt, target);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case AlignType.Right:
|
|||
|
|
{
|
|||
|
|
float target = rts.Max(rt => GetWorldRight(rt));
|
|||
|
|
foreach (var rt in rts) MoveWorldRightTo(rt, target);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case AlignType.CenterHorizontal:
|
|||
|
|
{
|
|||
|
|
float minX = rts.Min(rt => GetWorldLeft(rt));
|
|||
|
|
float maxX = rts.Max(rt => GetWorldRight(rt));
|
|||
|
|
float centerX = (minX + maxX) / 2f;
|
|||
|
|
foreach (var rt in rts) MoveWorldCenterXTo(rt, centerX);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case AlignType.Top:
|
|||
|
|
{
|
|||
|
|
float target = rts.Max(rt => GetWorldTop(rt));
|
|||
|
|
foreach (var rt in rts) MoveWorldTopTo(rt, target);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case AlignType.Bottom:
|
|||
|
|
{
|
|||
|
|
float target = rts.Min(rt => GetWorldBottom(rt));
|
|||
|
|
foreach (var rt in rts) MoveWorldBottomTo(rt, target);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
case AlignType.CenterVertical:
|
|||
|
|
{
|
|||
|
|
float minY = rts.Min(rt => GetWorldBottom(rt));
|
|||
|
|
float maxY = rts.Max(rt => GetWorldTop(rt));
|
|||
|
|
float centerY = (minY + maxY) / 2f;
|
|||
|
|
foreach (var rt in rts) MoveWorldCenterYTo(rt, centerY);
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static float GetWorldLeft(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return corners[0].x; // bottom-left.x
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static float GetWorldRight(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return corners[2].x; // top-right.x
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static float GetWorldTop(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return corners[1].y; // top-left.y
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static float GetWorldBottom(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return corners[0].y; // bottom-left.y
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将世界空间的位移转换为父空间的局部位移,并应用到 rectTransform.localPosition
|
|||
|
|
private static void ApplyWorldDeltaToRectTransform(RectTransform rt, Vector3 worldDelta, string undoName)
|
|||
|
|
{
|
|||
|
|
if (rt == null || rt.parent == null) return;
|
|||
|
|
Undo.RecordObject(rt, undoName);
|
|||
|
|
|
|||
|
|
// 将世界向量转换为父对象的局部向量(旋转会被考虑,但位置不重要因为这是增量)
|
|||
|
|
Vector3 localDelta = rt.parent.InverseTransformVector(worldDelta);
|
|||
|
|
|
|||
|
|
// 应用到 localPosition(而不是直接修改 world position),这样对于 UI 更稳定
|
|||
|
|
rt.localPosition += localDelta;
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MoveWorldLeftTo(RectTransform rt, float worldX)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
float curLeft = corners[0].x;
|
|||
|
|
float delta = worldX - curLeft;
|
|||
|
|
if (Mathf.Approximately(delta, 0f)) return;
|
|||
|
|
ApplyWorldDeltaToRectTransform(rt, new Vector3(delta, 0, 0), "Align Left");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MoveWorldRightTo(RectTransform rt, float worldX)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
float curRight = corners[2].x;
|
|||
|
|
float delta = worldX - curRight;
|
|||
|
|
if (Mathf.Approximately(delta, 0f)) return;
|
|||
|
|
ApplyWorldDeltaToRectTransform(rt, new Vector3(delta, 0, 0), "Align Right");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MoveWorldTopTo(RectTransform rt, float worldY)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
float curTop = corners[1].y;
|
|||
|
|
float delta = worldY - curTop;
|
|||
|
|
if (Mathf.Approximately(delta, 0f)) return;
|
|||
|
|
ApplyWorldDeltaToRectTransform(rt, new Vector3(0, delta, 0), "Align Top");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MoveWorldBottomTo(RectTransform rt, float worldY)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
float curBottom = corners[0].y;
|
|||
|
|
float delta = worldY - curBottom;
|
|||
|
|
if (Mathf.Approximately(delta, 0f)) return;
|
|||
|
|
ApplyWorldDeltaToRectTransform(rt, new Vector3(0, delta, 0), "Align Bottom");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MoveWorldCenterXTo(RectTransform rt, float worldX)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
float curCenterX = (corners[0].x + corners[2].x) / 2f;
|
|||
|
|
float delta = worldX - curCenterX;
|
|||
|
|
if (Mathf.Approximately(delta, 0f)) return;
|
|||
|
|
ApplyWorldDeltaToRectTransform(rt, new Vector3(delta, 0, 0), "Align Center X");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MoveWorldCenterYTo(RectTransform rt, float worldY)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
float curCenterY = (corners[0].y + corners[2].y) / 2f;
|
|||
|
|
float delta = worldY - curCenterY;
|
|||
|
|
if (Mathf.Approximately(delta, 0f)) return;
|
|||
|
|
ApplyWorldDeltaToRectTransform(rt, new Vector3(0, delta, 0), "Align Center Y");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void DistributeSelected(DistributeAxis axis)
|
|||
|
|
{
|
|||
|
|
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
|||
|
|
if (gos == null || gos.Length <= 2) return; // 两个对象没必要
|
|||
|
|
RectTransform[] rts = gos.Select(g => g.GetComponent<RectTransform>()).Where(r => r != null).ToArray();
|
|||
|
|
if (rts.Length <= 2) return;
|
|||
|
|
|
|||
|
|
// 按中心排序
|
|||
|
|
if (axis == DistributeAxis.Horizontal)
|
|||
|
|
{
|
|||
|
|
var ordered = rts.OrderBy(rt => GetWorldCenter(rt).x).ToArray();
|
|||
|
|
float left = GetWorldLeft(ordered.First());
|
|||
|
|
float right = GetWorldRight(ordered.Last());
|
|||
|
|
float totalWidth = ordered.Sum(rt => GetWidthWorld(rt));
|
|||
|
|
float space = (right - left - totalWidth) / (ordered.Length - 1);
|
|||
|
|
float curX = left;
|
|||
|
|
for (int i = 0; i < ordered.Length; i++)
|
|||
|
|
{
|
|||
|
|
float w = GetWidthWorld(ordered[i]);
|
|||
|
|
float targetCenter = curX + w / 2f;
|
|||
|
|
MoveWorldCenterXTo(ordered[i], targetCenter);
|
|||
|
|
curX += w + space;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var ordered = rts.OrderBy(rt => GetWorldCenter(rt).y).ToArray();
|
|||
|
|
float bottom = GetWorldBottom(ordered.First());
|
|||
|
|
float top = GetWorldTop(ordered.Last());
|
|||
|
|
float totalHeight = ordered.Sum(rt => GetHeightWorld(rt));
|
|||
|
|
float space = (top - bottom - totalHeight) / (ordered.Length - 1);
|
|||
|
|
float curY = bottom;
|
|||
|
|
for (int i = 0; i < ordered.Length; i++)
|
|||
|
|
{
|
|||
|
|
float h = GetHeightWorld(ordered[i]);
|
|||
|
|
float targetCenter = curY + h / 2f;
|
|||
|
|
MoveWorldCenterYTo(ordered[i], targetCenter);
|
|||
|
|
curY += h + space;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static Vector2 GetWorldCenter(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return (corners[0] + corners[2]) / 2f;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static float GetWidthWorld(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return Mathf.Abs(corners[2].x - corners[0].x);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static float GetHeightWorld(RectTransform rt)
|
|||
|
|
{
|
|||
|
|
var corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
return Mathf.Abs(corners[1].y - corners[0].y);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void MatchSizeToFirst()
|
|||
|
|
{
|
|||
|
|
var gos = UXSelectionUtil.gameObjects;
|
|||
|
|
if (gos == null || gos.Length <= 1) return;
|
|||
|
|
var first = gos[0].GetComponent<RectTransform>();
|
|||
|
|
if (first == null) return;
|
|||
|
|
for (int i = 1; i < gos.Length; i++)
|
|||
|
|
{
|
|||
|
|
var rt = gos[i].GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) continue;
|
|||
|
|
Undo.RecordObject(rt, "Match Size");
|
|||
|
|
rt.sizeDelta = first.sizeDelta;
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void PixelSnapSelected()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
var rt = go.GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) continue;
|
|||
|
|
Undo.RecordObject(rt, "Pixel Snap");
|
|||
|
|
Vector2 ap = rt.anchoredPosition;
|
|||
|
|
ap.x = Mathf.Round(ap.x);
|
|||
|
|
ap.y = Mathf.Round(ap.y);
|
|||
|
|
rt.anchoredPosition = ap;
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void ResetTransformSelected()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
var rt = go.GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) continue;
|
|||
|
|
Undo.RecordObject(rt, "Reset Transform");
|
|||
|
|
rt.anchoredPosition = Vector2.zero;
|
|||
|
|
rt.localRotation = Quaternion.identity;
|
|||
|
|
rt.localScale = Vector3.one;
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SetAnchorsToCornersKeepPos()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
var rt = go.GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) continue;
|
|||
|
|
Undo.RecordObject(rt, "Set Anchors to Corners Keep Position");
|
|||
|
|
|
|||
|
|
Transform parent = rt.parent;
|
|||
|
|
if (parent == null) continue;
|
|||
|
|
RectTransform parentRT = parent as RectTransform;
|
|||
|
|
if (parentRT == null) continue;
|
|||
|
|
|
|||
|
|
Vector3[] corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
Vector3[] parentCorners = new Vector3[4];
|
|||
|
|
parentRT.GetWorldCorners(parentCorners);
|
|||
|
|
|
|||
|
|
Vector2 newAnchorMin = new Vector2(
|
|||
|
|
Mathf.InverseLerp(parentCorners[0].x, parentCorners[2].x, corners[0].x),
|
|||
|
|
Mathf.InverseLerp(parentCorners[0].y, parentCorners[2].y, corners[0].y));
|
|||
|
|
Vector2 newAnchorMax = new Vector2(
|
|||
|
|
Mathf.InverseLerp(parentCorners[0].x, parentCorners[2].x, corners[2].x),
|
|||
|
|
Mathf.InverseLerp(parentCorners[0].y, parentCorners[2].y, corners[2].y));
|
|||
|
|
|
|||
|
|
Vector2 oldAnchored = rt.anchoredPosition;
|
|||
|
|
rt.anchorMin = newAnchorMin;
|
|||
|
|
rt.anchorMax = newAnchorMax;
|
|||
|
|
rt.anchoredPosition = oldAnchored; // 尝试保留视觉位置
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SetAnchorsToCenterKeepPos()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
var rt = go.GetComponent<RectTransform>();
|
|||
|
|
if (rt == null) continue;
|
|||
|
|
Undo.RecordObject(rt, "Set Anchors to Center Keep Position");
|
|||
|
|
Transform parent = rt.parent;
|
|||
|
|
if (parent == null) continue;
|
|||
|
|
RectTransform parentRT = parent as RectTransform;
|
|||
|
|
if (parentRT == null) continue;
|
|||
|
|
|
|||
|
|
Vector3[] corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
Vector3[] parentCorners = new Vector3[4];
|
|||
|
|
parentRT.GetWorldCorners(parentCorners);
|
|||
|
|
|
|||
|
|
float centerX = (corners[0].x + corners[2].x) / 2f;
|
|||
|
|
float centerY = (corners[0].y + corners[2].y) / 2f;
|
|||
|
|
|
|||
|
|
Vector2 anchor = new Vector2(
|
|||
|
|
Mathf.InverseLerp(parentCorners[0].x, parentCorners[2].x, centerX),
|
|||
|
|
Mathf.InverseLerp(parentCorners[0].y, parentCorners[2].y, centerY));
|
|||
|
|
|
|||
|
|
Vector2 oldAnchored = rt.anchoredPosition;
|
|||
|
|
rt.anchorMin = anchor;
|
|||
|
|
rt.anchorMax = anchor;
|
|||
|
|
rt.anchoredPosition = oldAnchored;
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(rt);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void BringToFront()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
Transform parent = go.transform.parent;
|
|||
|
|
Undo.RecordObject(go.transform, "Bring To Front");
|
|||
|
|
if (parent != null)
|
|||
|
|
{
|
|||
|
|
go.transform.SetSiblingIndex(parent.childCount - 1);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
go.transform.SetSiblingIndex(go.transform.GetSiblingIndex());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
EditorUtility.SetDirty(go);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SendToBack()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
Transform parent = go.transform.parent;
|
|||
|
|
Undo.RecordObject(go.transform, "Send To Back");
|
|||
|
|
go.transform.SetSiblingIndex(0);
|
|||
|
|
EditorUtility.SetDirty(go);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void CreateContainerAroundSelection()
|
|||
|
|
{
|
|||
|
|
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
|||
|
|
if (gos == null || gos.Length == 0) return;
|
|||
|
|
// 只允许相同父级
|
|||
|
|
Transform parent = gos[0].transform.parent;
|
|||
|
|
if (gos.Any(g => g.transform.parent != parent))
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("创建容器要求所有对象在同一父级下");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算包围盒(世界坐标)
|
|||
|
|
var rts = gos.Select(g => g.GetComponent<RectTransform>()).Where(r => r != null).ToArray();
|
|||
|
|
if (rts.Length == 0) return;
|
|||
|
|
|
|||
|
|
Vector3 worldMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
|
|||
|
|
Vector3 worldMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
|
|||
|
|
foreach (var rt in rts)
|
|||
|
|
{
|
|||
|
|
Vector3[] corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
worldMin = Vector3.Min(worldMin, corners[0]);
|
|||
|
|
worldMax = Vector3.Max(worldMax, corners[2]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建容器
|
|||
|
|
GameObject root = UIBuilderUtil.CreateUIObj("Container");
|
|||
|
|
Undo.RegisterCreatedObjectUndo(root, "Create Container");
|
|||
|
|
var rootRT = root.GetComponent<RectTransform>();
|
|||
|
|
root.transform.SetParent(parent);
|
|||
|
|
root.transform.SetSiblingIndex(rts.Min(r => r.GetSiblingIndex()));
|
|||
|
|
rootRT.localScale = Vector3.one;
|
|||
|
|
|
|||
|
|
// 将世界包围盒转换为父本地坐标
|
|||
|
|
RectTransform parentRT = parent as RectTransform;
|
|||
|
|
if (parentRT == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("父对象不是 RectTransform,无法创建容器");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vector3 localMin = parentRT.InverseTransformPoint(worldMin);
|
|||
|
|
Vector3 localMax = parentRT.InverseTransformPoint(worldMax);
|
|||
|
|
Vector2 size = new Vector2(localMax.x - localMin.x, localMax.y - localMin.y);
|
|||
|
|
Vector2 center = (localMin + localMax) / 2f;
|
|||
|
|
|
|||
|
|
rootRT.sizeDelta = size;
|
|||
|
|
rootRT.anchoredPosition = center;
|
|||
|
|
|
|||
|
|
// 将选中对象设置为容器子对象并保持位置
|
|||
|
|
foreach (var g in gos)
|
|||
|
|
{
|
|||
|
|
Undo.SetTransformParent(g.transform, root.transform, "Wrap In Container");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Selection.activeGameObject = root;
|
|||
|
|
EditorUtility.SetDirty(rootRT);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void DuplicateSelection()
|
|||
|
|
{
|
|||
|
|
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
|||
|
|
if (gos == null || gos.Length == 0) return;
|
|||
|
|
List<GameObject> newOnes = new List<GameObject>();
|
|||
|
|
foreach (var g in gos)
|
|||
|
|
{
|
|||
|
|
GameObject dup = Object.Instantiate(g, g.transform.parent);
|
|||
|
|
dup.name = g.name + " Copy";
|
|||
|
|
Undo.RegisterCreatedObjectUndo(dup, "Duplicate UI Element");
|
|||
|
|
dup.transform.SetSiblingIndex(g.transform.GetSiblingIndex() + 1);
|
|||
|
|
newOnes.Add(dup);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Selection.objects = newOnes.ToArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void RenameSelection()
|
|||
|
|
{
|
|||
|
|
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
|||
|
|
Selection.activeGameObject = UXSelectionUtil.gameObjects[0];
|
|||
|
|
// 调用编辑器的重命名命令
|
|||
|
|
EditorApplication.ExecuteMenuItem("Edit/Rename");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SelectParent()
|
|||
|
|
{
|
|||
|
|
if (UXSelectionUtil.gameObjects == null || UXSelectionUtil.gameObjects.Length == 0) return;
|
|||
|
|
var go = UXSelectionUtil.gameObjects[0];
|
|||
|
|
if (go.transform.parent != null)
|
|||
|
|
{
|
|||
|
|
Selection.activeGameObject = go.transform.parent.gameObject;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void FrameSelection()
|
|||
|
|
{
|
|||
|
|
SceneView.lastActiveSceneView?.FrameSelected();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void ToggleRaycastTarget()
|
|||
|
|
{
|
|||
|
|
foreach (var go in UXSelectionUtil.gameObjects ?? new GameObject[0])
|
|||
|
|
{
|
|||
|
|
var g = go.GetComponent<Graphic>();
|
|||
|
|
if (g == null) continue;
|
|||
|
|
Undo.RecordObject(g, "Toggle RaycastTarget");
|
|||
|
|
g.raycastTarget = !g.raycastTarget;
|
|||
|
|
EditorUtility.SetDirty(g);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void SortSiblingsByName()
|
|||
|
|
{
|
|||
|
|
var gos = UXSelectionUtil.gameObjects?.Where(g => g != null).ToArray();
|
|||
|
|
if (gos == null || gos.Length == 0) return;
|
|||
|
|
Transform parent = gos[0].transform.parent;
|
|||
|
|
if (parent == null) return;
|
|||
|
|
|
|||
|
|
var children = parent.Cast<Transform>().Select(t => t.gameObject).OrderBy(o => o.name).ToArray();
|
|||
|
|
for (int i = 0; i < children.Length; i++)
|
|||
|
|
{
|
|||
|
|
Undo.RecordObject(children[i].transform, "Sort By Name");
|
|||
|
|
children[i].transform.SetSiblingIndex(i);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// CombineWidgetLogic 放在同一文件以便完整交付
|
|||
|
|
public static class CombineWidgetLogic
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 组合节点
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="rects">需要组合的对象 需要所有对象在同一层级</param>
|
|||
|
|
/// <returns>组合之后的父节点</returns>
|
|||
|
|
public static GameObject GenCombineRootRect(List<RectTransform> rects)
|
|||
|
|
{
|
|||
|
|
if (rects == null || rects.Count == 0) return null;
|
|||
|
|
|
|||
|
|
// 确保所有在同一父级
|
|||
|
|
Transform parent = rects[0].transform.parent;
|
|||
|
|
if (rects.Any(r => r.transform.parent != parent))
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("所有对象必须在同一父级下才能组合");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算世界包围盒
|
|||
|
|
Vector3 worldMin = Vector3.positiveInfinity;
|
|||
|
|
Vector3 worldMax = Vector3.negativeInfinity;
|
|||
|
|
foreach (var rt in rects)
|
|||
|
|
{
|
|||
|
|
Vector3[] corners = new Vector3[4];
|
|||
|
|
rt.GetWorldCorners(corners);
|
|||
|
|
worldMin = Vector3.Min(worldMin, corners[0]);
|
|||
|
|
worldMax = Vector3.Max(worldMax, corners[2]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建容器
|
|||
|
|
GameObject root = UIBuilderUtil.CreateUIObj("CombineRoot");
|
|||
|
|
RectTransform rootRT = root.GetComponent<RectTransform>();
|
|||
|
|
root.transform.SetParent(parent);
|
|||
|
|
rootRT.localScale = Vector3.one;
|
|||
|
|
|
|||
|
|
// 将世界包围盒转换为父本地坐标
|
|||
|
|
RectTransform parentRT = parent as RectTransform;
|
|||
|
|
if (parentRT == null)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("父对象不是 RectTransform,无法创建组合容器");
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Vector3 localMin = parentRT.InverseTransformPoint(worldMin);
|
|||
|
|
Vector3 localMax = parentRT.InverseTransformPoint(worldMax);
|
|||
|
|
Vector2 size = new Vector2(localMax.x - localMin.x, localMax.y - localMin.y);
|
|||
|
|
Vector2 center = (localMin + localMax) / 2f;
|
|||
|
|
|
|||
|
|
rootRT.sizeDelta = size;
|
|||
|
|
rootRT.anchoredPosition = center;
|
|||
|
|
|
|||
|
|
// 按原顺序设置为子对象
|
|||
|
|
rects.Sort((a, b) => a.GetSiblingIndex() - b.GetSiblingIndex());
|
|||
|
|
foreach (var rt in rects)
|
|||
|
|
{
|
|||
|
|
Undo.SetTransformParent(rt.transform, root.transform, "Combine Widgets");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Selection.activeGameObject = root;
|
|||
|
|
return root;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 组合节点
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="rects"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static GameObject GenCombineRootRect(GameObject[] objs)
|
|||
|
|
{
|
|||
|
|
List<RectTransform> rects = objs.ToList().Select(a => a.GetComponent<RectTransform>()).Where(r => r != null).ToList();
|
|||
|
|
|
|||
|
|
return GenCombineRootRect(rects);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
///
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="rects"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private static bool AllHaveSameParent(GameObject[] objs)
|
|||
|
|
{
|
|||
|
|
if (objs.Length == 1)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Transform parent = objs[0].transform.parent;
|
|||
|
|
for (int i = 1; i < objs.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (objs[i].transform.parent != parent)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static bool AllHaveRectTransform(GameObject[] objs)
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < objs.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (objs[i].GetComponent<RectTransform>() == null)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static bool CanCombine(GameObject[] objs)
|
|||
|
|
{
|
|||
|
|
if (objs == null || objs.Length <= 1)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool SameLevelUI = false;
|
|||
|
|
if (AllHaveSameParent(objs))
|
|||
|
|
{
|
|||
|
|
SameLevelUI = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
bool UIInPrefab = false;
|
|||
|
|
foreach (GameObject go in objs)
|
|||
|
|
{
|
|||
|
|
string assetPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(go);
|
|||
|
|
if (!string.IsNullOrEmpty(assetPath))
|
|||
|
|
{
|
|||
|
|
UIInPrefab = true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (SameLevelUI && !UIInPrefab)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
#endif
|