종속성 분석 툴 제작
설계나 리팩토링 과정에서 AI에게 코드를 분석해 달라고 요청했더니 클래스를 도식화하여 종속성을 나타내 보여주었는데 이러한 툴이 있다면 참 편할 것 같다고 생각하여 툴을 제작하려 한다.
언리얼 엔진의 경우에는 참조 그래프를 그려주는 내장 툴이 있는데 유니티는 그래프는 아니고 다음과 같이만 표시를 해줬다.

이걸로 어느 정도 알 수 있지만 클래스 간의 참조 관계를 알 수 없다.
어디에 사용되는지 정도만 표시되는 것 같다.
그래서 간단하게 스크립트를 지정하면 종속성을 그래프로 표현해 주는 툴을 제작하려 한다.
필요 기능 정리
해당 툴에 필요한 기능을 간단하게 정리해보자.
- 에디터 윈도우
- 스크립트 선택
- 종속성 검사
- 그래프 그리기
에디터 윈도우
해당 툴은 에디터에서 구조를 편하게 확인하려는 목적이기 때문에 EditorWindow를 구현하면 좋을 것 같다.
우선, EditorWindow를 상속받으면 유니티 에디터 상에서 사용할 수 있는 창을 만들 수 있다.
EditorWindow에는 OnGUI라는 함수가 있고 이를 오버라이딩하여 원하는 방식으로 창을 구성할 수 있다.
public class DependencyGraphTool : EditorWindow
{
[MenuItem("Tools/Dependency Graph")]
public static void ShowWindow()
{
GetWindow<DependencyGraphTool>("Dependency Graph");
}
private void OnGUI()
{
}
}
또한, MenuItem이라는 속성으로 에디터 툴바에 메뉴 아이템을 추가할 수 있다.

그러면 위와 같이 설정한 경로에 맞게 메뉴 아이템이 추가된다.
에디터 GUI 구성
이제 에디터 창의 layout을 구성해 보자.

설계한 툴의 모습은 위와 같다.
왼쪽 부분에는 에셋을 선택할 수 있는 부분과 버튼들을 배치하였다.
오른쪽 부분에는 실제로 그래프가 그려지는 패널이다.
이를 구성하기 위해서는 OnGUI함수에서 레이아웃을 설정하고 내용과 기능을 채워 넣으면 된다.
우선, 패널부터 그려보자.
private void OnGUI()
{
mainRect = new Rect(panelPadding, panelPadding, position.width - panelPadding * 2, position.height - panelPadding * 2);
GUILayout.BeginHorizontal();
// 왼쪽 패널
GUILayout.BeginVertical(GUILayout.Width(300));
DrawControlPanel();
GUILayout.EndVertical();
// 오른쪽 패널
GUILayout.BeginVertical();
DrawGraphView();
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
GUILayout을 통해 레이아웃을 설정할 수 있다.
가로 뷰를 시작하고 왼쪽 패널과 오른쪽 패널을 그리게 하여 가로축으로 두 패널이 그려지도록 만들었다.
그럼 왼쪽 패널부터 그려보자.
우선 박스를 그려서 두 패널이 구분되면 좋겠어서 박스를 추가하였다.
private void DrawGraphView()
{
rightPanelRect = new Rect( leftPanelRect.x + leftPanelWidth + panelSpacing, mainRect.y, mainRect.width - leftPanelWidth - panelSpacing, mainRect.height);
GUI.Box(rightPanelRect, "");
GUILayout.BeginArea(rightPanelRect);
GUILayout.Label("오른쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}
private void DrawControlPanel()
{
leftPanelRect = new Rect(mainRect.x, mainRect.y, leftPanelWidth, mainRect.height);
GUI.Box(leftPanelRect, "");
GUILayout.BeginArea(leftPanelRect);
GUILayout.Label("왼쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}

Rectr를 이용하여 어디부터 어느 정도 크기를 그릴지 나타낼 수 있다.
그리고 패널 제목이 너무 패널과 붙어 있는 것 같으니 Content Rect를 추가하여 padding을 넣어보자.
private void DrawControlPanel()
{
leftPanelRect = new Rect(mainRect.x, mainRect.y, leftPanelWidth, mainRect.height);
leftContentRect = new Rect(leftPanelRect.x + contentPadding, leftPanelRect.y + contentPadding, leftPanelWidth - contentPadding, mainRect.height - contentPadding);
GUI.Box(leftPanelRect, "");
GUILayout.BeginArea(leftContentRect);
GUILayout.Label("왼쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}
private void DrawGraphView()
{
rightPanelRect = new Rect( leftPanelRect.x + leftPanelWidth + panelSpacing, mainRect.y, mainRect.width - leftPanelWidth - panelSpacing, mainRect.height);
rightContentRect = new Rect(rightPanelRect.x + contentPadding, rightPanelRect.y + contentPadding, rightPanelRect.width - contentPadding, rightPanelRect.height - contentPadding);
GUI.Box(rightPanelRect, "");
GUILayout.BeginArea(rightContentRect);
GUILayout.Label("오른쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}

이제 레이아웃을 얼추 해결했으니 내용을 채워보자.
기능은 제외하고 GUI 컴포넌트만 추가해 보자.
우선, 에셋을 선택할 수 있는 부분부터 만들어 보자.
에디터에서 List를 관리하는 부분의 UI가 훌륭하다고 생각하여 이를 응용하려 한다.
에디터에서 List를 나타내는 UI의 이름은 ReorderableList이다.
각 액션에 대해 리스너를 달아주고 이를 표시하면 된다.
private void InitializeAssetList()
{
assetList = new ReorderableList(selectedAssets, typeof(string), true, true, true, true);
// 헤더 설정
assetList.drawHeaderCallback = (Rect rect) => {
EditorGUI.LabelField(rect, "선택된 에셋");
};
// 요소 그리기
assetList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => {
};
// 추가 버튼 이벤트
assetList.onAddCallback = (ReorderableList list) => {
};
// 항목 제거 버튼 이벤트
assetList.onRemoveCallback = (ReorderableList list) => {
};
// 선택 이벤트
assetList.onSelectCallback = (ReorderableList list) => {
};
}
private void DrawControlPanel()
{
...
EditorGUILayout.LabelField("에셋 목록", EditorStyles.boldLabel);
assetListScrollPosition = EditorGUILayout.BeginScrollView(assetListScrollPosition, GUILayout.Height(150));
if (assetList != null)
{
assetList.DoLayoutList();
}
EditorGUILayout.EndScrollView();
...
}
ReorderableList.DoLayoutList()를 통해 창에 나타낼 수 있다.

에셋 목록이 잘 보인다.
편의를 위해 현재 선택된 에셋을 바로 넣을 수 있도록 버튼을 추가해 보자.
if (GUILayout.Button("현재 선택된 에셋 추가"))
{
}

이제 에셋 목록 아래에 그래프를 그릴 수 있게 하는 버튼을 추가해 보자.
해당 버튼은 선택된 에셋이 없다면 활성화되지 않아야 한다.
따라서, DisableGroup을 활용하여 제어하면 좋을 것 같다.
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("분석 도구", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(selectedAssets.Count == 0);
if (GUILayout.Button("선택한 에셋 분석", GUILayout.Height(30)))
{
// 여기에 분석 로직이 들어갑니다 (아직 구현하지 않음)
Debug.Log("에셋 분석 시작 - 총 " + selectedAssets.Count + "개 에셋");
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();

이 정도면 우선 컨트롤 패널의 레이아웃은 어느 정도 만들어진 것 같다.
이제 그래프 뷰를 그려보자.
그래프 뷰에는 상단에 뷰를 제어하는 버튼과 확대/축소 등 toolbar를 만들고 하단에는 그래프에 대한 정보를 표시하면 좋을 것 같다.
우선, 툴바는 다음과 같이 만들 수 있다.
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandleLocal"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_GridAndSnap"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandlePivot"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField("확대/축소:", GUILayout.Width(60));
float zoom = GUILayout.HorizontalSlider(1.0f, 0.1f, 2.0f, GUILayout.Width(100));
GUILayout.Button("100%", EditorStyles.toolbarButton, GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
툴바스타일의 Horizontal을 만들고 툴바 버튼을 추가한다.
이후, 오른쪽 끝으로 공간을 이동한 뒤 요소들을 추가한다.

그리고 실제로 그래프가 그려지는 부분은 다음과 같이 만들 수 있다.
Rect graphScrollArea = EditorGUILayout.GetControlRect(false, rightContentRect.height - 60);
GUI.Box(graphScrollArea, "", EditorStyles.helpBox);
그리고 여기에 scroll을 추가해 보자.
scrollPosition = GUI.BeginScrollView(graphScrollArea, scrollPosition, new Rect(0, 0, 2000, 2000));
GUI.EndScrollView();

마지막으로 하단에 그래프에 대한 정보를 표시해 보자.
// 하단 상태 표시줄
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("노드: 6개", EditorStyles.miniLabel);
GUILayout.Label("|", EditorStyles.miniLabel);
GUILayout.Label("연결: 5개", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();

전체 코드
using System;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
using Object = UnityEngine.Object;
public class DependencyGraphTool : EditorWindow
{
private Vector2 scrollPosition;
private Vector2 assetListScrollPosition;
private List<string> selectedAssets = new List<string>();
private Rect mainRect;
private Rect leftPanelRect;
private Rect rightPanelRect;
private Rect leftContentRect;
private Rect rightContentRect;
private float leftPanelWidth = 240f;
private float panelSpacing = 10f;
private float panelPadding = 10f;
private float contentPadding = 10f;
private ReorderableList assetList;
[MenuItem("Tools/Dependency Graph")]
public static void ShowWindow()
{
GetWindow<DependencyGraphTool>("Dependency Graph");
}
private void OnEnable()
{
InitializeAssetList();
}
private void InitializeAssetList()
{
assetList = new ReorderableList(selectedAssets, typeof(string), true, true, true, true);
// 헤더 설정
assetList.drawHeaderCallback = (Rect rect) => {
EditorGUI.LabelField(rect, "선택된 에셋");
};
// 요소 그리기
assetList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => {
};
// 추가 버튼 이벤트
assetList.onAddCallback = (ReorderableList list) => {
};
// 항목 제거 버튼 이벤트
assetList.onRemoveCallback = (ReorderableList list) => {
};
// 선택 이벤트
assetList.onSelectCallback = (ReorderableList list) => {
};
}
private void OnGUI()
{
mainRect = new Rect(panelPadding, panelPadding, position.width - panelPadding * 2, position.height - panelPadding * 2);
GUILayout.BeginHorizontal();
// 왼쪽 패널
GUILayout.BeginVertical(GUILayout.Width(300));
DrawControlPanel();
GUILayout.EndVertical();
// 오른쪽 패널
GUILayout.BeginVertical();
DrawGraphView();
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
private void DrawControlPanel()
{
leftPanelRect = new Rect(mainRect.x, mainRect.y, leftPanelWidth, mainRect.height);
leftContentRect = new Rect(leftPanelRect.x + contentPadding, leftPanelRect.y + contentPadding, leftPanelWidth - contentPadding, mainRect.height - contentPadding);
GUI.Box(leftPanelRect, "");
GUILayout.BeginArea(leftContentRect);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("에셋 종속성 분석", EditorStyles.boldLabel);
GUILayout.Space(10);
// 현재 선택된 에셋 추가 버튼
if (GUILayout.Button("현재 선택된 에셋 추가"))
{
}
GUILayout.Space(10);
// 에셋 목록
EditorGUILayout.LabelField("에셋 목록", EditorStyles.boldLabel);
assetListScrollPosition = EditorGUILayout.BeginScrollView(assetListScrollPosition, GUILayout.Height(150));
if (assetList != null) assetList.DoLayoutList();
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
GUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("분석 도구", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(selectedAssets.Count == 0);
if (GUILayout.Button("선택한 에셋 분석", GUILayout.Height(30)))
{
Debug.Log("에셋 분석 시작 - 총 " + selectedAssets.Count + "개 에셋");
}
EditorGUI.EndDisabledGroup();
GUILayout.EndVertical();
GUILayout.EndArea();
}
private void DrawGraphView()
{
rightPanelRect = new Rect( leftPanelRect.x + leftPanelWidth + panelSpacing, mainRect.y, mainRect.width - leftPanelWidth - panelSpacing, mainRect.height);
rightContentRect = new Rect(rightPanelRect.x + contentPadding, rightPanelRect.y + contentPadding, rightPanelRect.width - contentPadding, rightPanelRect.height - contentPadding);
GUI.Box(rightPanelRect, "");
GUILayout.BeginArea(rightContentRect);
GUILayout.Label("종속성 그래프", EditorStyles.boldLabel);
// 그래프 제어 도구 모음 (상단)
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandleLocal"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_GridAndSnap"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandlePivot"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField("확대/축소:", GUILayout.Width(60));
float zoom = GUILayout.HorizontalSlider(1.0f, 0.1f, 2.0f, GUILayout.Width(100));
GUILayout.Button("100%", EditorStyles.toolbarButton, GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
// 그래프 영역
Rect graphScrollArea = EditorGUILayout.GetControlRect(false, rightContentRect.height - 60);
GUI.Box(graphScrollArea, "", EditorStyles.helpBox);
// 스크롤 뷰 (가상 캔버스 영역)
scrollPosition = GUI.BeginScrollView(graphScrollArea, scrollPosition, new Rect(0, 0, 2000, 2000));
GUI.EndScrollView();
// 하단 상태 표시줄
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("노드: 6개", EditorStyles.miniLabel);
GUILayout.Label("|", EditorStyles.miniLabel);
GUILayout.Label("연결: 5개", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.EndArea();
}
}
종속성 분석 툴 제작
설계나 리팩토링 과정에서 AI에게 코드를 분석해 달라고 요청했더니 클래스를 도식화하여 종속성을 나타내 보여주었는데 이러한 툴이 있다면 참 편할 것 같다고 생각하여 툴을 제작하려 한다.
언리얼 엔진의 경우에는 참조 그래프를 그려주는 내장 툴이 있는데 유니티는 그래프는 아니고 다음과 같이만 표시를 해줬다.

이걸로 어느 정도 알 수 있지만 클래스 간의 참조 관계를 알 수 없다.
어디에 사용되는지 정도만 표시되는 것 같다.
그래서 간단하게 스크립트를 지정하면 종속성을 그래프로 표현해 주는 툴을 제작하려 한다.
필요 기능 정리
해당 툴에 필요한 기능을 간단하게 정리해보자.
- 에디터 윈도우
- 스크립트 선택
- 종속성 검사
- 그래프 그리기
에디터 윈도우
해당 툴은 에디터에서 구조를 편하게 확인하려는 목적이기 때문에 EditorWindow를 구현하면 좋을 것 같다.
우선, EditorWindow를 상속받으면 유니티 에디터 상에서 사용할 수 있는 창을 만들 수 있다.
EditorWindow에는 OnGUI라는 함수가 있고 이를 오버라이딩하여 원하는 방식으로 창을 구성할 수 있다.
public class DependencyGraphTool : EditorWindow
{
[MenuItem("Tools/Dependency Graph")]
public static void ShowWindow()
{
GetWindow<DependencyGraphTool>("Dependency Graph");
}
private void OnGUI()
{
}
}
또한, MenuItem이라는 속성으로 에디터 툴바에 메뉴 아이템을 추가할 수 있다.

그러면 위와 같이 설정한 경로에 맞게 메뉴 아이템이 추가된다.
에디터 GUI 구성
이제 에디터 창의 layout을 구성해 보자.

설계한 툴의 모습은 위와 같다.
왼쪽 부분에는 에셋을 선택할 수 있는 부분과 버튼들을 배치하였다.
오른쪽 부분에는 실제로 그래프가 그려지는 패널이다.
이를 구성하기 위해서는 OnGUI함수에서 레이아웃을 설정하고 내용과 기능을 채워 넣으면 된다.
우선, 패널부터 그려보자.
private void OnGUI()
{
mainRect = new Rect(panelPadding, panelPadding, position.width - panelPadding * 2, position.height - panelPadding * 2);
GUILayout.BeginHorizontal();
// 왼쪽 패널
GUILayout.BeginVertical(GUILayout.Width(300));
DrawControlPanel();
GUILayout.EndVertical();
// 오른쪽 패널
GUILayout.BeginVertical();
DrawGraphView();
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
GUILayout을 통해 레이아웃을 설정할 수 있다.
가로 뷰를 시작하고 왼쪽 패널과 오른쪽 패널을 그리게 하여 가로축으로 두 패널이 그려지도록 만들었다.
그럼 왼쪽 패널부터 그려보자.
우선 박스를 그려서 두 패널이 구분되면 좋겠어서 박스를 추가하였다.
private void DrawGraphView()
{
rightPanelRect = new Rect( leftPanelRect.x + leftPanelWidth + panelSpacing, mainRect.y, mainRect.width - leftPanelWidth - panelSpacing, mainRect.height);
GUI.Box(rightPanelRect, "");
GUILayout.BeginArea(rightPanelRect);
GUILayout.Label("오른쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}
private void DrawControlPanel()
{
leftPanelRect = new Rect(mainRect.x, mainRect.y, leftPanelWidth, mainRect.height);
GUI.Box(leftPanelRect, "");
GUILayout.BeginArea(leftPanelRect);
GUILayout.Label("왼쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}

Rectr를 이용하여 어디부터 어느 정도 크기를 그릴지 나타낼 수 있다.
그리고 패널 제목이 너무 패널과 붙어 있는 것 같으니 Content Rect를 추가하여 padding을 넣어보자.
private void DrawControlPanel()
{
leftPanelRect = new Rect(mainRect.x, mainRect.y, leftPanelWidth, mainRect.height);
leftContentRect = new Rect(leftPanelRect.x + contentPadding, leftPanelRect.y + contentPadding, leftPanelWidth - contentPadding, mainRect.height - contentPadding);
GUI.Box(leftPanelRect, "");
GUILayout.BeginArea(leftContentRect);
GUILayout.Label("왼쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}
private void DrawGraphView()
{
rightPanelRect = new Rect( leftPanelRect.x + leftPanelWidth + panelSpacing, mainRect.y, mainRect.width - leftPanelWidth - panelSpacing, mainRect.height);
rightContentRect = new Rect(rightPanelRect.x + contentPadding, rightPanelRect.y + contentPadding, rightPanelRect.width - contentPadding, rightPanelRect.height - contentPadding);
GUI.Box(rightPanelRect, "");
GUILayout.BeginArea(rightContentRect);
GUILayout.Label("오른쪽 패널", EditorStyles.boldLabel);
GUILayout.EndArea();
}

이제 레이아웃을 얼추 해결했으니 내용을 채워보자.
기능은 제외하고 GUI 컴포넌트만 추가해 보자.
우선, 에셋을 선택할 수 있는 부분부터 만들어 보자.
에디터에서 List를 관리하는 부분의 UI가 훌륭하다고 생각하여 이를 응용하려 한다.
에디터에서 List를 나타내는 UI의 이름은 ReorderableList이다.
각 액션에 대해 리스너를 달아주고 이를 표시하면 된다.
private void InitializeAssetList()
{
assetList = new ReorderableList(selectedAssets, typeof(string), true, true, true, true);
// 헤더 설정
assetList.drawHeaderCallback = (Rect rect) => {
EditorGUI.LabelField(rect, "선택된 에셋");
};
// 요소 그리기
assetList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => {
};
// 추가 버튼 이벤트
assetList.onAddCallback = (ReorderableList list) => {
};
// 항목 제거 버튼 이벤트
assetList.onRemoveCallback = (ReorderableList list) => {
};
// 선택 이벤트
assetList.onSelectCallback = (ReorderableList list) => {
};
}
private void DrawControlPanel()
{
...
EditorGUILayout.LabelField("에셋 목록", EditorStyles.boldLabel);
assetListScrollPosition = EditorGUILayout.BeginScrollView(assetListScrollPosition, GUILayout.Height(150));
if (assetList != null)
{
assetList.DoLayoutList();
}
EditorGUILayout.EndScrollView();
...
}
ReorderableList.DoLayoutList()를 통해 창에 나타낼 수 있다.

에셋 목록이 잘 보인다.
편의를 위해 현재 선택된 에셋을 바로 넣을 수 있도록 버튼을 추가해 보자.
if (GUILayout.Button("현재 선택된 에셋 추가"))
{
}

이제 에셋 목록 아래에 그래프를 그릴 수 있게 하는 버튼을 추가해 보자.
해당 버튼은 선택된 에셋이 없다면 활성화되지 않아야 한다.
따라서, DisableGroup을 활용하여 제어하면 좋을 것 같다.
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("분석 도구", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(selectedAssets.Count == 0);
if (GUILayout.Button("선택한 에셋 분석", GUILayout.Height(30)))
{
// 여기에 분석 로직이 들어갑니다 (아직 구현하지 않음)
Debug.Log("에셋 분석 시작 - 총 " + selectedAssets.Count + "개 에셋");
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();

이 정도면 우선 컨트롤 패널의 레이아웃은 어느 정도 만들어진 것 같다.
이제 그래프 뷰를 그려보자.
그래프 뷰에는 상단에 뷰를 제어하는 버튼과 확대/축소 등 toolbar를 만들고 하단에는 그래프에 대한 정보를 표시하면 좋을 것 같다.
우선, 툴바는 다음과 같이 만들 수 있다.
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandleLocal"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_GridAndSnap"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandlePivot"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField("확대/축소:", GUILayout.Width(60));
float zoom = GUILayout.HorizontalSlider(1.0f, 0.1f, 2.0f, GUILayout.Width(100));
GUILayout.Button("100%", EditorStyles.toolbarButton, GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
툴바스타일의 Horizontal을 만들고 툴바 버튼을 추가한다.
이후, 오른쪽 끝으로 공간을 이동한 뒤 요소들을 추가한다.

그리고 실제로 그래프가 그려지는 부분은 다음과 같이 만들 수 있다.
Rect graphScrollArea = EditorGUILayout.GetControlRect(false, rightContentRect.height - 60);
GUI.Box(graphScrollArea, "", EditorStyles.helpBox);
그리고 여기에 scroll을 추가해 보자.
scrollPosition = GUI.BeginScrollView(graphScrollArea, scrollPosition, new Rect(0, 0, 2000, 2000));
GUI.EndScrollView();

마지막으로 하단에 그래프에 대한 정보를 표시해 보자.
// 하단 상태 표시줄
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("노드: 6개", EditorStyles.miniLabel);
GUILayout.Label("|", EditorStyles.miniLabel);
GUILayout.Label("연결: 5개", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();

전체 코드
using System;
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using UnityEditorInternal;
using Object = UnityEngine.Object;
public class DependencyGraphTool : EditorWindow
{
private Vector2 scrollPosition;
private Vector2 assetListScrollPosition;
private List<string> selectedAssets = new List<string>();
private Rect mainRect;
private Rect leftPanelRect;
private Rect rightPanelRect;
private Rect leftContentRect;
private Rect rightContentRect;
private float leftPanelWidth = 240f;
private float panelSpacing = 10f;
private float panelPadding = 10f;
private float contentPadding = 10f;
private ReorderableList assetList;
[MenuItem("Tools/Dependency Graph")]
public static void ShowWindow()
{
GetWindow<DependencyGraphTool>("Dependency Graph");
}
private void OnEnable()
{
InitializeAssetList();
}
private void InitializeAssetList()
{
assetList = new ReorderableList(selectedAssets, typeof(string), true, true, true, true);
// 헤더 설정
assetList.drawHeaderCallback = (Rect rect) => {
EditorGUI.LabelField(rect, "선택된 에셋");
};
// 요소 그리기
assetList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => {
};
// 추가 버튼 이벤트
assetList.onAddCallback = (ReorderableList list) => {
};
// 항목 제거 버튼 이벤트
assetList.onRemoveCallback = (ReorderableList list) => {
};
// 선택 이벤트
assetList.onSelectCallback = (ReorderableList list) => {
};
}
private void OnGUI()
{
mainRect = new Rect(panelPadding, panelPadding, position.width - panelPadding * 2, position.height - panelPadding * 2);
GUILayout.BeginHorizontal();
// 왼쪽 패널
GUILayout.BeginVertical(GUILayout.Width(300));
DrawControlPanel();
GUILayout.EndVertical();
// 오른쪽 패널
GUILayout.BeginVertical();
DrawGraphView();
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
private void DrawControlPanel()
{
leftPanelRect = new Rect(mainRect.x, mainRect.y, leftPanelWidth, mainRect.height);
leftContentRect = new Rect(leftPanelRect.x + contentPadding, leftPanelRect.y + contentPadding, leftPanelWidth - contentPadding, mainRect.height - contentPadding);
GUI.Box(leftPanelRect, "");
GUILayout.BeginArea(leftContentRect);
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("에셋 종속성 분석", EditorStyles.boldLabel);
GUILayout.Space(10);
// 현재 선택된 에셋 추가 버튼
if (GUILayout.Button("현재 선택된 에셋 추가"))
{
}
GUILayout.Space(10);
// 에셋 목록
EditorGUILayout.LabelField("에셋 목록", EditorStyles.boldLabel);
assetListScrollPosition = EditorGUILayout.BeginScrollView(assetListScrollPosition, GUILayout.Height(150));
if (assetList != null) assetList.DoLayoutList();
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
GUILayout.Space(10);
GUILayout.BeginVertical(EditorStyles.helpBox);
GUILayout.Label("분석 도구", EditorStyles.boldLabel);
EditorGUI.BeginDisabledGroup(selectedAssets.Count == 0);
if (GUILayout.Button("선택한 에셋 분석", GUILayout.Height(30)))
{
Debug.Log("에셋 분석 시작 - 총 " + selectedAssets.Count + "개 에셋");
}
EditorGUI.EndDisabledGroup();
GUILayout.EndVertical();
GUILayout.EndArea();
}
private void DrawGraphView()
{
rightPanelRect = new Rect( leftPanelRect.x + leftPanelWidth + panelSpacing, mainRect.y, mainRect.width - leftPanelWidth - panelSpacing, mainRect.height);
rightContentRect = new Rect(rightPanelRect.x + contentPadding, rightPanelRect.y + contentPadding, rightPanelRect.width - contentPadding, rightPanelRect.height - contentPadding);
GUI.Box(rightPanelRect, "");
GUILayout.BeginArea(rightContentRect);
GUILayout.Label("종속성 그래프", EditorStyles.boldLabel);
// 그래프 제어 도구 모음 (상단)
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandleLocal"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_GridAndSnap"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.Button(EditorGUIUtility.IconContent("d_ToolHandlePivot"), EditorStyles.toolbarButton, GUILayout.Width(30));
GUILayout.FlexibleSpace();
EditorGUILayout.LabelField("확대/축소:", GUILayout.Width(60));
float zoom = GUILayout.HorizontalSlider(1.0f, 0.1f, 2.0f, GUILayout.Width(100));
GUILayout.Button("100%", EditorStyles.toolbarButton, GUILayout.Width(50));
EditorGUILayout.EndHorizontal();
// 그래프 영역
Rect graphScrollArea = EditorGUILayout.GetControlRect(false, rightContentRect.height - 60);
GUI.Box(graphScrollArea, "", EditorStyles.helpBox);
// 스크롤 뷰 (가상 캔버스 영역)
scrollPosition = GUI.BeginScrollView(graphScrollArea, scrollPosition, new Rect(0, 0, 2000, 2000));
GUI.EndScrollView();
// 하단 상태 표시줄
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
GUILayout.Label("노드: 6개", EditorStyles.miniLabel);
GUILayout.Label("|", EditorStyles.miniLabel);
GUILayout.Label("연결: 5개", EditorStyles.miniLabel);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
GUILayout.EndArea();
}
}