이번에는 Unity에서 EditorWindow를 이용해보려 한다.

기존에 마야에서 PySide2를 이용해 기본적인 툴을 만들었듯이, 유니티에도 기본적인 툴을 제작할 수 있는 환경이 있었다.

이 툴을 통해 에디터에서 직접 벡터의 내적을 계산하고, 핸들을 사용해 위치를 조정해 보자.

image-20250219150154151

주요기능Permalink

  • p0, p1, c 세 점을 조작가능
  • 벡터간 내적을 실시간으로 계산한다.
  • Handles.FreeMoveHandle()을 사용해 에디터에서 점 이동가능
  • Handles.Label()을 활용해 내적 값을 에디터에 출력

ShowWindowPermalink

[MenuItem("Tools/Dot Product")]
public static void ShowWindow()
{
    DotProductEditor window = (DotProductEditor)GetWindow
        (typeof(DotProductEditor), true, "Dot Product");
    window.Show();
}
  • Unity 에디터의 메뉴바에 버튼을 추가해, DotProductEditor 창을 연다.
  • GetWindow를 통해 윈도우를 생성하고, Show로 호출한다.

image-20250219150213941

OneEnablePermalink

private void OnEnable()
{
    if (pt0 == Vector3.zero && pt1 == Vector3.zero)
    {
        pt0 = new Vector3(0f, 1f, 0f);
        pt1 = new Vector3(.5f, .5f, 0f);
        ptC = Vector3.zero;
    }

    obj = new SerializedObject(this);
    propP0 = obj.FindProperty("pt0");
    propP1 = obj.FindProperty("pt1");
    propC = obj.FindProperty("ptC");

    guiStyle.fontSize = 25;
    guiStyle.fontStyle = FontStyle.Bold;
    guiStyle.normal.textColor = Color.white;

    SceneView.duringSceneGui += SceneGUI;
}
  • 에디터 창이 활성화될 때 초기화
  • 기본 벡터 좌표 설정
  • SerializedObject를 통해 Unity Inspector에서 프로퍼티 조작 가능
  • SceneView.duringSceneGui += SceneGUI;를 통해 SceneView에서 GUI를 그릴 수 있도록 이벤트를 등록

OnDisablePermalink

private void OnDisable()
{
    SceneView.duringSceneGui -= SceneGUI;
}
  • 에디터 창이 닫힐 때 이벤트 해제
  • SceneView에서 SceneGUI 이벤트를 제거

OnGUIPermalink

private void OnGUI()
{
    obj.Update();

    DrawBlockGUI("p0", propP0);
    DrawBlockGUI("p1", propP1);
    DrawBlockGUI("c", propC);

    if (obj.ApplyModifiedProperties())
    {
        SceneView.RepaintAll();
    }
}
  • 사용자 UI 그리기
  • p0, p1, p2의 값을 에디터에서 수정 할 수 있도록 UI 생성
  • SceneView.RepaintAll()로 변경사항이 즉시 반영되도록 지정

DrawBlockGUIPermalink

void DrawBlockGUI(string lab, SerializedProperty prop)
{
    EditorGUILayout.BeginHorizontal("box");
    EditorGUILayout.LabelField(lab, GUILayout.Width(50));
    EditorGUILayout.PropertyField(prop, GUIContent.none);
    EditorGUILayout.EndHorizontal();
}
  • 입력 필드를 생성한다.
  • p0, p1, p2에 대한 GUI 블록을 생성
  • EditorGUILayout.LabelField()를 사용하여 필드 라벨을 추가

SceneGUIPermalink

private void SceneGUI(SceneView view)
{
    Handles.color = Color.red;
    Vector3 p0 = SetMovePoint(pt0);
    Handles.color = Color.green;
    Vector3 p1 = SetMovePoint(pt1);
    Handles.color = Color.white;
    Vector3 c = SetMovePoint(ptC);

    if (pt0 != p0 || pt1 != p1 || ptC != c)
    {
        pt0 = p0;
        pt1 = p1;
        ptC = c;

        Repaint();
    }

    DrawLabel(p0, p1, c);
}
  • SceneView에서 점을 이동하며 내적 계산
  • SetMovePoint()를 사용하여 SceneView에서 점을 이동 가능하도록 설정
  • 사용자가 이동한 경우 Repaint()를 호출하여 즉시 업데이트

SetMovePointPermalink

Vector3 SetMovePoint(Vector3 pos)
{
    float size = HandleUtility.GetHandleSize(Vector3.zero) * 0.15f;
    return Handles.FreeMoveHandle(pos, size, Vector3.zero, Handles.SphereHandleCap);
}
  • SceneView에서 점을 이동가능하도록 설정
  • Handles.FreeMoveHandle()을 사용하여 마우스로 이동 가능한 점을 SceneView에 표시

DotProductPermalink

float DotProduct(Vector3 p0, Vector3 p1, Vector3 c)
{
    Vector3 a = (p0 - c).normalized;
    Vector3 b = (p1 - c).normalized;

    return (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
}
  • 두 벡터를 정규화(normalize)한 후 내적 연산 수행
  • 결과 값은 두 벡터 사이의 코사인 값을 나타낸다. (1: 0도, 0: 90도, -1: 180도)

DrawLabelPermalink

void DrawLabel(Vector3 p0, Vector3 p1, Vector3 c)
{
    Handles.Label(c, DotProduct(p0, p1, c).ToString("F2"), guiStyle);
    Handles.color = Color.black;

    Handles.DrawAAPolyLine(3f, p0, c);
    Handles.DrawAAPolyLine(3f, p1, c);
}
  • 내적 결과를 SceneView에 표시
  • Handles.Label을 사용하여 내적 결과를 SceneView에 텍스트로 출력
  • Handles.DrawAAPolyLine을 사용하여 점과 점을 선으로 연결

  • "F2" 이 숫자를 통해 보여질 소수점 자리를 지정 가능

마무리Permalink

Unity의 EditorWindow를 활용하면 Maya에서 PySide2로 툴을 제작했던 것처럼, 내가 원하는 기능을 직접 추가하여 커스텀 유니티 툴을 제작할 수 있었다.

이번에서는 벡터 내적(Dot Product) 계산을 시각적으로 조작할 수 있는 툴을 만들어봤다.

이 툴을 통해 SceneView에서 핸들을 조작하여 벡터를 실시간으로 이동시키고, 내적 값을 즉시 확인하는 인터랙티브한 편집 환경까지 되었다.

앞으로도 에디터를 확장하며 제작 환경을 커스터마이징해 보다 직관적이게 구축하고, 반복적인 작업을 자동화하거나, 더 편리하게 데이터를 조작할 수 있도록 지원할 수 있도록 만져봐야겠다.

댓글남기기