Unity) EditorWindow(DotProduct)
이번에는 Unity에서 EditorWindow를 이용해보려 한다.
기존에 마야에서 PySide2를 이용해 기본적인 툴을 만들었듯이, 유니티에도 기본적인 툴을 제작할 수 있는 환경이 있었다.
이 툴을 통해 에디터에서 직접 벡터의 내적을 계산하고, 핸들을 사용해 위치를 조정해 보자.
주요기능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
로 호출한다.
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에서 핸들을 조작하여 벡터를 실시간으로 이동시키고, 내적 값을 즉시 확인하는 인터랙티브한 편집 환경까지 되었다.
앞으로도 에디터를 확장하며 제작 환경을 커스터마이징해 보다 직관적이게 구축하고, 반복적인 작업을 자동화하거나, 더 편리하게 데이터를 조작할 수 있도록 지원할 수 있도록 만져봐야겠다.
댓글남기기