├── Editor ├── UIAssistant │ ├── TreeNode.cs │ ├── UIAssistantTools.cs │ ├── UIAssistantWindow.cs │ └── UINodeInfo.cs └── UITool.cs ├── README.md └── Scirpts └── DrawRectLine.cs /Editor/UIAssistant/TreeNode.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | using UnityEngine.UI; 4 | using UnityEditor; 5 | using System; 6 | 7 | public class TreeNode 8 | { 9 | public int batchCount = 0; 10 | public int maskBatchCount = 0; 11 | private static float nodeDefaultMarginX = 20f; 12 | private static float nodeDefaultWidth = 100f; 13 | private static float nodeDefaultWidthObject = 180f; 14 | private static float nodeDefaultHeight = 20f; 15 | 16 | private bool isShow = true; 17 | private bool isDestroy = false; 18 | private bool isRoot = false; 19 | public bool IsRoot 20 | { 21 | set 22 | { 23 | isRoot = value; 24 | } 25 | get 26 | { 27 | return isRoot; 28 | } 29 | } 30 | 31 | private int depth = 0; 32 | public int Depth 33 | { 34 | set 35 | { 36 | depth = value; 37 | } 38 | get 39 | { 40 | return depth; 41 | } 42 | } 43 | 44 | private string title = null; 45 | private string subTitle = null; 46 | private RectTransform rectTransform = null; 47 | private GUIStyle subTitleGuiStyle = null; 48 | private TreeNode parent = null; 49 | private GameObject assetObject = null; 50 | public GameObject AssetObject 51 | { 52 | get 53 | { 54 | return assetObject; 55 | } 56 | } 57 | private List children = new List(); 58 | private Rect position = new Rect(0, 0, nodeDefaultWidth, nodeDefaultHeight); 59 | private float marginX = nodeDefaultMarginX; 60 | private Vector2 recursiveSize = new Vector2(nodeDefaultWidth, nodeDefaultHeight); 61 | public Vector2 RecursiveSize 62 | { 63 | set 64 | { 65 | recursiveSize = value; 66 | } 67 | get 68 | { 69 | return recursiveSize; 70 | } 71 | } 72 | 73 | private UINodeInfo nodeInfo; 74 | public UINodeInfo NodeInfo 75 | { 76 | get 77 | { 78 | return nodeInfo; 79 | } 80 | } 81 | 82 | public TreeNode(string title, GameObject assetObject = null, int depth = 0) 83 | { 84 | this.title = title; 85 | this.assetObject = assetObject; 86 | this.depth = depth; 87 | 88 | GenInfo(); 89 | 90 | if (title == null) 91 | { 92 | position.width = nodeDefaultWidthObject; 93 | } 94 | } 95 | 96 | public void OnGUI() 97 | { 98 | if (Event.current.type == EventType.MouseDown && position.Contains(Event.current.mousePosition) && Event.current.button == 0) 99 | { 100 | Selection.activeGameObject = assetObject; 101 | } 102 | 103 | isShow = EditorGUI.Foldout(position, isShow, title, UIAssistantTools.GetStyles(nodeInfo.BatchID % UIAssistantTools.DefaultDepth)); 104 | if (subTitle != null) 105 | { 106 | EditorGUI.LabelField(new Rect(position.x + position.width + 5, position.y, position.width, position.height), subTitle, subTitleGuiStyle); 107 | } 108 | 109 | if (isShow) 110 | { 111 | float childY = position.y + position.height; 112 | for (int i = 0; i < children.Count; i++) 113 | { 114 | if (children[i] != null) 115 | { 116 | children[i].position.y = childY; 117 | children[i].OnGUI(); 118 | childY = childY + children[i].recursiveSize.y; 119 | } 120 | } 121 | } 122 | 123 | CalcRecursiveSize(); 124 | } 125 | 126 | public TreeNode Clone() 127 | { 128 | TreeNode cloneNode = new TreeNode(title, assetObject, depth); 129 | cloneNode.SetSubTitle(subTitle, subTitleGuiStyle != null ? subTitleGuiStyle.normal.textColor : Color.green); 130 | cloneNode.SetParent(parent); 131 | for (int i = 0; i < children.Count; i++) 132 | { 133 | if (children[i] != null) 134 | { 135 | cloneNode.AddChild(children[i].Clone()); 136 | } 137 | } 138 | 139 | return cloneNode; 140 | } 141 | 142 | public void Destroy() 143 | { 144 | SetParent(null); 145 | assetObject = null; 146 | isDestroy = true; 147 | 148 | for (int i = 0; i < children.Count; i++) 149 | { 150 | if (children[i] != null) 151 | { 152 | children[i].Destroy(); 153 | } 154 | } 155 | children.Clear(); 156 | } 157 | 158 | public void PrintNodeInfo() 159 | { 160 | for (int i = 0; i < children.Count; i++) 161 | { 162 | if (children[i] != null) 163 | { 164 | children[i].PrintNodeInfo(); 165 | } 166 | } 167 | } 168 | 169 | public void UpdataInfo() 170 | { 171 | nodeInfo.RefreshInfo(); 172 | if (nodeInfo.Use) 173 | { 174 | if (nodeInfo.check > -1) 175 | { 176 | title = string.Format("{0} {1} info : ( {2} / {3} / {4} / {5} / {6}) check : {7} BatchID : {8} )", nodeInfo.HierarychyOrder, nodeInfo.Name, nodeInfo.Depth, nodeInfo.MaterialInstanceID, nodeInfo.TextureID, nodeInfo.IsInMask, nodeInfo.IsInMask2D, nodeInfo.check, nodeInfo.BatchID); 177 | } 178 | else 179 | { 180 | title = string.Format("{0} {1} info : ( {2} / {3} / {4} / {5} / {6}) BatchID : {7}", nodeInfo.HierarychyOrder, nodeInfo.Name, nodeInfo.Depth, nodeInfo.MaterialInstanceID, nodeInfo.TextureID, nodeInfo.IsInMask, nodeInfo.IsInMask2D, nodeInfo.BatchID); 181 | } 182 | } 183 | for (int i = 0; i < children.Count; i++) 184 | { 185 | if (children[i] != null) 186 | { 187 | children[i].UpdataInfo(); 188 | } 189 | } 190 | } 191 | 192 | public List> GetNodesInfo() 193 | { 194 | List> list = new List>(); 195 | list.Add(new KeyValuePair(this, nodeInfo)); 196 | 197 | for (int i = 0; i < children.Count; i++) 198 | { 199 | if (children[i] != null) 200 | { 201 | List> childList = children[i].GetNodesInfo(); 202 | if(childList.Count > 0) 203 | { 204 | if(nodeInfo.IsInMask == 0) 205 | { 206 | for (int j = 0; j < childList.Count; ++j) 207 | { 208 | childList[j].Value.IsInMask = 1; 209 | } 210 | } 211 | else if (nodeInfo.IsInMask2D != -1) 212 | { 213 | for (int j = 0; j < childList.Count; ++j) 214 | { 215 | if(childList[j].Value.IsInMask2D == -1) 216 | { 217 | childList[j].Value.IsInMask2D = nodeInfo.IsInMask2D; 218 | childList[j].Value.Clip(nodeInfo.GetRect()); 219 | } 220 | } 221 | } 222 | list.AddRange(childList); 223 | } 224 | } 225 | } 226 | nodeInfo.IsInMask2D = -1; 227 | return list; 228 | } 229 | 230 | public void AddChild(TreeNode node) 231 | { 232 | if (node == null) 233 | { 234 | return; 235 | } 236 | 237 | if (node == this) 238 | { 239 | return; 240 | } 241 | 242 | if (node.isDestroy) 243 | { 244 | return; 245 | } 246 | 247 | if (node.parent == this) 248 | { 249 | return; 250 | } 251 | 252 | node.SetParent(null); 253 | children.Add(node); 254 | node.parent = this; 255 | node.SetPositionX(position.x + marginX); 256 | node.Depth = depth + 1; 257 | } 258 | 259 | private void RemoveChild(TreeNode node) 260 | { 261 | if (node == null) 262 | { 263 | return; 264 | } 265 | 266 | int index = -1; 267 | for (int i = 0; i < children.Count; i++) 268 | { 269 | if (node == children[i]) 270 | { 271 | index = i; 272 | break; 273 | } 274 | } 275 | 276 | if (index != -1) 277 | { 278 | children[index].parent = null; 279 | children.RemoveAt(index); 280 | } 281 | } 282 | 283 | private void RemoveAllChild() 284 | { 285 | for (int i = 0; i < children.Count; i++) 286 | { 287 | if (children[i] != null) 288 | { 289 | children[i].parent = null; 290 | } 291 | } 292 | children.Clear(); 293 | } 294 | 295 | private void DestroyChild(TreeNode node) 296 | { 297 | if (node == null) 298 | { 299 | return; 300 | } 301 | 302 | int index = -1; 303 | for (int i = 0; i < children.Count; i++) 304 | { 305 | if (node == children[i]) 306 | { 307 | index = i; 308 | break; 309 | } 310 | } 311 | 312 | if (index != -1) 313 | { 314 | children[index].Destroy(); 315 | } 316 | } 317 | 318 | private void DestroyAllChild() 319 | { 320 | for (int i = 0; i < children.Count; i++) 321 | { 322 | if (children[i] != null) 323 | { 324 | children[i].Destroy(); 325 | } 326 | } 327 | children.Clear(); 328 | } 329 | 330 | private void SetParent(TreeNode node) 331 | { 332 | if (node == null) 333 | { 334 | if (parent != null) 335 | { 336 | parent.RemoveChild(this); 337 | parent = null; 338 | } 339 | return; 340 | } 341 | 342 | if (node == this) 343 | { 344 | return; 345 | } 346 | 347 | if (node.isDestroy) 348 | { 349 | return; 350 | } 351 | 352 | if (node != parent) 353 | { 354 | if (parent != null) 355 | { 356 | parent.RemoveChild(this); 357 | parent = null; 358 | } 359 | node.AddChild(this); 360 | } 361 | } 362 | 363 | private int GetShowChildNum() 364 | { 365 | int num = 0; 366 | if (isShow) 367 | { 368 | num++; 369 | } 370 | 371 | for (int i = 0; i < children.Count; i++) 372 | { 373 | if (children[i] != null && children[i].IsShowRecursively()) 374 | { 375 | num++; 376 | } 377 | } 378 | 379 | return num; 380 | } 381 | 382 | private void SetSize(float width, float height) 383 | { 384 | position.width = width; 385 | position.height = height; 386 | } 387 | 388 | private void SetMarginX(float marginX) 389 | { 390 | this.marginX = marginX; 391 | 392 | for (int i = 0; i < children.Count; i++) 393 | { 394 | if (children[i] != null) 395 | { 396 | children[i].SetPositionX(position.x + this.marginX); 397 | } 398 | } 399 | } 400 | 401 | private void SetSubTitle(string subTitle, Color titleColor) 402 | { 403 | this.subTitle = subTitle; 404 | 405 | if (subTitle != null) 406 | { 407 | if (subTitleGuiStyle == null) 408 | { 409 | subTitleGuiStyle = new GUIStyle(); 410 | subTitleGuiStyle.fontSize = 11; 411 | subTitleGuiStyle.alignment = TextAnchor.MiddleLeft; 412 | } 413 | subTitleGuiStyle.normal.textColor = titleColor; 414 | } 415 | } 416 | 417 | //calculate node size 418 | private void CalcRecursiveSize() 419 | { 420 | recursiveSize.x = position.width; 421 | recursiveSize.y = position.height; 422 | 423 | if (isShow) 424 | { 425 | float childMaxWidth = 0f; 426 | for (int i = 0; i < children.Count; i++) 427 | { 428 | if (children[i] != null) 429 | { 430 | childMaxWidth = Math.Max(childMaxWidth, children[i].recursiveSize.x); 431 | recursiveSize.y = recursiveSize.y + children[i].recursiveSize.y; 432 | } 433 | } 434 | 435 | //all nodes have same size 436 | if (childMaxWidth > 0f) 437 | { 438 | recursiveSize.x = childMaxWidth + Math.Abs(marginX); 439 | } 440 | } 441 | } 442 | 443 | private void SetPositionX(float x) 444 | { 445 | position.x = x; 446 | 447 | for (int i = 0; i < children.Count; i++) 448 | { 449 | if (children[i] != null) 450 | { 451 | children[i].SetPositionX(position.x + marginX); 452 | } 453 | } 454 | } 455 | 456 | private bool IsShowRecursively() 457 | { 458 | if (!isShow) 459 | { 460 | return false; 461 | } 462 | 463 | if (parent == null) 464 | { 465 | return isShow; 466 | } 467 | 468 | return parent.IsShowRecursively(); 469 | } 470 | 471 | private void GenInfo() 472 | { 473 | rectTransform = assetObject.GetComponent(); 474 | MaskableGraphic ui = assetObject.GetComponent(); 475 | nodeInfo = new UINodeInfo(ui != null, assetObject.GetComponent() != null, assetObject.name); 476 | nodeInfo.Depth = depth; 477 | nodeInfo.IsInMask = assetObject.GetComponent() != null ? 0 : -1; 478 | nodeInfo.IsInMask2D = assetObject.GetComponent() != null ? nodeInfo.GetHashCode() : -1; 479 | if (ui != null) 480 | { 481 | nodeInfo.MaterialInstanceID = ui.material.GetInstanceID(); 482 | nodeInfo.TextureID = ui.mainTexture.GetInstanceID(); 483 | } 484 | Matrix4x4 localToWorldMatrix = assetObject.transform.localToWorldMatrix; 485 | Text t = assetObject.GetComponent(); 486 | if(t != null) 487 | { 488 | if (t.text.Equals("")) 489 | { 490 | for (int i = 0; i < 4; i++) 491 | { 492 | nodeInfo.Corners[i] = localToWorldMatrix.MultiplyPoint(Vector2.zero); 493 | } 494 | } 495 | else 496 | { 497 | Rect rect = t.GetPixelAdjustedRect(); 498 | float halfWidth = t.preferredWidth / 2; 499 | float halfHeight = t.preferredHeight / 2; 500 | Vector2 pos_1 = Vector2.zero; 501 | int idx = (int)t.alignment; 502 | if (idx % 3 == 0) 503 | { 504 | pos_1.x = -rect.width / 2 + halfWidth; 505 | } 506 | else if(idx % 3 == 2) 507 | { 508 | pos_1.x = rect.width / 2 - halfWidth; 509 | } 510 | if(idx / 3 == 0) 511 | { 512 | pos_1.y = rect.height / 2 - halfHeight; 513 | } 514 | else if(idx / 3 == 2) 515 | { 516 | pos_1.y = -rect.height / 2 + halfHeight; 517 | } 518 | Vector2[] pos = new Vector2[4]; 519 | pos[0] = new Vector2(pos_1.x - halfWidth, pos_1.y - halfHeight); 520 | pos[1] = new Vector2(pos_1.x - halfWidth, pos_1.y + halfHeight); 521 | pos[2] = new Vector2(pos_1.x + halfWidth, pos_1.y + halfHeight); 522 | pos[3] = new Vector2(pos_1.x + halfWidth, pos_1.y - halfHeight); 523 | 524 | if(rectTransform.pivot.x < 0.000001) 525 | { 526 | for (int i = 0; i < 4; ++i) 527 | { 528 | pos[i] = new Vector2(pos[i].x + rect.size.x / 2, pos[i].y); 529 | } 530 | } 531 | else if(Mathf.Abs(rectTransform.pivot.x - 1) < 0.000001) 532 | { 533 | for (int i = 0; i < 4; ++i) 534 | { 535 | pos[i] = new Vector2(pos[i].x - rect.size.x / 2, pos[i].y); 536 | } 537 | } 538 | 539 | if (rectTransform.pivot.y < 0.000001) 540 | { 541 | for (int i = 0; i < 4; ++i) 542 | { 543 | pos[i] = new Vector2(pos[i].x, pos[i].y + rect.size.y / 2); 544 | } 545 | } 546 | else if (Mathf.Abs(rectTransform.pivot.y - 1) < 0.000001) 547 | { 548 | for (int i = 0; i < 4; ++i) 549 | { 550 | pos[i] = new Vector2(pos[i].x, pos[i].y - rect.size.y / 2); 551 | } 552 | } 553 | 554 | for (int i = 0; i < 4; i++) 555 | { 556 | nodeInfo.Corners[i] = localToWorldMatrix.MultiplyPoint(pos[i]); 557 | } 558 | } 559 | } 560 | else 561 | { 562 | rectTransform.GetWorldCorners(nodeInfo.Corners); 563 | } 564 | } 565 | } -------------------------------------------------------------------------------- /Editor/UIAssistant/UIAssistantTools.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | public class UIAssistantTools 5 | { 6 | private static Color[] m_colors = new Color[6] { Color.red, Color.yellow, Color.blue, Color.green, Color.black, Color.white }; 7 | private static GUIStyle[] styles = null; 8 | private static List list = new List(); 9 | private static Dictionary maskDic = new Dictionary(); 10 | 11 | public static int DefaultDepth = 1000; 12 | 13 | public static void InitStyles() 14 | { 15 | styles = new GUIStyle[6]; 16 | for (int i = 0; i < styles.Length; ++i) 17 | { 18 | styles[i] = new GUIStyle(); 19 | styles[i].normal.textColor = m_colors[i]; 20 | } 21 | } 22 | 23 | public static GUIStyle GetStyles(int id) 24 | { 25 | if(styles == null) 26 | { 27 | InitStyles(); 28 | } 29 | 30 | return styles[id % 6]; 31 | } 32 | 33 | public static void GenTreeInfo(TreeNode node) 34 | { 35 | if (node.IsRoot) 36 | { 37 | Dictionary>> rectInfos = new Dictionary>>(); 38 | rectInfos.Add(1, new KeyValuePair>(node, new List())); 39 | List> infos = node.GetNodesInfo(); 40 | int idx = 0; 41 | for(int i = 0; i < infos.Count; ++i) 42 | { 43 | if (infos[i].Value.Use) 44 | { 45 | DrawRectInfo rectInfo = new DrawRectInfo(infos[i].Value.Name, infos[i].Value.IsOnlyCanvas); 46 | rectInfo.Depth = infos[i].Value.Depth; 47 | rectInfo.HierarychyOrder = idx++; 48 | rectInfo.MaterialInstanceID = infos[i].Value.MaterialInstanceID; 49 | rectInfo.TextureID = infos[i].Value.TextureID; 50 | rectInfo.Corners = infos[i].Value.Corners; 51 | rectInfo.IsInMask = infos[i].Value.IsInMask; 52 | rectInfo.IsInMask2D = infos[i].Value.IsInMask2D; 53 | infos[i].Value.ConnectInfo(rectInfo); 54 | if (!rectInfos.ContainsKey(rectInfo.Depth / DefaultDepth)) 55 | { 56 | rectInfos[rectInfo.Depth / DefaultDepth] = new KeyValuePair>(infos[i].Key, new List()); 57 | } 58 | rectInfos[rectInfo.Depth / DefaultDepth].Value.Add(rectInfo); 59 | } 60 | } 61 | 62 | if(rectInfos.Count > 0) 63 | { 64 | int allBatchCount = 0; 65 | int allMaskBatchCount = 0; 66 | KeyValuePair temp; 67 | foreach(var keyValue in rectInfos) 68 | { 69 | temp = CalculateDepthInCanvas(keyValue.Value.Key, keyValue.Value.Value); 70 | allBatchCount += temp.Key; 71 | allMaskBatchCount += temp.Value; 72 | } 73 | node.batchCount = allBatchCount; 74 | node.maskBatchCount = allMaskBatchCount; 75 | } 76 | } 77 | } 78 | 79 | private static KeyValuePair CalculateDepthInCanvas(TreeNode node, List rectInfos) 80 | { 81 | if(rectInfos.Count == 0) 82 | { 83 | return new KeyValuePair(0, 0); 84 | } 85 | list.Clear(); 86 | list.Add(rectInfos[0]); 87 | 88 | for (int i = 1; i < rectInfos.Count; ++i) 89 | { 90 | CalculateDepth(rectInfos[i]); 91 | list.Add(rectInfos[i]); 92 | } 93 | 94 | //Print 95 | rectInfos.Sort(); 96 | 97 | rectInfos[0].BatchID = rectInfos[0].Depth; 98 | for (int i = 1; i < rectInfos.Count; ++i) 99 | { 100 | if ((rectInfos[i].Depth == rectInfos[i - 1].Depth && rectInfos[i].CanBatch(rectInfos[i - 1])) || rectInfos[i].IsOnlyCanvas) 101 | { 102 | rectInfos[i].BatchID = rectInfos[i - 1].BatchID; 103 | } 104 | else 105 | { 106 | rectInfos[i].BatchID = rectInfos[i - 1].BatchID + 1; 107 | } 108 | //rectInfos[i].Print(); 109 | } 110 | 111 | for (int i = 0; i < rectInfos.Count - 2; ++i) 112 | { 113 | for (int j = i + 1; j < rectInfos.Count; ++j) 114 | { 115 | if (rectInfos[i].BatchID != rectInfos[j].BatchID && rectInfos[i].CanBatch(rectInfos[j])) 116 | { 117 | rectInfos[i].check = rectInfos[j].HierarychyOrder; 118 | rectInfos[j].check = rectInfos[i].HierarychyOrder; 119 | } 120 | } 121 | } 122 | 123 | maskDic.Clear(); 124 | for (int i = 0; i < rectInfos.Count; ++i) 125 | { 126 | if(rectInfos[i].IsInMask == 0 && !maskDic.ContainsKey(rectInfos[i].BatchID)) 127 | { 128 | maskDic.Add(rectInfos[i].BatchID, true); 129 | } 130 | } 131 | 132 | node.maskBatchCount = maskDic.Count; 133 | node.batchCount = rectInfos[rectInfos.Count - 1].BatchID - rectInfos[0].Depth + 1 + maskDic.Count; 134 | node.UpdataInfo(); 135 | DrawRectLine ui = node.AssetObject.GetComponent(); 136 | if (ui == null) 137 | { 138 | ui = node.AssetObject.AddComponent(); 139 | } 140 | ui.SetDrawRectInfoList(rectInfos); 141 | return new KeyValuePair(node.batchCount, node.maskBatchCount); 142 | } 143 | 144 | private static void CalculateDepth(DrawRectInfo a) 145 | { 146 | a.Depth = list[0].Depth; 147 | for(int i = 0; i < list.Count; ++i) 148 | { 149 | if(Intersects(a.GetRect(), list[i].GetRect())) 150 | { 151 | int depth = list[i].Depth + 1; 152 | if (list[i].CanBatch(a)) 153 | { 154 | depth = list[i].Depth; 155 | } 156 | a.Depth = depth > a.Depth ? depth : a.Depth; 157 | } 158 | } 159 | } 160 | 161 | private static bool Intersects(Rect a, Rect b) 162 | { 163 | if(a.xMax > b.xMin && b.xMax > a.xMin && a.yMax > b.yMin && b.yMax > a.yMin) 164 | { 165 | return true; 166 | } 167 | 168 | return false; 169 | } 170 | } -------------------------------------------------------------------------------- /Editor/UIAssistant/UIAssistantWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | public class UIAssistantWindow : EditorWindow 5 | { 6 | private int defaultDepth = UIAssistantTools.DefaultDepth; 7 | private int depthIdx = 1; 8 | private GameObject m_selectObj = null; 9 | private TreeNode m_treeRootNode = null; 10 | private Vector2 m_ScrollPos = Vector2.zero; 11 | private Color[] m_colors = new Color[6] { Color.red, Color.yellow, Color.blue, Color.green, Color.black, Color.white }; 12 | 13 | void OnGUI() 14 | { 15 | GUILabelType(); 16 | GUILayout.Label("UIAssistant"); 17 | 18 | GUILabelType(TextAnchor.UpperLeft); 19 | GUILayout.Space(2); 20 | CreateSplit(); 21 | 22 | GUILayout.BeginHorizontal(); 23 | for(int i = 0; i < m_colors.Length; ++i) 24 | { 25 | GUI.color = m_colors[i]; 26 | GUILayout.Label(string.Format("{0} : ", i)); 27 | GUILayout.Box("", GUILayout.Width(15), GUILayout.Height(15)); 28 | GUILayout.Space(140); 29 | } 30 | GUILayout.EndHorizontal(); 31 | 32 | GUILabelType(TextAnchor.UpperLeft); 33 | GUILayout.Space(2); 34 | CreateSplit(); 35 | 36 | GUILayout.BeginHorizontal(); 37 | if (GUILayout.Button("Catch")) 38 | { 39 | Catch(); 40 | } 41 | if (GUILayout.Button("Refresh")) 42 | { 43 | Refresh(); 44 | } 45 | if (GUILayout.Button("Clear")) 46 | { 47 | Reset(); 48 | } 49 | GUILayout.EndHorizontal(); 50 | 51 | GUILabelType(TextAnchor.UpperLeft); 52 | GUILayout.Space(2); 53 | CreateSplit(); 54 | 55 | ShowCatchUI(); 56 | } 57 | 58 | private void ShowCatchUI() 59 | { 60 | if (m_selectObj != null) 61 | { 62 | GUILabelType(TextAnchor.UpperLeft); 63 | GUILayout.Space(2); 64 | GUILayout.Label(m_treeRootNode == null ? "Result: " : string.Format("Result: {0}({1})", m_treeRootNode.batchCount, m_treeRootNode.maskBatchCount)); 65 | GUILayout.Space(2); 66 | 67 | m_ScrollPos = GUI.BeginScrollView(new Rect(10, 140, 800, position.height - 160), m_ScrollPos, new Rect(0, 0, m_treeRootNode.RecursiveSize.x, m_treeRootNode.RecursiveSize.y), true, true); 68 | m_treeRootNode.OnGUI(); 69 | GUI.EndScrollView(); 70 | } 71 | } 72 | 73 | private void Catch() 74 | { 75 | if(Selection.activeGameObject == null) 76 | { 77 | EditorUtility.DisplayDialog("Tips", "Select Object is null!", "close"); 78 | return; 79 | } 80 | 81 | if (Selection.activeGameObject.layer == LayerMask.NameToLayer("UI")) 82 | { 83 | m_selectObj = Selection.activeGameObject; 84 | Refresh(); 85 | } 86 | } 87 | 88 | private void Refresh() 89 | { 90 | if (m_treeRootNode != null) 91 | { 92 | m_treeRootNode.Destroy(); 93 | } 94 | 95 | if (m_selectObj != null) 96 | { 97 | depthIdx = 1; 98 | m_treeRootNode = new TreeNode(m_selectObj.name, m_selectObj, depthIdx * defaultDepth); 99 | m_treeRootNode.IsRoot = true; 100 | GenChildNodes(m_treeRootNode, m_selectObj.transform); 101 | UIAssistantTools.GenTreeInfo(m_treeRootNode); 102 | } 103 | } 104 | 105 | private void GenChildNodes(TreeNode node, Transform transform) 106 | { 107 | if(transform.childCount > 0) 108 | { 109 | int depth = 0; 110 | for (int i = 0; i < transform.childCount; ++i) 111 | { 112 | Transform child = transform.GetChild(i); 113 | if(child.gameObject.activeSelf) 114 | { 115 | depth = node.Depth + 1; 116 | if (child.GetComponent() != null) 117 | { 118 | ++depthIdx; 119 | depth = depthIdx * defaultDepth; 120 | } 121 | TreeNode childNode = new TreeNode(child.name, child.gameObject, depth); 122 | GenChildNodes(childNode, child); 123 | node.AddChild(childNode); 124 | } 125 | } 126 | } 127 | } 128 | 129 | private void Reset() 130 | { 131 | m_selectObj = null; 132 | m_treeRootNode = null; 133 | m_ScrollPos = Vector2.zero; 134 | } 135 | 136 | public GUIStyle GUILabelType(TextAnchor anchor = TextAnchor.UpperCenter) 137 | { 138 | GUIStyle labelstyle = GUI.skin.GetStyle("Label"); 139 | labelstyle.alignment = anchor; 140 | return labelstyle; 141 | } 142 | 143 | public void CreateSplit() 144 | { 145 | GUILayout.Label("-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"); 146 | } 147 | } -------------------------------------------------------------------------------- /Editor/UIAssistant/UINodeInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | public class UINodeInfo 5 | { 6 | public int check = -1; 7 | private int m_BatchID = 0; 8 | public int BatchID 9 | { 10 | set 11 | { 12 | m_BatchID = value; 13 | } 14 | get 15 | { 16 | return m_BatchID; 17 | } 18 | } 19 | 20 | private int m_depth = 0; 21 | public int Depth 22 | { 23 | set 24 | { 25 | m_depth = value; 26 | } 27 | get 28 | { 29 | return m_depth; 30 | } 31 | } 32 | 33 | private int m_hierarychyOrder = 0; 34 | public int HierarychyOrder 35 | { 36 | get 37 | { 38 | return m_hierarychyOrder; 39 | } 40 | } 41 | 42 | private int m_materialInstanceID = 0; 43 | public int MaterialInstanceID 44 | { 45 | set 46 | { 47 | m_materialInstanceID = value; 48 | } 49 | get 50 | { 51 | return m_materialInstanceID; 52 | } 53 | } 54 | 55 | private int m_textureID = 0; 56 | public int TextureID 57 | { 58 | set 59 | { 60 | m_textureID = value; 61 | } 62 | get 63 | { 64 | return m_textureID; 65 | } 66 | } 67 | 68 | private bool m_isDrawGraphic = false; 69 | private bool m_isCanvas = false; 70 | public bool IsOnlyCanvas 71 | { 72 | get 73 | { 74 | return m_isCanvas && !m_isDrawGraphic; 75 | } 76 | } 77 | 78 | public bool Use 79 | { 80 | get 81 | { 82 | return m_isDrawGraphic || m_isCanvas; 83 | } 84 | } 85 | 86 | private int m_isInMask = -1; 87 | public int IsInMask 88 | { 89 | set 90 | { 91 | m_isInMask = value; 92 | } 93 | get 94 | { 95 | return m_isInMask; 96 | } 97 | } 98 | 99 | private int m_isInMask2D = -1; 100 | public int IsInMask2D 101 | { 102 | set 103 | { 104 | m_isInMask2D = value; 105 | } 106 | get 107 | { 108 | return m_isInMask2D; 109 | } 110 | } 111 | 112 | private string m_name; 113 | public string Name 114 | { 115 | set 116 | { 117 | m_name = value; 118 | } 119 | get 120 | { 121 | return m_name; 122 | } 123 | } 124 | 125 | private Vector3[] m_corners = new Vector3[4]; 126 | public Vector3[] Corners 127 | { 128 | set 129 | { 130 | m_corners = value; 131 | } 132 | get 133 | { 134 | return m_corners; 135 | } 136 | } 137 | 138 | private DrawRectInfo m_info; 139 | 140 | public UINodeInfo(bool drawGraphic, bool isCanvas, string name) 141 | { 142 | m_isDrawGraphic = drawGraphic; 143 | m_isCanvas = isCanvas; 144 | m_name = name; 145 | } 146 | 147 | public void ConnectInfo(DrawRectInfo info) 148 | { 149 | m_info = info; 150 | } 151 | 152 | public void RefreshInfo() 153 | { 154 | if (m_info != null) 155 | { 156 | check = m_info.check; 157 | m_BatchID = m_info.BatchID; 158 | m_depth = m_info.Depth; 159 | m_hierarychyOrder = m_info.HierarychyOrder; 160 | } 161 | } 162 | 163 | public Rect GetRect() 164 | { 165 | float minX, minY, maxX, maxY; 166 | minX = maxX = m_corners[0].x; 167 | minY = maxY = m_corners[0].y; 168 | for (int i = 1; i < 4; ++i) 169 | { 170 | if (m_corners[i].x < minX) 171 | { 172 | minX = m_corners[i].x; 173 | } 174 | if (m_corners[i].x > maxX) 175 | { 176 | maxX = m_corners[i].x; 177 | } 178 | if (m_corners[i].y < minY) 179 | { 180 | minY = m_corners[i].y; 181 | } 182 | if (m_corners[i].y > maxY) 183 | { 184 | maxY = m_corners[i].y; 185 | } 186 | } 187 | return new Rect(minX, minY, maxX - minX, maxY - minY); 188 | } 189 | 190 | public void Clip(Rect rect) 191 | { 192 | Rect curRect = GetRect(); 193 | if((curRect.xMax > rect.xMin) && (curRect.xMin < rect.xMax) && (curRect.yMax > rect.yMin) && (curRect.yMin < rect.yMax)) 194 | { 195 | float minX = curRect.xMin > rect.xMin ? curRect.xMin : rect.xMin; 196 | float maxX = curRect.xMax < rect.xMax ? curRect.xMax : rect.xMax; 197 | float minY = curRect.yMin > rect.yMin ? curRect.yMin : rect.yMin; 198 | float maxY = curRect.yMax < rect.yMax ? curRect.yMax : rect.yMax; 199 | m_corners[0] = new Vector2(minX, minY); 200 | m_corners[1] = new Vector2(minX, maxY); 201 | m_corners[2] = new Vector2(maxX, maxY); 202 | m_corners[3] = new Vector2(maxX, minY); 203 | } 204 | else 205 | { 206 | for(int i = 0; i < m_corners.Length; ++i) 207 | { 208 | m_corners[i] = Vector2.zero; 209 | } 210 | m_isDrawGraphic = false; 211 | } 212 | } 213 | } -------------------------------------------------------------------------------- /Editor/UITool.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEditor; 5 | 6 | public class UITool { 7 | 8 | [MenuItem("Tools/UI/UIAssistant")] 9 | public static void OpenUIAssistant() 10 | { 11 | UIAssistantWindow windows = EditorWindow.GetWindow(); 12 | windows.autoRepaintOnSceneChange = true; 13 | windows.titleContent = new GUIContent("UIAssistant"); 14 | windows.maxSize = new Vector2(1600, 1000); 15 | windows.minSize = new Vector2(200, 500); 16 | 17 | windows.Show(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UIAssistantTools -------------------------------------------------------------------------------- /Scirpts/DrawRectLine.cs: -------------------------------------------------------------------------------- 1 | #if UNITY_EDITOR 2 | using System; 3 | using UnityEngine; 4 | using System.Collections.Generic; 5 | 6 | public class DrawRectInfo : IComparable 7 | { 8 | public int check = -1; 9 | private int m_BatchID = 0; 10 | public int BatchID 11 | { 12 | set 13 | { 14 | m_BatchID = value; 15 | } 16 | get 17 | { 18 | return m_BatchID; 19 | } 20 | } 21 | 22 | private string m_name; 23 | public string Name 24 | { 25 | set 26 | { 27 | m_name = value; 28 | } 29 | get 30 | { 31 | return m_name; 32 | } 33 | } 34 | 35 | private int m_hierarychyOrder = 0; 36 | public int HierarychyOrder 37 | { 38 | set 39 | { 40 | m_hierarychyOrder = value; 41 | } 42 | get 43 | { 44 | return m_hierarychyOrder; 45 | } 46 | } 47 | 48 | private int m_depth = 0; 49 | public int Depth 50 | { 51 | set 52 | { 53 | m_depth = value; 54 | } 55 | get 56 | { 57 | return m_depth; 58 | } 59 | } 60 | 61 | private int m_materialInstanceID = 0; 62 | public int MaterialInstanceID 63 | { 64 | set 65 | { 66 | m_materialInstanceID = value; 67 | } 68 | get 69 | { 70 | return m_materialInstanceID; 71 | } 72 | } 73 | 74 | private int m_textureID = 0; 75 | public int TextureID 76 | { 77 | set 78 | { 79 | m_textureID = value; 80 | } 81 | get 82 | { 83 | return m_textureID; 84 | } 85 | } 86 | 87 | private Vector3[] m_corners = new Vector3[4]; 88 | public Vector3[] Corners 89 | { 90 | set 91 | { 92 | m_corners = value; 93 | } 94 | get 95 | { 96 | return m_corners; 97 | } 98 | } 99 | 100 | private bool m_isOnlyCanvas; 101 | public bool IsOnlyCanvas 102 | { 103 | get 104 | { 105 | return m_isOnlyCanvas; 106 | } 107 | } 108 | 109 | private int m_isInMask = -1; 110 | public int IsInMask 111 | { 112 | set 113 | { 114 | m_isInMask = value; 115 | } 116 | get 117 | { 118 | return m_isInMask; 119 | } 120 | } 121 | 122 | private int m_isInMask2D = -1; 123 | public int IsInMask2D 124 | { 125 | set 126 | { 127 | m_isInMask2D = value; 128 | } 129 | get 130 | { 131 | return m_isInMask2D; 132 | } 133 | } 134 | 135 | public DrawRectInfo(string name, bool onlyCanvas) 136 | { 137 | this.m_name = name; 138 | this.m_isOnlyCanvas = onlyCanvas; 139 | } 140 | 141 | public Rect GetRect() 142 | { 143 | float minX, minY, maxX, maxY; 144 | minX = maxX = m_corners[0].x; 145 | minY = maxY = m_corners[0].y; 146 | for (int i = 1; i < 4; ++i) 147 | { 148 | if (m_corners[i].x < minX) 149 | { 150 | minX = m_corners[i].x; 151 | } 152 | if (m_corners[i].x > maxX) 153 | { 154 | maxX = m_corners[i].x; 155 | } 156 | if (m_corners[i].y < minY) 157 | { 158 | minY = m_corners[i].y; 159 | } 160 | if (m_corners[i].y > maxY) 161 | { 162 | maxY = m_corners[i].y; 163 | } 164 | } 165 | return new Rect(minX, minY, maxX - minX, maxY - minY); 166 | } 167 | 168 | public bool CanBatch(DrawRectInfo b) 169 | { 170 | return MaterialInstanceID == b.MaterialInstanceID && TextureID == b.TextureID && IsInMask == b.IsInMask && IsInMask2D == b.IsInMask2D; 171 | } 172 | 173 | public int CompareTo(DrawRectInfo b) 174 | { 175 | if (m_depth > b.Depth) 176 | { 177 | return 1; 178 | } 179 | else if (m_depth == b.Depth) 180 | { 181 | if(m_materialInstanceID > b.MaterialInstanceID) 182 | { 183 | return 1; 184 | } 185 | else if(m_materialInstanceID == b.MaterialInstanceID) 186 | { 187 | if(m_textureID > b.TextureID) 188 | { 189 | return 1; 190 | } 191 | else if(m_textureID == b.TextureID) 192 | { 193 | if (m_hierarychyOrder > b.HierarychyOrder) 194 | { 195 | return 1; 196 | } 197 | else if (m_hierarychyOrder == b.HierarychyOrder) 198 | { 199 | return 0; 200 | } 201 | } 202 | } 203 | } 204 | return -1; 205 | } 206 | 207 | public void Print() 208 | { 209 | Debug.LogError("------------------- Name: " + Name); 210 | Debug.LogError("------------------- Depth: " + Depth); 211 | //Debug.LogError("------------------- MaterialInstanceID: " + MaterialInstanceID); 212 | //Debug.LogError("------------------- TextureID: " + TextureID); 213 | Debug.LogError("------------------- HierarychyOrder: " + HierarychyOrder); 214 | } 215 | } 216 | 217 | public class DrawRectLine : MonoBehaviour { 218 | private Color[] m_colors = new Color[6] { Color.red, Color.yellow, Color.blue, Color.green, Color.black, Color.white}; 219 | 220 | private List m_rectInfoList = new List(); 221 | public void SetDrawRectInfoList(List rectInfoList) 222 | { 223 | m_rectInfoList.Clear(); 224 | m_rectInfoList = rectInfoList; 225 | } 226 | 227 | void OnDrawGizmos() 228 | { 229 | for (int i = 0; i < m_rectInfoList.Count; ++i) 230 | { 231 | Gizmos.color = m_colors[m_rectInfoList[i].BatchID % m_colors.Length]; 232 | for (int j = 0; j < 4; ++j) 233 | { 234 | Gizmos.DrawLine(m_rectInfoList[i].Corners[j], m_rectInfoList[i].Corners[(j + 1) % 4]); 235 | } 236 | } 237 | } 238 | } 239 | #endif --------------------------------------------------------------------------------