#45 커스텀 툴 제작

국기웅·2024년 6월 26일

유니티 에디터에서 반복작업들을 유니티를 뜯어고쳐 커스텀 툴을 만드는 것으로 쉽게 해결할 수 있다고 한다.


유니티 에디터 내부의 패널같은 형태를 제작하여 버튼을 클릭하면 어떤 작업을 실행하도록 만들 수 있다.
테스트에서 어떠한 위치로 갈때 작동되는 기믹같은 부분에서도 활용하여 테스트를 편리하게 할 수 있다는 장점도 있다. -> 개발 시간 대비 효율을 높여줄 수 있음

Behavior tool을 사용하면 노드를 이용해 연결하는것으로 틀을 만들 수도 있다. 하지만 UI ToolKit이라는 것까지 공부해야해서 이부분은 오히려 힘들 수도 있다..


개발 과정은 다음과 같다.

우선 Editor라는 폴더를 새로 생성하여 빌드에는 포함하지 못하도록 해주어야 한다. (빌드에서 오류날 수 있기 때문에)

using UnityEditor를 선언해주고, 스크립트 내부에 monobehavior대신 EditorWindow를 상속시킨다.
[MenuItem("MyTool/MenuTest")]와 같이 어트리뷰트를 작성하고 static 메서드를 만들어 내부 코딩을 진행하면 된다.
그러면 에디터 상단 툴바에서 찾을 수 있다.


GetWindow<'스크립트 이름'>();를 사용하면 창을 띄울 수 있다.

OnGUI()메서드를 선언하여 그 내용을 만들 수 있다.
내부에는 여러가지 함수를 이용해 만들어준다
EditorGUILayout.LabelField("메뉴 라벨");
EditorGUILayout.TestField("입력");
GUILayout.Button("버튼");

버튼은 bool값을 출력하기때문에 if문 내부에 넣어서 버튼을 눌렀을 때 작동하는 내용을 만들어 줄 수 있다.
예를들면 GameObject.CreatePrimitive(PrimitiveType.Cube);
object.name = "큐브";
을 조건문에 작성하여 큐브를 생성해 줄 수 있다.


public enum playerType
{
	None,
    case1,
    case2,
    case3
}

을 선언해주고

playerType p;
Color color;
Gradient gradient = new Gradient();

변수를 만든 뒤에

p = (playerType)EditorGUILayout.EnumFlagsField("종류 선택", p);
color = EditorGUILayout.ColorField("Color",color);
gradient = EditorGUILayout.GradientField("Gradient",gradient);

위 메서드로 색이나 그레디언트 값을 받아올 수 있다.


[MenuItem("MyTool/MenuTest")]
에서
[MenuItem("MyTool/MenuTest #1")]로 shift + 1로 열리는 단축키를 만들 수 있다
%는 컨트롤, &는 알트이다.


신을 전환시켜줄 수도 있다.

메서드에 버튼 세개를 제작해준다

EditorGUILayout.LabelField("신이동버튼");

if(GUILayout.Button("마을 씬"))
{
	SceneManager.LoadScene(0);
}
if(GUILayout.Button("던전 씬"))
{
	SceneManager.LoadScene(1);
}
if(GUILayout.Button("상점 씬"))
{
	SceneManager.LoadScene(2);
}

같은 형태로 가능하다.
하지만 한가지 문제가 있는데, 플레이중에서는 문제가 없지만, 플레이중이 아닌 경우에 해당 버튼을 누르면 nullexception이 발생한다. loadscene은 runtime중에만 가능하기 때문이다..

이건 editor상에서 신을 이동시키는 메서드를 사용해야한다.
EditorSceneManager.OpenScene("Assets/Scenes/Village.unity");같은 형태로 가능하다.

if(Application.isPlaying)으로 플레이중인지 아닌지 예외처리까지 해주면 된다.


EditorGUILayout.
GUILayout.
GUI.
EditorGUI.
위와같은 네가지 종류가있다..

Editor + GUI + Layout의 형태인데,

Editor가 있는 경우-> editor에서만 동작함

GUI.Label(new Rect(10,10,50,50),"라벨");
의 경우 그려지는 포지션을 직접 만들어주는 형태

위치를 직접 잡아주어야 한다는 점이 단점이다


GUILayout. 은 rect를 직접 잡아준다.
GUILayout.Lable("라벨");의 형태로 임의의 위치에 생성해준다.알아서 정렬해주기 때문에 매우편리할 것 같다.

옆으로 나열시키고 싶은 경우,

GUILayout.BeginHorizontal();
{

}
GUILayout.EndHorizontal();

을 사용하여 가로로 작성되게 만들 수 있다.

"괄호가 필요하진 않지만 begin으로 열면 end로 닫아주어야 한다는 것을 기억해야한다"

GUILayout은 에디터가 아닌곳에서도 사용이 가능하다. 게임오브젝트에 붙이는 것으로 게임 상에서도 띄우도록 만들 수 있다.(빌드를 해도 생성됨)
하지만 인게임상에서 저걸로 ui를 띄울생각을 하는건 좋지 않다..


new GUIContent()라는 객체를 사용한다.
이 외에 GUIStyle이라는 객체도 있다.

Content()는 그려지는 컨텐츠의 정보를,
GUIStyle()은 어떤식으로 그려질지를 보여준다.

GUIContent 내부에는 test, tooltip, 등의 변수가 존재한다.

GUIStyle 내부에는 normal.textColor, hover.textColor, active.textColor, fontSize등이 있다.
이를 활용하면 어떤 식으로 생성될지 그 GUI의 정보를 수정할 수 있다.


에디터를 이용하면 인스펙터창도 변경해줄 수 있다.
Editor를 상속시켜주고, [CustomEditor(typeof(PlayerController))]
를 붙여주면 PlayerController스크립트의 인스펙터창을 바꿔줄 수 이싿.

public override void OnInspectorGUI()
{
	DrawDefaultInspector();
}

위 메서드 내부에 작성하면된다.
DrawDefaultInspector();는 기본값을 그리는 창이기에 지워주고 원하는것으로 해주면 된다.

중요한 점은 직렬화가 가능한 변수만 그릴 수 있다는 것이다.

SerializedProperty hp;
SerializedProperty maxhp;
SerializedProperty lv;
SerializedProperty weapon;

private void OnEnable()
{
	hp = serializedOnject.FindProperty(nameof(playerController.hp));
    maxhp = serializedOnject.FindProperty(nameof(playerController.maxhp));
    .
    .
    .
    
}

와 같은 형태로 다른 클래스의 변수를 가져올 수 있다.

OnEnspectorGUI 메서드 내부에 다음과 같이 작성한다.

serializedObject.Update();

maxHp.floatValue = EditorGUILayout.FloatField("최대 체력 입력",  maxHp.floatValue);
hp.floatValue = EditorGUILayout.Slider("현재 체력", hp.floatValue, 0, 100);

serializedObject.ApplyModifiedProperties();

위 함수들로 최대체력 입력칸이나 스크롤바를 만들어 변수를 조절할 수 있다.

if(GUILayout.Button("레벨업"))
{
	level.intvalue ++;
}

로 레벨업하는 버튼도 제작 가능하다.


bool isOpen;변수를 선언하고

isOpen = EditorGUILayout.Foldout(isOpen, "Custom");

if(!isOpen)
	return;

으로 인스펙터창에 작은 폴더를 만들 수 있다.


void FindMaterials()
{
	if(GUILayout.Button("Find All Materials"))
    {
    	string[] assetGUIDs = AssetDatabase.FindAssets("t:material");
        
    }
}

위 형태로 material 에셋들을 다 가져올 수 있다.

Vector2 scrollposition
.
.
foreach(var asset in assetGUIDs)
{
	string path = AssetDatabase.GUIDToAssetPath(asset);
    AssetDatabase.LoadAssetAtPath<.Material>(path);
}

위와 같이 경로를 받아오고..

Selection.activeObject.Getcomponetn<.MeshRenderer>.material = m;
//m은 선택한 material변수이다

를 사용해 적용시켜줄 수 있다.
다른 부분도 타겟과 변수를 바꾸는 것으로 쉽게 바꿀 수 있다.

AssetDatabase.FindAssets("",new[] {"Asset/Prefabs"})

를 사용하면 프리펩 폴더 내부의 에셋들을 전부 가져올 수 있다.


자세한 부분은 unity docs를 살펴보자
filter라던가 여러 유용한 메서드,변수 등을 볼 수 있다.

profile
게임개발호소인

0개의 댓글